| title | Authentication |
|---|---|
| description | API keys, session JWTs and team scoping. |
Every request to every service authenticates with one header:
Authorization: Bearer <token>
Two token kinds are accepted, and the server tells them apart by shape.
An API key is an opaque bearer token of the form gtm_live_ plus 40 characters. There is no OAuth handshake: you paste it into your runtime and send it as the bearer token on every call.
- The secret is shown once.
POST /api/api-keysreturns the full token inplaintext_tokenin that one response. Afterwards the API returns onlytoken_prefixandtoken_last4for display. The server stores a SHA-256 hash, not the key. - Rotation replaces the secret.
POST /api/api-keys/{sid}/rotatemints a newgtm_live_secret for the same key record and invalidates the old one. Rotate on any suspicion of a leak, or on a schedule. - Keys expire. Default expiry is 3 years from creation; pass
expires_at: nullon create for a perpetual key. - Keys carry permissions. A key created by another key can only hold a subset of the parent's permissions. Downscoping is allowed, escalation is a
forbiddenerror. - Keys are team-scoped. A key belongs to the team it was created in and acts on that team's data.
- A key is not a person. It authenticates as itself, with its own permissions, so no user account sits behind it.
GET /api/users/current(get_current_user) therefore answers403withcontext.reason: not_a_user_actoron a key, and no permission you add to the key changes that: the profile that endpoint returns belongs to a signed-in person. Sign in, or connect through OAuth, when you actually need it.
Manage keys in the app or over the ID and Teams API: search them for an access audit (last_used_at shows stale keys worth revoking), check expires_at for keys about to lapse, and revoke what you no longer use.
Signing in to app.gtm-api.com issues a short-lived JWT. It goes in the same Authorization: Bearer header. This is what the app itself uses; for server-to-server integrations prefer an API key, which does not expire mid-job.
A token is normally already scoped to one team, and that scope is authoritative. The optional Team-SID header exists for tokens that do not carry a team: it selects which of your teams the call acts on.
Team-SID: ts_tm_Hx7kQ3mN2pL4
When the token already names a team, Team-SID is ignored. With an API key you can leave it out entirely.
| Code | HTTP | Meaning |
|---|---|---|
unauthorized |
401 | Missing, expired, revoked or malformed token |
forbidden |
403 | Valid token, but no rights on this team or resource |
Both arrive as the standard error envelope, so they are machine-readable like every other failure.
Treat gtm_live_ tokens like passwords: keep them in a secret manager or environment variable, never in client-side code or a repository, and never in logs. If a key leaks, rotate it; the old secret stops working immediately.