Skip to main content

Overview

QFieldCloud API uses token-based authentication. Each request must include a valid authentication token in the Authorization header. The API supports two authentication methods:
  1. Token Authentication - Primary method for API access
  2. Session Authentication - Used by the web interface

Authentication Token

Authentication tokens are 100-character alphanumeric strings that expire after a configurable period (default: varies by instance).

Token Format

Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a123456789...

Token Properties

  • Length: 100 characters
  • Type: Alphanumeric string
  • Expiration: Configurable (instance-specific)
  • Client-specific: Different token policies based on client type
  • Multi-token: Users can have multiple active tokens for different clients

Obtaining an Authentication Token

Username/Password Login

Use the login endpoint to obtain a token with username or email and password:
curl -X POST https://app.qfieldcloud.com/api/v1/auth/login/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "your_username",
    "password": "your_password"
  }'

Success Response

{
  "token": "401f7ac837da42b97f613d789819ff93537bee6a1234567890abcdef...",
  "expires_at": "2024-03-11T10:30:00Z"
}
token
string
required
The authentication token to use in subsequent API requests
expires_at
string
required
ISO 8601 timestamp when the token expires

Email Login

You can also authenticate using email instead of username:
curl -X POST https://app.qfieldcloud.com/api/v1/auth/login/ \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "your_password"
  }'

Alternative Token Endpoint

The /api/v1/auth/token/ endpoint provides identical functionality to /api/v1/auth/login/:
curl -X POST https://app.qfieldcloud.com/api/v1/auth/token/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "your_username",
    "password": "your_password"
  }'

Using Authentication Tokens

Include the token in the Authorization header with the Token prefix:
curl https://app.qfieldcloud.com/api/v1/projects/ \
  -H "Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a..."

Token Behavior

  • Tokens are updated with last_used_at timestamp on each request
  • Expired tokens return 401 Unauthorized with error code token_authentication_failed
  • Inactive user accounts return 401 Unauthorized even with valid tokens

SSO Authentication

QFieldCloud supports Single Sign-On (SSO) via OAuth2 providers configured through django-allauth.

Listing Available Providers

Get a list of configured authentication providers:
curl https://app.qfieldcloud.com/api/v1/auth/providers/

Example Response

[
  {
    "type": "credentials",
    "id": "credentials",
    "name": "Username / Password"
  },
  {
    "type": "oauth2",
    "id": "google",
    "name": "Google",
    "grant_flow": 3,
    "scope": "openid email profile",
    "pkce_enabled": true,
    "token_url": "https://oauth2.googleapis.com/token",
    "refresh_token_url": "https://oauth2.googleapis.com/token",
    "request_url": "https://accounts.google.com/o/oauth2/v2/auth",
    "redirect_host": "localhost",
    "redirect_port": 7070,
    "redirect_url": "",
    "client_id": "your-client-id.apps.googleusercontent.com",
    "extra_tokens": {
      "id_token": "X-Id-Token"
    },
    "idp_id_header": "X-Idp-Id"
  }
]

Provider Response Fields

type
string
Provider type: credentials for username/password, oauth2 for SSO providers
id
string
Unique provider identifier (e.g., google, keycloak, github)
name
string
Human-readable provider name
grant_flow
integer
OAuth2 grant flow type:
  • 0: Authorization Code
  • 3: Authorization Code with PKCE
scope
string
OAuth2 scopes requested during authentication
pkce_enabled
boolean
Whether PKCE (Proof Key for Code Exchange) is enabled
token_url
string
OAuth2 token exchange endpoint
request_url
string
OAuth2 authorization endpoint
client_id
string
OAuth2 client ID for this provider

Client Types

QFieldCloud tracks different client types and applies appropriate token policies:
Client TypeUser-Agent PatternToken Behavior
qfieldqfield|*Single token per user (new login invalidates old token)
qfieldsync* QGIS/[34]xxxx*Single token per user
sdksdk|*Multiple tokens allowed
clicli|*Multiple tokens allowed
browserBrowser User-AgentMultiple tokens allowed
workerInternal workersMultiple tokens allowed
unknownOtherSingle token per user

Setting Client Type

Set an appropriate User-Agent header to ensure correct token handling:
# QField mobile app
curl -H "User-Agent: qfield|QField/3.0.0" \
  -H "Authorization: Token YOUR_TOKEN" \
  https://app.qfieldcloud.com/api/v1/projects/

