Service accounts are available on the Business and Custom plans. On other plans the
endpoints below return
403 SERVICE_ACCOUNTS_REQUIRE_PLAN.How it differs from the MCP server
The MCP server acts as you: it runs an OAuth flow in a browser, and everything it does is attributed to your user account. That is right for an AI assistant and wrong for a server, which has no browser and should not be borrowing a person’s identity. A service account is its own identity. It is bound to one team, carries its own role and scopes, and keeps working when the person who created it leaves.Create one
From Team settings → API keys in the dashboard, or through the API as a team admin:Use it
Send the key as a bearer token, exactly where a user access token would go:Roles and scopes
A service account has both, and both are enforced on every request.owner is not assignable. An owner can transfer or delete the team, which is not something a
credential sitting in a CI runner should be able to do.
A key without
katla:write gets 403 INSUFFICIENT_SCOPE on any non-GET request. Issue
read-only keys wherever the integration only reports — a dashboard, an alerting job — so a
leak cannot change anything.
What a service account cannot do
The boundary is deliberate: a service account operates your sites and compliance data; it cannot administer your account. It is refused, with403 SERVICE_ACCOUNT_FORBIDDEN, on:
- Billing — plans, payment methods, invoices, cancellation
- Team members — inviting, removing, or listing people
- Team lifecycle — creating, renaming, transferring, or deleting a team
- Google Tag Manager connection, which needs a human at a Google consent screen
- Service accounts themselves, including issuing further keys
403, not that team’s data.
Rotate without downtime
An account can hold more than one live key, which is what makes rotation a deploy rather than an outage:Revoke
Revoking the account withdraws every key under it at once — use this if a machine is decommissioned or you suspect a compromise:Keeping keys safe
- Store them as secrets. A
katla_sk_key is a password. Never commit one, and never put one in frontend code — the API refuses cross-origin browser calls, so a key in a browser is exposed without even working. - Give each integration its own account. Then revoking one does not break the others, and
lastUsedAttells you which integrations are actually live. - Prefer read-only. Most integrations only report.
- Set an expiry.
expiresInDaysputs an upper bound on the damage from a key nobody remembers.
Rate limits
Every credential gets its own budget, so one key cannot spend another’s — or yours.
Over the limit you get
429 with code: RATE_LIMIT_EXCEEDED, a retryAfterSeconds field,
and a standard Retry-After header — back off on that rather than retrying immediately.
X-RateLimit-Remaining tells you how much budget is left before you get there.
Because the budget is per key, rotating a leaked key also gives the replacement a clean
slate rather than inheriting whatever the old one spent.
Reference
All routes require a teamadmin and the Business or Custom plan.