Authentication
https://terrads.com/api/v1Updated September 10, 2026Every request to the API is authenticated with an API key sent as a bearer token. There are no sessions, cookies or sign-in flows on the API.
curl -s "$BASE/brands" -H "Authorization: Bearer $TOKEN"
Keys start with trds_live_. Anything else in the header — a missing header,
a different prefix, a revoked key — is a 401 with
invalid_api_key. The API never says which of those it was.
Creating a key
Keys are created in Settings → API keys in the app and are shown exactly once, at creation; we store only a hash, so a lost key must be replaced. A key carries a name you choose and a prefix (the first few characters) so you can tell keys apart later. Keys act on behalf of your whole team: a run created with a key is visible to every member in the app, and a key can read every run the team owns.
Creating keys requires a plan that includes API access. If your plan does not, the settings page tells you which plan does.
Revoking a key
Revoke from the same settings page. Revocation is immediate: the next request
with that key is a 401. A key also stops working, immediately, when the
person who created it leaves the team or their account is suspended or
deleted — even if the key itself was never revoked. Treat keys as belonging to
the person who created them, and rotate when people leave.
Plan checks happen on every request
Holding a key is not enough on its own. On every request the API checks that your team still has API access and is in good standing:
| Situation | Response |
|---|---|
| The team has no active subscription | 403 no_subscription |
| The plan does not include API access | 403 upgrade_required |
| The subscription's last payment failed and is being retried | 402 past_due |
Creating a run adds one more check, at that endpoint only: a run that would
cost more credits than the team holds is a 402 insufficient_credits, and
nothing is charged.
These are the same rules the app applies: a team that downgrades or falls behind on payment sees the same refusals in the app and on the API. Existing keys keep working the moment the plan is restored — there is nothing to re-create.
Scopes
Every key created today carries every scope, so you never need to request one. Endpoints are still grouped by the scope they will require once scoped keys exist, and the endpoint reference lists it per route:
| Scope | Covers |
|---|---|
runs:read | Reading runs, their events and their results |
runs:write | Creating runs, answering questions, approving copy, cancelling |
catalog:read | Brands, products, angles, audiences, models |
A request that lands on an endpoint outside its key's scopes would be a
403 with insufficient_scope. With today's keys you will not see it.
What a key can do if it leaks
Say this part plainly, because it is the part that costs money. A key is a team-wide credential, so whoever holds it can:
- Read every run the team owns, including the copy and the finished creative.
- Create runs, which spend the team's credits.
maxCreditscaps a single run, but the caller chooses that value — it protects you from a surprise, not from someone using the key on purpose. - Cancel runs in progress, and answer or approve the ones that are waiting.
A key cannot change billing, invite anyone, read another team's anything, or do anything an admin can do.
If a key leaks
- Revoke it in Settings → API keys. Revocation is immediate: the next
request with that key is a
401. - Create a replacement and deploy it. There is no rotation window to wait out — revoke first, then replace.
- Assume everything the team owns was readable while the key was out, and check your credit balance and run history for activity you did not start.
Keeping keys safe
- Server-side only. Never put a key in client-side code, a browser, a mobile app, a desktop app, or a public repository. Anything shipped to a user's device is readable by that user.
- Never in a URL. Send it in the
Authorizationheader, never a query string. URLs end up in access logs, proxy logs,Refererheaders and browser history; headers do not. No endpoint here accepts a key any other way. - Always
https://. Never send a key over plain HTTP. - One key per integration, named after it, so revoking one does not take down another.
- Rotate when people leave. A key also stops working by itself when the person who created it leaves the team or their account is suspended.
- The API never echoes a key back, not even in an error message.