Comotion Dash API Reference¶
Version 2 · October 2026
This document describes the HTTP APIs behind the Comotion Python SDK. It is for engineers who want to call the APIs directly, with curl, Python requests or any other HTTP client.
You should know HTTP and JSON. You do not need to know anything about how Comotion runs the service.
In examples, replace <org> with your org name and <zone> with your zone name. $TOKEN stands for a valid access token (see Authentication).
1. Overview¶
There are three APIs:
| API | What it is for | Base URL |
|---|---|---|
| Org API | Query and load data in your org's main data lake. | https://<org>.api.comodash.io/v2 |
| Zone API | Query and load data in one zone of your org. | https://<zone>.<org>.api.comodash.io/v2 |
| DailyRun API | Turn the nightly daily run on or off, start a run, check run status. | https://api.<org>.comodash.io/superset/dailyRun/... |
All three APIs:
- use HTTPS (TLS 1.2 or later) and JSON,
- need an
Authorization: Bearer <access token>header on every request, - get their tokens from your org's ComoAuth realm.
Org vs zone¶
An org is your Comotion Dash tenant. It has a main data lake.
A zone is a separate data area inside your org, with its own lake, its own permissions and its own API host. Use the Zone API for data in a zone. Use the Org API for the org's main lake. The two APIs have the same query and load endpoints. Only the Org API has the identity endpoints.
The data lake (data model v2)¶
- Lake tables hold your data. You add data to a lake table with a load.
- Every row has a service client (
service_client_id). Your access is limited to the service clients your user has been given. See Roles and permissions. - You read data with a query (SQL, Amazon Athena / Trino dialect).
Where the SDK fits¶
The Comotion Python SDK wraps these APIs. It handles login, token refresh, polling, file conversion and chunked uploads for you. Each endpoint below names the matching SDK call. If you work in Python, the SDK is usually the easiest option. Use the HTTP API directly from other languages, or when you need more control.
2. Hosts and regions¶
| Region | API domain |
|---|---|
| Europe (eu-west-1) | comodash.io |
| United States (us-east-1) | comodash.com |
| API | Host pattern |
|---|---|
| Org API | <orgname>.api.<domain> with base path /v2 |
| Zone API | <zonename>.<orgname>.api.<domain> with base path /v2 |
| DailyRun API | api.<orgname>.<domain> with base path /superset |
All paths in this document are relative to the base URL. For example, POST /query on the Org API is POST https://<org>.api.comodash.io/v2/query.
3. Authentication¶
3.1 Your ComoAuth realm¶
Each org has its own login realm on ComoAuth (an OpenID Connect provider):
| Item | Value for your org |
|---|---|
| Issuer | https://auth.comotion.us/auth/realms/<org> |
| Discovery document | https://auth.comotion.us/auth/realms/<org>/.well-known/openid-configuration |
| Authorization endpoint | https://auth.comotion.us/auth/realms/<org>/protocol/openid-connect/auth |
| Token endpoint | https://auth.comotion.us/auth/realms/<org>/protocol/openid-connect/token |
The APIs only accept tokens whose issuer is your org's realm. A token from another org's realm is rejected.
3.2 Getting a token as a user (interactive)¶
The SDK's comotion authenticate command uses the OAuth 2.0 authorization code flow with PKCE:
- It opens your browser at the authorization endpoint with
client_id=comotion_cli,response_type=code,scope=openid, a PKCEcode_challenge(S256) and a redirect tohttp://localhost:<random port>. - You log in. ComoAuth redirects back to the local port with a
code. - The SDK swaps the code for tokens at the token endpoint (
grant_type=authorization_code, thecode_verifierand the sameredirect_uri). - The SDK saves the refresh token in your operating system's credential manager (keyring). It does not save access tokens.
- Each time it needs an access token, the SDK posts
grant_type=refresh_token,client_id=comotion_cliand the saved refresh token to the token endpoint. It gets a new access token about 30 seconds before the current one expires.
If you already use the SDK, the simplest way to get a token for curl is:
from comotion.auth import Auth
print(Auth("<org>").get_access_token())
To do it yourself, refresh a token like this:
curl -s https://auth.comotion.us/auth/realms/<org>/protocol/openid-connect/token \
-d grant_type=refresh_token \
-d client_id=comotion_cli \
-d refresh_token="$REFRESH_TOKEN" | jq -r .access_token
3.3 Getting a token as an application (machine to machine)¶
For scheduled jobs and services, Comotion can set up an application client for you with a client ID and client secret. It uses the OAuth 2.0 client credentials grant:
TOKEN=$(curl -s https://auth.comotion.us/auth/realms/<org>/protocol/openid-connect/token \
-d grant_type=client_credentials \
-d client_id="$CLIENT_ID" \
-d client_secret="$CLIENT_SECRET" | jq -r .access_token)
import requests
resp = requests.post(
"https://auth.comotion.us/auth/realms/<org>/protocol/openid-connect/token",
data={
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
},
)
resp.raise_for_status()
TOKEN = resp.json()["access_token"]
SDK equivalent: Auth("<org>", entity_type=Auth.APPLICATION, application_client_id=..., application_client_secret=...).
Ask Comotion support to create an application client. Its roles, scopes and service clients are set when it is created.
3.4 Token lifetime¶
Access tokens are short-lived. Read expires_in in the token response, or the exp claim in the token, and get a new token before it expires.
3.5 What the APIs check in the token¶
| Check | Org API | Zone API (<zone>) | DailyRun API |
|---|---|---|---|
| Issuer | your realm | your realm | your realm |
Audience (aud) | dash_api | dash_api_<zone> | dash_api |
| Scope | per endpoint, e.g. query:write | per endpoint, e.g. <zone>:query:write | per endpoint, e.g. dailyrun:execution:read |
| Claims the handler reads | sub, jti, service_client_id, resource_access | same | none |
The service_client_id claim is a string in the form [12 13]: square brackets around service client IDs separated by spaces.
resource_access must contain an entry for the API's client (dash_api or dash_api_<zone>) listing your roles. See Roles and permissions.
Your Comotion administrator sets your audiences, scopes, roles and service clients. If a call fails with 401 or 403, decode your token (for example at a JWT debugger you trust, or with jq -R 'split(".")[1] | @base64d | fromjson') and compare it with the table above.
3.6 Common 401 and 403 causes¶
| Response | Likely cause | Fix |
|---|---|---|
401 {"message":"Unauthorized"} | No Authorization header, a token that has expired, a token from the wrong realm, or the wrong audience (for example a dash_api token sent to the Zone API). | Get a fresh token from your org's realm with the right audience. |
403 {"message":"Forbidden"} | The token is valid but lacks the scope this endpoint needs. | Ask your administrator for the scope. |
403 with a "permissions problem" message | Your query touches data your role or service clients cannot read. | See Roles and permissions. |
500 on query endpoints right after login | The token lacks the service_client_id, sub, jti or resource_access claim, or service_client_id is badly formed. | Ask your administrator to assign you a service client and a role. |
4. Roles and permissions¶
4.1 Service clients¶
Comotion uses service clients for row-level security. A service client usually represents one of your own clients or business units, and your access is limited to the service clients assigned to you.
Two IDs are special:
| ID | Meaning |
|---|---|
901 | Public. Everyone with a role can read public rows. It is added to every user's list automatically. |
0 | All service clients. A user whose service_client_id claim includes 0, and who has any role other than PublicConsumer, can read every service client. |
4.2 Roles¶
Roles are assigned per API: on dash_api for the Org API, and on dash_api_<zone> for each zone. A user can have different roles in different zones.
| Role | In short |
|---|---|
PublicConsumer | Reads public (901) insights only. |
OwnServiceClientConsumer | Reads insights for their own service clients. |
OwnServiceClientAnalyst | Reads lake and insights data for their own service clients. Can create tables in the sandbox. |
AllServiceClientAnalyst | Reads all service clients. Can write to lake and insights tables. |
ZoneAdmin | Everything an AllServiceClientAnalyst can do, plus delete and modify tables. |
4.3 Capability matrix¶
| Capability | PublicConsumer | OwnSCConsumer | OwnSCAnalyst | AllSCAnalyst | ZoneAdmin |
|---|---|---|---|---|---|
Read public (901) insights | ✓ | ✓ | ✓ | ✓ | ✓ |
| Read insights for own service clients | ✓ | ✓ | ✓ | ✓ | |
| Read insights for all service clients | ¹ | ¹ | ✓ | ✓ | |
| Read lake and data-input tables (own service clients) | ✓ | ✓ | ✓ | ||
| Read lake and data-input tables (all service clients) | ¹ | ✓ | ✓ | ||
| Create, change and drop tables in the sandbox | ✓ | ✓ | ✓ | ||
| Create and update lake and insights tables, add and remove partitions, write data with SQL | ✓ | ✓ | |||
| Drop and modify lake and insights tables | ✓ | ||||
| Load rows for a service client through the load API | ² | ✓ | ✓ | ✓ (any) | ✓ (any) |
¹ Only if the user's service_client_id claim includes 0. ² Public (901) rows only.
Notes:
- If a user has several roles, the capabilities add up.
- The load API also needs the
load:writescope (or<zone>:load:write). Loads write through Comotion's load pipeline, not with your own permissions. The pipeline checks that every row belongs to a service client you can access. - A user with no recognised role can only read their own query results. Every query that touches data fails.
4.4 How data scoping works in queries¶
Access is enforced on the stored data, which is split by service client. In practice:
- A query that reads data outside your service clients fails with a permissions error. It does not silently drop those rows.
- To read only what you may see, add your service client filter to the query.
GET /identity/security_metadatareturns a ready-made condition, for exampleservice_client_id IN ('12', '901')or1=1(all rows). - Your query results are stored in a location tied to your user, roles and service clients. Only you can read them. If your roles or service clients change, you can no longer read results of queries you ran before the change.
4.5 Scopes per endpoint¶
| Endpoint | Org API scope | Zone API scope |
|---|---|---|
GET /identity/security_metadata | none (any valid token) | not available |
POST /query | query:write | <zone>:query:write |
GET /query/{query_id} | query:read | <zone>:query:read |
DELETE /query/{query_id} | query:write | <zone>:query:write |
GET /query/{query_id}/result | query:read | <zone>:query:read |
GET /query/{query_id}/csv | query:read | <zone>:query:read |
POST /load | load:write | <zone>:load:write |
POST /load/{load_id}/file | load:write | <zone>:load:write |
POST /load/{load_id}/commit | load:write | <zone>:load:write |
GET /load/{load_id} | load:read | <zone>:load:read |
GET /dailyRun/dailyRun_enabled | dailyrun:enabled:read (DailyRun API) | not available |
POST /dailyRun/dailyRun_enabled | dailyrun:enabled:write (DailyRun API) | not available |
GET /dailyRun/execution_status | dailyrun:execution:read (DailyRun API) | not available |
POST /dailyRun/start_execution | dailyrun:execution:write (DailyRun API) | not available |
5. Conventions¶
- Requests: send JSON bodies with
Content-Type: application/json. - Responses: JSON unless stated otherwise.
- Errors: most errors have the body
{"message": "..."}. The identity endpoint also adds anerrorfield. Where a message includes a request code (...please quote this code: <id>), send that code to support. - Gateway errors: some errors come from the API gateway before your request reaches Comotion's code:
401 Unauthorized,403 Forbidden(missing scope),404 Not Found(unknown path),413(request too large) and a timeout error for requests that take longer than 30 seconds. - Timeouts: every endpoint is designed to answer within seconds. Long-running work (queries, load commits, daily runs) runs in the background, and you poll for status.
Shell examples use these variables:
export ORG=<org>
export API=https://$ORG.api.comodash.io/v2
export TOKEN=... # access token, see section 3
Python examples use:
import requests
ORG = "<org>"
API = f"https://{ORG}.api.comodash.io/v2"
TOKEN = "..." # access token, see section 3
HEADERS = {"Authorization": f"Bearer {TOKEN}"}