Authentication & API keys
Every programmatic request to the Bird API authenticates with an API key passed as a bearer token. Keys belong to a workspace, carry a fixed set of permissions chosen at creation, and are shown in full exactly once. (The dashboard itself signs in with a session cookie; everything you build authenticates with a key.)
How requests authenticate
Pass your key in the Authorization header on every request. The SDKs and the CLI take the key once and set the header for you:
import { BirdClient } from "@messagebird/sdk";
const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });
await bird.email.send({
from: "hello@yourdomain.com",
to: ["delivered@messagebird.dev"],
subject: "Hi",
text: "Hello.",
});import os
from bird import Bird
client = Bird(api_key=os.environ["BIRD_API_KEY"])
client.email.send(
from_="hello@yourdomain.com",
to=["delivered@messagebird.dev"],
subject="Hi",
text="Hello.",
)client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
_, err = client.Email.Send(context.Background(), bird.EmailSendParams{
From: "hello@yourdomain.com",
To: []string{"delivered@messagebird.dev"},
Subject: "Hi",
Text: "Hello.",
})use MessageBird\Bird;
$bird = new Bird(getenv('BIRD_API_KEY') ?: '');
$bird->email->send(
from: 'hello@yourdomain.com',
to: ['delivered@messagebird.dev'],
subject: 'Hi',
text: 'Hello.',
);export BIRD_API_KEY="bk_us1_..."
bird email send \
--from hello@yourdomain.com \
--to delivered@messagebird.dev \
--subject Hi \
--text Hello.curl -X POST https://us1.platform.bird.com/v1/email/messages \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "from": "hello@yourdomain.com", "to": ["delivered@messagebird.dev"], "subject": "Hi", "text": "Hello." }'The region in the key prefix tells you which host to call: bk_us1_... keys go to https://us1.platform.bird.com, bk_eu1_... keys to https://eu1.platform.bird.com. Official Bird SDKs and the CLI read the region from the key and select the host for you. A key sent to the wrong regional host returns 421 (type misdirected_error); see Regions.
A missing or invalid key returns 401. A valid key that lacks the permission an endpoint requires returns 403. Header semantics and error responses live in the authentication reference.
Anatomy of a key
कोड उदाहरण
bk_us1_Ab3xKq9mP2wR5tY8uI1oL4nJ2kQ9m
└┬┘└┬┘ └──────────┬──────────┘└─┬──┘
│ │ payload checksum
│ └ region (routes the request)
└ Bird key prefix- Prefix: bk_{region}_ names the credential type and its region. The fixed, distinctive prefix is what lets secret scanners recognize a Bird key in code, and the region segment routes your request to the right host.
- Payload: 23 random characters carrying 136 bits of entropy.
- Checksum: the last 6 characters are a checksum of the rest of the key, so an SDK or the API can reject a mistyped or truncated key immediately, before it is ever looked up.
The full key is returned once, in the response that creates it. Bird stores only a keyed hash (HMAC-SHA-256); the plaintext cannot be retrieved again, by you or by Bird. What you can see afterwards is the key_prefix (the first 15 characters, e.g. bk_us1_Ab3xKq9m) and a stable 12-character fingerprint for matching a key in logs and support conversations without exposing its value.
If you lose a key, you don't recover it: you revoke it and create a new one.
Creating a key
Create keys in the dashboard under Developers → API keys. A key is created with a name, one or more scopes, and an optional expiry. The response that creates it is the only one that ever carries the token field (the full key): store it in your secret manager immediately.

Three properties are fixed the moment the key exists:
- The scope set is immutable. Scopes cannot be added or removed on an existing key. To change what a key can do, create a new key with the right scopes and revoke the old one; every key's permissions stay exactly what they were when it was issued and audited.
- Expiry is immutable too. Set expires_at when a key should stop working at a known time (a contractor's engagement, a migration window). Past that moment the key returns 401; a key without an expiry lives until revoked.
- Key management stays with people. Creating and revoking keys requires the api_keys:write permission, held by the workspace admin and developer roles (see Users, teams & roles) and never grantable to an API key itself. A leaked key cannot mint more keys.
The API keys page lists every key with its key_prefix, scopes, and last_used_on date (day precision), so you can spot stale keys at a glance. Revoked keys stay out of the listing unless you choose to show them.
Scopes & levels
Each scope on a key is a {scope, level} pair, where level is read or write (write includes read). API keys hold data-plane scopes:
| Scope | read | write |
|---|---|---|
| emails | Read sent messages and delivery status | Send email |
| email_management | Read suppressions, email configuration, and templates | Manage suppressions, email configuration, and templates |
| email_marketing | Read contacts, audiences, and broadcasts | Manage contacts, audiences, and broadcasts |
| domains | Read sending domains and their DNS records | Add, verify, and manage sending domains |
| sms | Read sent SMS and delivery status | Send SMS |
| whatsapp_messages | Read sent WhatsApp messages and status | Send WhatsApp messages |
| whatsapp_management | Read WhatsApp templates and settings | Manage WhatsApp templates and settings |
| verify | Read verification status | Send and check verification codes |
Control-plane operations (workspace members, settings, key issuance, IP pools) are deliberately not grantable to API keys; they remain dashboard-only. More scopes will be added as products gain programmatic surfaces. Grant the narrowest set that works: a key that only sends email should hold emails:write and nothing else.
Revoking a key
Revoke a key from its row on the Developers → API keys page. Revocation is permanent: a revoked key cannot be reactivated, and its record is preserved for audit with revoked_at set.
Revocation propagates fast but not instantly. Key validation runs through a short-lived cache, so a freshly revoked key can keep working for a few seconds (five at most) before every request with it returns 401.
Bird has no rotate operation. To rotate a key with zero downtime, overlap two keys:
- Create a new key with the same scopes.
- Deploy the new key to your services.
- Watch the old key's last_used_on until traffic has moved.
- Revoke the old key.
Keys belong to the workspace
An API key is bound to exactly one workspace: it authenticates as the workspace, not as the person who created it. That has two practical consequences:
- Keys survive departures. When an employee leaves and their user account is removed, the keys they created keep working. You never have a production outage because the person who clicked "create" left the company. (Their departure is still a good prompt to rotate keys they had access to.)
- The key's reach stops at the workspace. It can never perform organization-level operations: billing, org members, org settings.
Because the key pins the workspace, requests with a key need no extra context; see Workspaces for how the workspace and the organization above it divide up what you can reach.
The delegated path: OAuth tokens for the CLI and MCP server
API keys are for services. When a person uses the Bird CLI or the Bird MCP server, those tools authenticate via OAuth instead: you log in as yourself in the browser, choose a workspace and a subset of your own permissions, and the tool receives a short-lived bt_{region}_... user token. These tokens are capped to what you personally hold and revocable per tool under Profile → Connected apps. They are never something you copy-paste or store in a secret manager; don't use them in place of API keys for server workloads.
Next steps
- Authentication reference: request header semantics and error responses
- Regions: regional hosts and routing
- Users, teams & roles: who can manage keys
- Workspaces: the workspace a key binds to