This is the shortest path from "I have a BetterSuite tenant" to "my server is talking to BetterSuite over GraphQL." Conceptual depth lives in Authentication — this page is a checklist.
Prerequisites
- An active BetterSuite tenant.
- Tenant Owner access to the Owner Dashboard. API key creation is Owner-tier — a regular Tenant Admin cannot issue or rotate keys.
- A terminal with
curlandjq, or any HTTP client.
Step 1: Create an API key
- Sign in to
bettersuite.io/dashboard. - Open
API Keysfrom the sidebar (one of the flat sidebar items in the Owner Dashboard). - Click Create Key, give it a name (e.g.
dev-laptop), and optionally setRate limit (req/min)andRate limit (req/day). Leave both blank to inherit the tenant default — see Rate Limits. - Copy the key from the amber banner that appears. We show the plaintext exactly once; the database only stores the SHA-256 hash. If you lose it, rotate it.
The key looks like this:
btk_019234ab_Kj8mN2pQrStUvWxYz1a2B3c4D5e6F7g8H9i0...
btk_— fixed prefix for a BetterSuite tenant key.019234ab— first 8 characters of your tenant's UUID, helpful when grepping logs.- The rest is 32 random bytes, base64url-encoded.
The dashboard lists every key by its key_prefix (first 12 chars), creation time, last used time, and rate-limit overrides. Active keys can be rotated (a step-up password prompt; old key revoked, new one issued) or revoked.
Step 2: Hit the endpoint
The production endpoint:
POST https://api.bettersuite.io/graphql
Send the key in the X-Api-Key header — not Authorization: Bearer. The Authorization header is reserved for user-bound JWT sessions.
Step 3: Send your first request
A simple query — tenantInfo returns your tenant's display name, currency, status, plan, and branding. It's a good handshake call because it needs only the API key for authentication.
cURL
curl https://api.bettersuite.io/graphql \
-H "X-Api-Key: $BETTERSUITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ tenantInfo { id name baseCurrency status } }"}' | jq
JavaScript
const res = await fetch("https://api.bettersuite.io/graphql", {
method: "POST",
headers: {
"X-Api-Key": process.env.BETTERSUITE_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "{ tenantInfo { id name baseCurrency status } }",
}),
});
console.log(await res.json());
Python
import os, requests
res = requests.post(
"https://api.bettersuite.io/graphql",
headers={"X-Api-Key": os.environ["BETTERSUITE_API_KEY"]},
json={"query": "{ tenantInfo { id name baseCurrency status } }"},
)
print(res.json())
A successful response looks like:
{
"data": {
"tenantInfo": {
"id": "01923a4b-...",
"name": "Acme Rides",
"baseCurrency": "USD",
"status": "ACTIVE"
}
}
}
If data is null and errors is populated, jump to the next section.
Step 4: Browse the schema
You can introspect via the standard __schema query, but it returns ~1.5 MB. The structured reference at GraphQL API is generated from the same schema.graphql and is grouped by subgraph (identity, tenant, taxi, shop, parking, payment, payout, etc. — 21 subgraphs in total).
Common errors
The auth middleware returns HTTP-level failures (the request never reaches the GraphQL handler):
| HTTP | Message | Cause |
|---|---|---|
401 | Invalid API key format | Key doesn't start with btk_ or is shorter than 12 chars |
401 | Unknown API key | Key's hash isn't in the database — probably a typo or a key from another environment |
401 | API key is revoked or expired | Key exists but was revoked, or its expires_at is in the past |
401 | Invalid or expired token | Sent on the Authorization: Bearer header path — likely a malformed JWT |
GraphQL-level errors (HTTP 200 with errors populated) carry a structured code in extensions.code. Common identity codes include UNAUTHORIZED, TENANT_NOT_FOUND, INVALID_CREDENTIALS, INVALID_REFRESH_TOKEN, SESSION_REVOKED, SESSION_EXPIRED, STEP_UP_REQUIRED, PERMISSION_DENIED, and VALIDATION_ERROR.
What's next
- API Keys — rotation, revocation, storage discipline.
- JWT Sessions — for user-bound calls from a mobile or web app.
- RequestContext — what the backend resolves about each call.
- Rate Limits — what counts and how to back off.