Skip to content

Authentication

The Querri Public API at https://app.querri.com/api/v1 accepts three authentication methods. Server-to-server integrations should use API keys — the rest of this page covers them in detail, with JWT and cookie auth documented at the bottom for the cases where they apply.

MethodUse it whenHeader(s)
API key (qk_)Server-to-server integrations, scripts, CLIs, BI tools, ZapierAuthorization: Bearer qk_... + X-Tenant-ID: org_...
JWT bearerA backend forwarding a signed-in user’s token to act on their behalfAuthorization: Bearer eyJ... (org_id read from the token)
Cookie + CSRFBrowser apps already logged into Querri (e.g. embedded extensions)access_token cookie + X-CSRF-Token header on mutations

qk_ API keys are the primary authentication method. Every request requires two headers: the API key as a Bearer token and your organization ID.

Terminal window
curl https://app.querri.com/api/v1/projects \
-H "Authorization: Bearer qk_live_abc123..." \
-H "X-Tenant-ID: org_..."
HeaderValueDescription
AuthorizationBearer qk_...Your API key
X-Tenant-IDorg_...Your organization ID

Both headers are required on every API-key request. Omitting either returns 401 Unauthorized.

API keys are created in the Querri web app:

  1. Go to Settings > API Keys
  2. Click Create API Key
  3. Enter a name and select scopes
  4. Copy the key — it is only shown once

The key is displayed once at creation time. Store it securely. If you lose it, revoke the key and create a new one.

You can also manage keys programmatically via the /keys endpoints (requires the admin:keys:manage scope).

Your organization ID (org_...) is shown in Settings > Account and in the API key creation dialog. Copy it from there and set it as an environment variable or pass it directly.

Keys are scoped to limit what they can access. Assign only the scopes each integration needs.

ScopeAccess
admin:users:readList and get users
admin:users:writeCreate, update, delete users
admin:projects:readList and get projects, steps, step data
admin:projects:writeCreate, update, delete, run projects
admin:dashboards:readList and get dashboards
admin:dashboards:writeCreate, update, delete, refresh dashboards
admin:policies:readList and get access policies
admin:policies:writeCreate, update, delete policies, assign users
admin:sources:readList data sources and connectors
admin:sources:writeCreate, update, delete sources, trigger sync
admin:files:readList and get files
admin:files:writeUpload and delete files
admin:permissions:readList sharing settings
admin:permissions:writeShare and revoke access to projects and dashboards
admin:keys:manageCreate, list, revoke API keys
admin:usage:readView usage metrics
admin:audit:readQuery audit log
data:readRead data sources and execute queries
data:writeWrite to data sources
embed:session:createCreate embed sessions

Wildcard scopes are supported: admin:users:* grants both read and write for users. admin:* grants all admin scopes.

API keys support optional restrictions for defense-in-depth:

  • IP allowlist — Restrict which IP addresses can use the key
  • Source scope — Limit which data sources the key can access (by source ID)
  • Bound user — Tie the key to a specific user’s permissions
  • Access policies — Apply row-level security policies to all requests made with the key
  • Rate limit — Per-minute request cap (in addition to the default org-wide limit)

These are configured when creating the key in the UI or via the API.

All API responses include rate limit headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1708300800

When the limit is exceeded, the API returns 429 Too Many Requests with a Retry-After: 60 header. Back off and retry after the indicated period.

If your backend already has a Querri-issued JWT for a signed-in user (from the WorkOS SSO flow), you can forward it directly to call the API as that user:

Terminal window
curl https://app.querri.com/api/v1/projects \
-H "Authorization: Bearer eyJhbGc..."
HeaderRequiredNotes
Authorization: Bearer ey...YesThe user’s JWT
X-Tenant-IDNoThe org_id is read from the token’s claims

Permissions follow the user’s role rather than API key scopes — admins get full access; member roles are limited to operational endpoints. This is the right choice when an action must run on behalf of a specific user (e.g., a backend that calls the API in response to a signed-in user’s actions).

The Python SDK does not currently expose a first-class JWT-forwarding mode — for that flow, construct your own request using httpx or the standard library, or use the SDK’s as_user(session) helper for embed-session-style calls.

Browser clients already logged into Querri are authenticated by the access_token cookie. Mutations (POST, PUT, PATCH, DELETE) additionally require an X-CSRF-Token header that matches the value set on the page.

fetch("https://app.querri.com/api/v1/projects", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": getCsrfToken(),
},
body: JSON.stringify({ name: "From the browser" }),
});

Most integrations should not use this flow — it’s intended for code running inside a Querri browser session (e.g., extensions, embedded tools) where the cookie is already present. For everything else, use an API key.

Missing, invalid, or expired API key:

{
"error": {
"type": "authentication_error",
"message": "Invalid API key"
}
}

Valid key but insufficient scopes or IP not in allowlist:

{
"error": {
"type": "permission_error",
"message": "API key does not have the required scope: admin:projects:read"
}
}
  1. Never expose keys in client-side code — API keys are server-side secrets. Use embed sessions for browser-based access.
  2. Use minimal scopes — Grant only the scopes each integration needs.
  3. Set IP allowlists — If your integration runs from known IPs, restrict the key.
  4. Rotate keys regularly — Revoke old keys and create new ones periodically.
  5. Use environment variables — Store keys in QUERRI_API_KEY, not in source code.
  6. Monitor the audit log — Review API key usage in Settings > Audit Log or via GET /audit/events.