Skip to main content

API keys

An API key proves a request is coming from your account. There is no login screen, no approval step, and no token that expires after an hour — which makes it the right choice for your own server software, and the wrong thing to put anywhere a customer could read it.

Create one​

To create a key, open the admin center and go to Integrations → API Keys, then click to add a new one. You will be asked to fill in a few settings — here is what each one means:

SettingWhat it means
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.
Limit it to specific objectsOptional resource grants. The flow picker appears once flows:call is ticked; it is only used by the flow call trigger.

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 twelve:

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

The admin center pulls this list live from the server (GET /api/v1/api-keys/scopes) — new scopes appear there automatically.

Example — turn on only the scopes you use. Each scope unlocks one kind of action, so tick only the ones your key actually uses:

To do thisIt calls…Scope
List your texting numbersGET /sms/linessms:read
Send a textPOST /sms/messagessms:send
Read conversationsGET /sms/conversationsmessages:read
List account usersGET /usersusers:read
Get a call recordingGET /calls/{callid}/recordingsrecordings:read
Manage webhooksGET /webhookswebhooks:read

You don't call GET /api/v1/api-keys/scopes yourself — it is just where the admin center gets its checkbox list. You tick the scopes you want when you create the key.

A key with no scopes selected reaches nothing

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

The scopes are set when you create the key and are not added to on their own. If we later add an endpoint that needs a new scope, your existing keys are refused (403) on it until someone edits the key and turns that scope on.

The key is shown only once, right when you create it. Copy it somewhere safe straight away — we store only a scrambled (hashed) version and can never show it to you again. If you lose it, reissue it.

Use it in a request​

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. (The one exception is the flow call trigger, outside this API, which also reads X-API-Key. Use Authorization there too.)

What a key can reach​

Every request with a key is checked against six things. All six must pass:

  1. The key is genuine — it matches the scrambled (hashed) copy we saved when it was created.
  2. It has not been switched off (revoked).
  3. It has not expired.
  4. It has the scope (permission) the endpoint needs.
  5. The request comes from one of the allowed addresses, if you set any.
  6. The thing it is trying to touch is within the key's allowed objects (resource grants), if you set any.

Unless you limit it to specific objects, a key reaches the whole account, within its scopes — no person's role narrows it, because a key stands for the account, not an individual. For example, GET /users returns every user on the account.

Resource grants: narrowing a key to specific objects​

A scope says what a key may do (e.g. send texts); a grant says which exact things it may do it to (e.g. which specific numbers it may text from). With no grants, a scope covers your whole account; with grants, only the items you list.

{
"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, number, hha and phone.

A number grant may name a texting number, a fax line or a voice number. It limits which lines a key may text from, read messages on and act on conversations for, and which fax lines it may send faxes from and read faxes on. It is one list for all of them, so a key granted only a fax line is also limited to that line for SMS. On the fax endpoints a grant narrows the role of the user the key runs as; it never adds a number that role does not have. See Faxes.

A number grant also limits caller ID. A key with number grants may only show one of those numbers as the caller ID on a call it places with POST /calls. It may name any number of the account: a texting number, a fax line or a voice number.

Phones: required for placing calls​

A phone grant names a phone (line) the key may ring first with POST /calls, for example {"type": "phone", "id": "6589455"}. Each phone must be a phone of your account; it does not have to belong to the user the key runs as.

A key with calls:make must list its phones. Unlike every other type, no phone grants does not mean unrestricted: a key with no phones selected cannot place calls, and every POST /calls gets 403 permission_denied ("this API key has no phones it may ring; add phones to the key to place calls"). See Click to call.

modeMeaning
allNo limits. The default, and what every key created before this feature means.
selectedThe grants apply. You must list at least one.
Grants apply per type, not globally

Under selected, a type of thing that has grants is limited to those grants, and a type with no grants stays open.

So a key granted two flows and no numbers is limited on flows and reaches every number — and a key granted only numbers can still call every flow, if it holds flows:call. 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.

The one exception is phone: with no phone grants, a key cannot place calls at all. See Phones.

A flow grant names a flow by its id — the number at the end of the flow editor's address. It is enforced by the flow call trigger: a key with flow grants may call only those flows, and never a flow on another account, whatever its grants say.

An hha grant names one of the account's HHA Exchange connections by its id. A key with hha grants looks callers up only in those connections, and only in connections the user it runs as may already use; a grant never adds one.

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

flowId still works​

The older flowId field is the same idea in its single-flow form — it names one call flow and is read as {"type": "flow", "id": "…"}. Anything using it keeps working.

A grant can be a flow (a call flow — for the call-trigger capability) or a number (a phone number — for the SMS endpoints). flowId can only ever name a single flow: never a second flow, and never a number. resources can express all of it, so if you send both, resources wins. Use resources for anything new:

Old way — one flow only:

{ "flowId": "7f3a" }

New way — any mix of flows and numbers:

{
"resources": {
"mode": "selected",
"grants": [
{ "type": "flow", "id": "7f3a" },
{ "type": "number", "id": "12125550188" }
]
}
}
Coming change

A key will run as its owner, so the owner's admin-center permissions will limit it too, on top of its scopes — the same rule OAuth already follows: what the key can do will be its scopes, limited by the owner's role. Today a key's owner role is ignored; after this change, a key whose owner lacks a permission loses that access too. Nothing has changed yet; everything above is how the API works today.

One thing no key can unlock: if the account has sensitive-content re-verification turned on, call recordings cannot be reached through the API at all. That check needs a real person at a browser, and a key has nothing to show it. See Recordings.

Rotate a key​

Reissuing keeps everything about the key the same — name, scopes, allowed addresses, owners — and just creates a new secret with a fresh expiry date.

There is no grace period

When you reissue a key, its old secret stops working the instant the new one is created — the two secrets never overlap. So put the new secret in place first, or expect a short gap — a brief window where your requests fail (401) because your software is still sending the old, now-dead secret.

If you need an overlap, do not reissue — instead create a second, separate key. Both keys are valid at the same time, so you can move your systems over to the new one at your own pace, and only then turn off the old one.

A key you have turned off (revoked) cannot be brought back — that is final. Create a new key instead.

Revoke a key​

Turning off a key takes effect on its next request. Do it right away if a key is exposed — in a log, a commit, a screenshot, or a ticket. Because a key reaches the whole account within its scopes, a leak is account-wide: turn it off first, investigate after.

Keeping a key safe​

  • Keep it on your own server. Never inside a web page, a mobile app, or anything you hand to a customer.
  • One key per system. If several systems share a key, you cannot turn it off without breaking them all at once — and the history cannot tell you which system did what.
  • Give it as few scopes as possible. A report job — a program that only reads your data to build a report — does not need sms:send. See Scopes.
  • Limit it to the objects it needs. A key that texts from one number should be granted just that number, so a leak cannot spread past it — see Resource grants.
  • Use the allowed-addresses list. Set it when your system sends from a fixed internet address — it is the one protection that still helps even if the key itself leaks.
  • Never write the key into your logs. If you need to tell which key a log entry belongs to, record only its first few characters (tbk_live_ab…), never the whole value.

Audit trail​

Every change made with a key is recorded with source: api and the id of the key that made it — the key's non-secret label, not the tbk_live_… secret. So in the account's change history, an owner can tell an integration's change from a person's and trace it to the exact key. For example, a number blocked by a key is tagged source: api with the key's id, not attributed to a person.

An owner reviews this history in the admin center; there is no API to read it back. Reads are not recorded — logging every list refresh would bury the changes that matter.

Rate limits​

None yet — the per-key call-limit fields are not applied to this API. See Rate limits before relying on that.