Skip to main content

API keys

An API key authenticates your account. No redirect, no consent screen, no expiring access token — which makes it the right credential for your own backend, and the wrong one to put anywhere a customer could read it.

Create one

In the admin center: Integrations → API Keys.

SettingNotes
NameFor your own reference. Required.
ScopesWhat the key may do — see Scopes. Select only what the integration needs.
ExpiryRequired. There is no never-expiring key. You are emailed before it lapses.
OwnersAccount users notified about this key's expiry, and shown as responsible for it. Defaults to whoever created it. At least one is required.
Allowed IPsOptional allowlist of exact IPs or CIDR blocks. If set, requests from anywhere else are refused.
Flow pinOnly relevant to the flows call-trigger API. Leave unset for the REST API.

Scopes to select for the whole API

Every endpoint on this API accepts a key — there is no OAuth-only endpoint. A key reaches an endpoint only if it carries that endpoint's scope, so a key meant to do everything needs all ten:

webhooks:read webhooks:write
sms:read sms:send sms:write
messages:read messages:write
users:read reports:read recordings:read

The admin center renders this list from the server, so it is always current — GET /api/v1/api-keys/scopes is where it comes from, and a scope we add appears there without a portal release.

:::danger A key with no scopes selected reaches nothing It does not default to "everything" — it defaults to the flows call-trigger scope, which no REST endpoint uses. Every request returns 403 insufficient_scope. Select scopes explicitly. :::

Scopes are fixed at creation and are not extended automatically. When we add an endpoint that needs a new scope, existing keys 403 on it until their owner edits the key and selects it.

The key is displayed once, at creation. Store it in your secret manager immediately — we keep only a hash and cannot show it to you again. If you lose it, reissue.

Send it

curl https://api.account.telebroad.com/api/public/v1/sms/lines \
-H "Authorization: Bearer tbk_live_…"

Authorization: Bearer is the only accepted header. There is no X-API-Key fallback — the same header carries an OAuth token, so your HTTP layer needs one code path either way.

What a key can reach

A key is checked on six things. All six must pass:

  1. The key exists and matches its stored hash.
  2. It has not been revoked.
  3. It has not expired.
  4. It carries the scope the endpoint requires.
  5. The request's source IP is in the allowed IPs, if any are set.
  6. The object it names is within the key's resource grants, if any are set.

Without resource grants, a key gets the whole account, within its scopes — there is no user role narrowing it, because a key authenticates an account rather than a person. GET /users returns every user; GET /sms/lines returns every SMS-capable number.

Resource grants: narrowing a key to specific objects

A scope says what a key may do. A grant says which objects it may do it to.

{
"scopes": ["sms:send", "messages:read"],
"resources": {
"mode": "selected",
"grants": [
{ "type": "number", "id": "12125550188" },
{ "type": "number", "id": "12125550199" }
]
}
}

That key can text and read messages on those two numbers and nothing else, even though the account owns forty. Ask for GET /api/v1/api-keys/resource-types for the current list of grantable types — today flow and number.

modeMeaning
allNo restriction. The default, and what every key created before this feature means.
selectedThe grants apply. At least one grant is required.

:::caution Grants apply per type, not globally Under selected, a type with grants is restricted to them, and a type with no grants is unrestricted.

So a key granted two flows and no numbers is limited on flows and reaches every number. That is deliberate: the alternative would mean pinning a key to a flow silently removed its messaging access, and every resource type we add later would break every existing key.

To restrict a type, grant at least one object of it. :::

Grants are validated when you save them. A flow or number that is not on your account is rejected there and then, rather than stored as a grant that can never match anything.

flowId still works

The older flowId field is the same idea in its original one-flow shape, and keys and clients still using it are unaffected — it is read as {"type": "flow", "id": "…"}. If you send both, resources wins, because it is the one that can express what flowId cannot. Prefer resources for anything new; flowId cannot describe two flows, or a number.

:::info This is changing A key will run as its owner, so the owner's admin-center role applies on top of the scopes — the same rule OAuth already follows:

effective access = key scopes ∩ owner's role

When it ships, a key whose owner lacks a permission loses the matching access, and this page will say so plainly. Nothing has changed yet; what is documented above is what the API does today. :::

One thing no key unlocks: if the account has sensitive-content re-verification enabled, call recordings are unavailable over the API entirely. That gate wants a live human at a browser, and there is nothing a machine credential can present. See Recordings.

Rotate a key

Reissue keeps the key's identity — same name, scopes, IP allowlist, owners — and mints a new secret with a fresh expiry.

:::caution There is no grace period The old secret stops working the moment the new one is issued. Deploy the new secret first, or accept the gap. If you need overlapping validity, create a second key, migrate, then revoke the first. :::

A revoked key cannot be reissued — revocation is final. Create a new key instead.

Revoke a key

Revoking takes effect on the next request. Do it immediately if a key is exposed — in a log, a git commit, a screenshot, a support ticket.

Because a key carries the whole account within its scopes, a leaked key is an account-level incident, not a per-user one. Revoke first, investigate second.

Keeping a key safe

  • Server-side only. Never in a browser bundle, a mobile app, or anything shipped to a customer.
  • One key per integration. Shared keys cannot be revoked without breaking everything at once, and the audit trail cannot tell you which system acted.
  • Narrow scopes. An export job does not need sms:send. See Scopes.
  • Narrow resources. A key that texts from one number should be granted that one number. A leaked key is then limited to it — see Resource grants.
  • Use the IP allowlist when the caller has a stable egress address. It is the one control that survives the key itself leaking.
  • Never log the key. Log its prefix (tbk_live_ab…) if you need to correlate.

Audit trail

Writes made with a key are recorded with source api and the key's id, so an account owner can tell a portal action from an integration's. Reads are not audited — an entry per number-picker refresh would bury the writes that matter.

Rate limits

None are enforced today, on keys or otherwise. The per-key call-limit fields are not applied to this API. See Rate limits before you build on that.