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.
Choosing an auth method
Section titled “Choosing an auth method”| Method | Use it when | Header(s) |
|---|---|---|
API key (qk_) | Server-to-server integrations, scripts, CLIs, BI tools, Zapier | Authorization: Bearer qk_... + X-Tenant-ID: org_... |
| JWT bearer | A backend forwarding a signed-in user’s token to act on their behalf | Authorization: Bearer eyJ... (org_id read from the token) |
| Cookie + CSRF | Browser apps already logged into Querri (e.g. embedded extensions) | access_token cookie + X-CSRF-Token header on mutations |
API Keys
Section titled “API Keys”qk_ API keys are the primary authentication method. Every request requires two headers: the API key as a Bearer token and your organization ID.
Required Headers
Section titled “Required Headers”curl https://app.querri.com/api/v1/projects \ -H "Authorization: Bearer qk_live_abc123..." \ -H "X-Tenant-ID: org_..."| Header | Value | Description |
|---|---|---|
Authorization | Bearer qk_... | Your API key |
X-Tenant-ID | org_... | Your organization ID |
Both headers are required on every API-key request. Omitting either returns 401 Unauthorized.
Creating API Keys
Section titled “Creating API Keys”API keys are created in the Querri web app:
- Go to Settings > API Keys
- Click Create API Key
- Enter a name and select scopes
- 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).
Finding Your Organization ID
Section titled “Finding Your Organization ID”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.
API Key Scopes
Section titled “API Key Scopes”Keys are scoped to limit what they can access. Assign only the scopes each integration needs.
| Scope | Access |
|---|---|
admin:users:read | List and get users |
admin:users:write | Create, update, delete users |
admin:projects:read | List and get projects, steps, step data |
admin:projects:write | Create, update, delete, run projects |
admin:dashboards:read | List and get dashboards |
admin:dashboards:write | Create, update, delete, refresh dashboards |
admin:policies:read | List and get access policies |
admin:policies:write | Create, update, delete policies, assign users |
admin:sources:read | List data sources and connectors |
admin:sources:write | Create, update, delete sources, trigger sync |
admin:files:read | List and get files |
admin:files:write | Upload and delete files |
admin:permissions:read | List sharing settings |
admin:permissions:write | Share and revoke access to projects and dashboards |
admin:keys:manage | Create, list, revoke API keys |
admin:usage:read | View usage metrics |
admin:audit:read | Query audit log |
data:read | Read data sources and execute queries |
data:write | Write to data sources |
embed:session:create | Create embed sessions |
Wildcard scopes are supported: admin:users:* grants both read and write for users. admin:* grants all admin scopes.
Key Restrictions
Section titled “Key Restrictions”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.
Rate Limiting
Section titled “Rate Limiting”All API responses include rate limit headers:
X-RateLimit-Limit: 60X-RateLimit-Remaining: 58X-RateLimit-Reset: 1708300800When 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.
JWT Bearer Tokens
Section titled “JWT Bearer Tokens”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:
curl https://app.querri.com/api/v1/projects \ -H "Authorization: Bearer eyJhbGc..."| Header | Required | Notes |
|---|---|---|
Authorization: Bearer ey... | Yes | The user’s JWT |
X-Tenant-ID | No | The 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.
Cookie + CSRF
Section titled “Cookie + CSRF”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.
Error Responses
Section titled “Error Responses”401 Unauthorized
Section titled “401 Unauthorized”Missing, invalid, or expired API key:
{ "error": { "type": "authentication_error", "message": "Invalid API key" }}403 Forbidden
Section titled “403 Forbidden”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" }}Security Best Practices
Section titled “Security Best Practices”- Never expose keys in client-side code — API keys are server-side secrets. Use embed sessions for browser-based access.
- Use minimal scopes — Grant only the scopes each integration needs.
- Set IP allowlists — If your integration runs from known IPs, restrict the key.
- Rotate keys regularly — Revoke old keys and create new ones periodically.
- Use environment variables — Store keys in
QUERRI_API_KEY, not in source code. - Monitor the audit log — Review API key usage in Settings > Audit Log or via
GET /audit/events.