# Python SDK
curl -H "User-Agent: sdk|my-app/1.0.0" \
  -H "Authorization: Token YOUR_TOKEN" \
  https://app.qfieldcloud.com/api/v1/projects/

# CLI tool
curl -H "User-Agent: cli|qfieldcloud-cli/2.1.0" \
  -H "Authorization: Token YOUR_TOKEN" \
  https://app.qfieldcloud.com/api/v1/projects/

Getting Current User

Retrieve the authenticated user’s information:
curl https://app.qfieldcloud.com/api/v1/auth/user/ \
  -H "Authorization: Token YOUR_TOKEN"

Response

{
  "pk": 123,
  "username": "john_doe",
  "email": "[email protected]",
  "first_name": "John",
  "last_name": "Doe"
}
pk
integer
User’s primary key identifier
username
string
Username
email
string
Email address (read-only)
first_name
string
First name
last_name
string
Last name

Logging Out

Invalidate the current authentication token:
curl -X POST https://app.qfieldcloud.com/api/v1/auth/logout/ \
  -H "Authorization: Token YOUR_TOKEN"

Response

{
  "detail": "Successfully logged out."
}
The logout endpoint:
  • Invalidates all active tokens for single-token clients (QField, QFieldSync, Unknown)
  • Invalidates only the current token for multi-token clients (SDK, CLI, Browser)
  • Sets token expires_at to current time

Error Handling

Invalid Credentials

{
  "non_field_errors": [
    "Unable to log in with provided credentials."
  ]
}
HTTP Status: 401 Unauthorized

Expired Token

{
  "code": "token_authentication_failed",
  "message": "Token authentication failed",
  "detail": "Token has expired."
}
HTTP Status: 401 Unauthorized

Invalid Token

{
  "code": "token_authentication_failed",
  "message": "Token authentication failed",
  "detail": "Invalid token."
}
HTTP Status: 401 Unauthorized

Too Many Login Attempts

{
  "code": "too_many_failed_login_attempts",
  "message": "Too many failed login attempts!",
  "detail": "Account temporarily locked due to too many failed login attempts."
}
HTTP Status: 401 Unauthorized

Inactive User

{
  "non_field_errors": [
    "User account is disabled."
  ]
}
HTTP Status: 401 Unauthorized

Security Best Practices

  • Never commit tokens to version control
  • Use environment variables or secure credential storage
  • Rotate tokens periodically
  • Use different tokens for different applications
  • Always use HTTPS endpoints in production
  • Never send tokens over unencrypted connections
  • Validate SSL certificates
  • Check expires_at timestamp before requests
  • Implement automatic token renewal
  • Handle 401 responses gracefully
  • Re-authenticate when tokens expire
  • Don’t log tokens in plain text
  • Use token prefixes in logs (e.g., Token abc***xyz)
  • Implement token revocation when compromised
  • Monitor for suspicious token usage

Example: Complete Authentication Flow

Here’s a complete example of authenticating and making an API request:
import requests
from datetime import datetime

class QFieldCloudClient:
    def __init__(self, base_url, username, password):
        self.base_url = base_url.rstrip('/')
        self.token = None
        self.expires_at = None
        self.authenticate(username, password)
    
    def authenticate(self, username, password):
        """Obtain authentication token"""
        response = requests.post(
            f'{self.base_url}/api/v1/auth/login/',
            json={'username': username, 'password': password}
        )
        response.raise_for_status()
        
        data = response.json()
        self.token = data['token']
        self.expires_at = datetime.fromisoformat(data['expires_at'].replace('Z', '+00:00'))
        
    def get_headers(self):
        """Get headers with authentication token"""
        return {
            'Authorization': f'Token {self.token}',
            'User-Agent': 'sdk|my-app/1.0.0'
        }
    
    def get_projects(self):
        """Get list of projects"""
        response = requests.get(
            f'{self.base_url}/api/v1/projects/',
            headers=self.get_headers()
        )
        response.raise_for_status()
        return response.json()

# Usage
client = QFieldCloudClient(
    'https://app.qfieldcloud.com',
    'your_username',
    'your_password'
)

projects = client.get_projects()
print(f"Found {len(projects)} projects")

Next Steps

API Overview

Learn about API structure and conventions

Projects API

Start managing projects via API

Users API

Manage user accounts and profiles

OpenAPI Docs

Interactive API documentation

Build docs developers (and LLMs) love