Interactive API documentation is available at /api/docs/ (Swagger UI):
- Development:
http://localhost:3000/api/docs/ - Staging:
https://staging-api.trends.earth/api/docs/ - Production:
https://api.trends.earth/api/docs/
For authentication details, see authentication.md.
All listing endpoints support:
| Parameter | Description |
|---|---|
filter |
SQL-style filter expressions, comma-separated. Operators: =, !=, >, <, >=, <=, like |
sort |
Comma-separated sort fields. Prefix with - for descending. Example: sort=status desc,name asc |
include |
Comma-separated extra fields to add to each result |
exclude |
Comma-separated fields to remove from each result |
page |
Page number (pagination only enabled when page or per_page is provided) |
per_page |
Items per page (default: 20, max: 100) |
Access control: Non-admin users cannot filter, sort by, or include user_name or user_email fields.
Attempting to do so returns HTTP 403 Forbidden.
GET /api-health
No authentication required. Returns server status, database connectivity, and API version.
GET /api/v1/status
Returns paginated status log entries with execution counts by state.
Query parameters: start_date, end_date, sort (default: -timestamp), page, per_page (max: 1000).
Response fields include executions_pending, executions_ready, executions_running,
executions_finished, executions_failed, executions_cancelled. Entries also carry
optional status_from, status_to, and execution_id fields that are set when
an entry is created by a status-change event (as opposed to a periodic snapshot).
GET /api/v1/script List scripts
GET /api/v1/script/<id> Get script
POST /api/v1/script Create script
PATCH /api/v1/script/<id> Update script
DELETE /api/v1/script/<id> Delete script (Admin only)
POST /api/v1/script/<id>/publish Publish script
POST /api/v1/script/<id>/unpublish Unpublish script
GET /api/v1/script/<id>/download Download script
GET /api/v1/script/<id>/log Get script logs
PATCH /api/v1/script/<id>/config Update script config (Admin only)
GET /api/v1/script/<id>/access Get access information
PUT /api/v1/script/<id>/access/roles Set allowed roles
PUT /api/v1/script/<id>/access/users Set allowed users
POST /api/v1/script/<id>/access/users/<uid> Add user
DELETE /api/v1/script/<id>/access/users/<uid> Remove user
POST /api/v1/script/<id>/access/roles/<role> Add role
DELETE /api/v1/script/<id>/access/roles/<role> Remove role
PUT /api/v1/script/<id>/access/app Set/clear required application (Admin only)
DELETE /api/v1/script/<id>/access Clear all restrictions
Access control fields: restricted (boolean), allowed_roles, allowed_users,
required_app_key.
PUT /access/app takes {"app_key": "<app_key>" | null}. When set, users with
an active grant for that application can also access the script. This only adds
access and never removes access granted by the other rules.
required_app_key appears only in the admin/owner access_control view and is
not part of default script payloads. DELETE /access does not clear it.
# Public scripts sorted by creation date
GET /api/v1/script?filter=public=true&sort=-created_at
# Scripts with pagination
GET /api/v1/script?page=1&per_page=10
# Include full user object and logs
GET /api/v1/script?include=user,logs
# Exclude large fields (Admin only — include user_name)
GET /api/v1/script?include=user_name&exclude=cpu_reservation,cpu_limit,memory_reservation,memory_limitPOST /api/v1/script/<id>/run Run a script
GET /api/v1/execution List executions (own for users, all for admins)
GET /api/v1/execution/user List current user's executions
GET /api/v1/execution/<id> Get execution
PATCH /api/v1/execution/<id> Update execution (Admin only)
POST /api/v1/execution/<id>/cancel Cancel execution
GET /api/v1/execution/<id>/log Get logs
POST /api/v1/execution/<id>/log Add log entry (Admin only)
GET /api/v1/execution/<id>/download-results Download results as JSON file
GET /api/v1/execution/<id>/docker-logs Get raw Docker service logs (Admin only)
GET /script/<id>, POST /script/<id>/run, GET /execution,
GET /execution/user, GET /execution/<id>, POST /execution/<id>/cancel,
GET /execution/<id>/log and GET /execution/<id>/download-results also accept
RS256 OIDC access tokens. Legacy POST /auth tokens (QGIS plugin, API UI) and
service-client tokens are checked first and behave exactly as before. An OIDC
token is tried only if legacy verification fails and the token header says
alg: RS256. Otherwise the original legacy error is returned unchanged.
An OIDC token needs script:read, execution:read or execution:write for
these routes. That scope must be in the token's granted scopes and in the
client's registered scopes, or the route returns 403 Insufficient scope. If
the client is app-gated, the user's application grant is also checked on every
request, so revoking access takes effect immediately
(403 app_access_required). Executions are owned by the token's user (sub).
Rate limits and the per-user concurrent-execution limit apply to that user, as
for a legacy token.
Cancelling an execution stops the Docker service, then attempts to cancel any associated Google Earth Engine tasks. Requires ownership or Admin role.
# Finished executions with duration, sorted by longest first
GET /api/v1/execution?status=FINISHED&include=duration&sort=-duration
# Exclude large fields to reduce response size
GET /api/v1/execution?exclude=params,results
# Running executions with user info (Admin only)
GET /api/v1/execution?status=RUNNING&include=user,user_name,duration&sort=-start_dateGET /api/v1/user List users (Admin only)
GET /api/v1/user/<id> Get user (Admin only)
GET /api/v1/user/me Get own profile
POST /api/v1/user Create user
PATCH /api/v1/user/<id> Update user (Admin only)
PATCH /api/v1/user/me Update own profile
PATCH /api/v1/user/me/change-password Change own password
PATCH /api/v1/user/<id>/change-password Change user password (Admin only)
DELETE /api/v1/user/<id> Delete user (SuperAdmin only)
DELETE /api/v1/user/me Delete own account
POST /api/v1/user/<id>/recover-password Send password recovery email
POST /api/v1/user/reset-password Reset password with token
# Users from USA, sorted by name
GET /api/v1/user?filter=country=USA&sort=name
# Admin users with pagination
GET /api/v1/user?filter=role=ADMIN&page=1&per_page=10Bounded identity and app-access API shared by OIDC-gated applications
(currently rio_coherence, rio_coherence_staging, avoided_emissions and
avoided_emissions_staging; unknown keys return
404 unknown_app after authentication). There are no application-specific
handlers or aliases: behaviour comes from the explicit, fail-closed policy
registry in gefapi/utils/app_access.py (APP_POLICIES: roles, default role,
admin roles, superadmin role, member discovery, directory fields).
Every route requires an RS256 OIDC access token issued to an OAuth client
whose required_app_key equals the {app_key} in the path. Legacy
POST /auth (HS256) tokens, service-client tokens, ID tokens and tokens for
other clients/apps are rejected. Account state, token revocation, email
verification and the caller's current grant are re-checked on every request
(stale signed app claims are never trusted); app admin comes from the
current grant role or an effective SUPERADMIN (global ADMIN does not confer
it).
Scopes: every route requires openid email profile plus the operation scope
below. The effective scopes are the token's granted scope intersected with
the client's registered allowlist, so removing a scope from the client
disables it for already-issued tokens. Scopes never confer roles.
GET /api/v1/apps/{app_key}/me app:read Own profile + effective access
GET /api/v1/apps/{app_key}/users?email=<exact> app-users:read Exact-email lookup
GET /api/v1/apps/{app_key}/users app-users:read Directory (app admin)
POST /api/v1/apps/{app_key}/users/resolve app-users:read Batch subject lookup (max 100)
PUT /api/v1/apps/{app_key}/users/<subject>/access app-access:write Set status/role (app admin)
PATCH /api/v1/account/profile account:write Update own name/institution
OIDC access tokens include an auth_version claim. A token is rejected (401)
after a password change, password reset or deactivation increments the
account's auth_version.
App profile shape (GET /me, lookups, directory and access responses wrap
profiles in data):
{"sub": "<uuid>", "email": "a@b.org", "email_verified": true, "name": "A",
"institution": "B", "active": true, "app_access": "active", "app_role": "member"}app_access is active when access is effective (including via SUPERADMIN),
otherwise the grant status (pending, revoked) or null; app_role is a
policy role only while access is effective, else null.
PATCH /api/v1/account/profileis the only profile write (there is noPATCH /apps/{app_key}/me). It accepts onlynameandinstitution; any other field returns400 field_not_allowed. It requires an OIDC client (403 client_identity_requiredotherwise), a current grant when the client is app-gated, and a verified email. The response is identity-only:{"data": {"sub", "email", "email_verified", "name", "institution", "active"}}(noapp_access/app_role).- Member discovery (
?email=andresolvefor non-admins) is a policy flag: enabled forrio_coherence, disabled foravoided_emissions(403 app_role_required). App admins can always use them. GET /users?email=returns{"data": [profile]}only for an active, verified user with effective access, otherwise{"data": []}.GET /users(noemail, admin only) returns{"data": [...], "page", "per_page", "total"}listing users with any grant for the app (pending/active/revoked) plus effective superadmins.per_pagedefaults to 50 and must be 1-100; optionalstatus=pending|active|revokedfilter.POST /users/resolvebody{"subjects": ["<uuid>", ...]}(<=100) returns eligible users; admins additionally receive users holding a pending/revoked grant. Unrelated users are never returned.PUT /users/<subject>/accessbody{"status": "active|pending|revoked", "role": "<policy role>", "note": "..."}updates only that app's grant. Revoking/demoting an effective superadmin returns403 superadmin_protected; revoking/demoting yourself returns409 self_demotion_forbidden; unknown subjects return404 not_found.
Errors: 401 for missing/invalid/expired/revoked tokens or disabled accounts;
403 with error of client_identity_required, client_app_mismatch,
insufficient_scope (includes required_scopes and missing_scopes),
email_unverified, app_access_required (includes app_access), or
app_role_required; 404 unknown_app; 400 invalid_request.
GET /api/v1/admin/app-access/apps/<app_key>/notification-recipients
PUT /api/v1/admin/app-access/apps/<app_key>/notification-recipients
Each application has its own list of reviewers who are emailed about new access
requests. PUT body: {"user_ids": ["<uuid>", ...]}. Every user must be an
active global ADMIN or SUPERADMIN; otherwise the request returns 400 with
invalid_user_ids. Sending an empty list switches back to the default, which
emails every superadmin.
Application admins are also emailed automatically. To qualify, a user needs:
- an active administering grant for the app;
- an active, verified account;
- an active OAuth client for the app with
app-access:write, so they can actually approve users.
A superadmin's implicit app-admin status does not count. Listed or default
reviewers get a link to the API UI (API_UI_URL). Automatically included app
admins get the app's own admin page (<APP_KEY>_ADMIN_URL, which must be https,
or http on localhost); if that is not configured, their email has no link. Each
person gets at most one email.
GET returns:
{"data": {"app_key": "avoided_emissions",
"listed_recipients": [{"user_id", "email", "name", "role", "is_active",
"eligible", "added_at"}],
"auto_app_admins": [{"user_id", "email", "name"}],
"effective_recipients": ["a@b.org"],
"using_default": false}}Changes are logged as the APP_ACCESS_NOTIFY_RECIPIENTS_CHANGED security event.
GET /api/v1/rate-limit/status Query current rate limit state
POST /api/v1/rate-limit/reset Reset all rate limit counters
Rate limits apply per user (authenticated with a legacy or OIDC access token) or per IP address (unauthenticated). Admin and SuperAdmin users are automatically exempt.
| Role | Capabilities |
|---|---|
| Regular user | Own scripts/executions, public scripts, change own password/profile |
| Admin | All scripts/executions, user data fields, most user management |
| SuperAdmin | All Admin capabilities + user role changes, user deletion, rate limit management |