Authentication
Two credential types, what each one reaches, and how project scoping works.
Every request carries a bearer credential:
Authorization: Bearer pb_live_...There are two prefixes. The prefix is not decoration: it is what the server uses to decide what the credential can reach.
| Prefix | Reaches | Held by |
|---|---|---|
pb_live_… | Everything in a project | Your server. Never a browser. |
pb_tok_… | One single mailbox | A browser or end-user client. |
Project keys (pb_live_)
A project key belongs to one project inside your org. Create one in the console, or over the API for automated rotation (see API keys).
For /messages, /events, /tokens, and /actions the key is already
project-scoped. There is nothing else to pass.
For org-level routes (/domains, /mailboxes, /webhooks, /folders,
policies, keys, stats) the key authenticates the whole org, so you point a
call at one project with a header:
X-Project-Id: prj_...Leave it off for genuinely org-level calls, such as listing projects. An unknown
or foreign project ID returns 404, not 403, because Postbox does not confirm
the existence of resources you cannot reach.
A pb_live_ key is project-scoped, but your plan allowance is metered
org-wide. A busy project on a shared org plan can trip the cap for every other
project in the org. Account for this before splitting environments across many
projects on a low tier.
Mailbox tokens (pb_tok_)
A mailbox token is minted server-side from a project key and is safe to hand to an untrusted client. It is locked to one mailbox for its whole life.
const { token } = await pb.tokens.create({
mailboxId: mailbox.id,
permissions: ["read"], // required: name at least one scope
expiresIn: 3600, // seconds; defaults to 30 days
});
// → { id, token: "pb_tok_...", expiresAt }permissions is required. A token minted without at least one scope is rejected
with 400, because a credential handed to a browser must never default to full
mailbox control. See Permissions.
Using a pb_tok_ against a sibling mailbox in the same project returns 403. It
never returns another mailbox's data.
Keep expiresIn short. The default of 30 days is a convenience, not a
recommendation, and a token in a browser is a token you no longer control.
The raw HTTP
The SDK is a thin wrapper. To mint a token from a language without one, the contract is:
POST /v1/tokens
Authorization: Bearer pb_live_...
Content-Type: application/json
{ "mailboxId": "…", "permissions": ["read"], "expiresIn": 3600 }{ "success": true, "data": { "id": "…", "token": "pb_tok_...", "expiresAt": "…" } }Minting a token requires the manage permission.
Revoking a token
A token stays valid until it expires, unless you revoke it first. A revoked token
fails every request with 401 immediately, even while its expiresAt is still in
the future.
POST /v1/tokens/{id}/revoke
Authorization: Bearer pb_live_...{ "success": true, "data": { "id": "…", "revokedAt": "…" } }Revocation is idempotent: revoking a token that is already revoked returns its original revocation time. To cut a whole mailbox off at once (a suspended student, say) revoke every active token it holds in one call:
DELETE /v1/mailboxes/{mailboxId}/tokens
Authorization: Bearer pb_live_...{ "success": true, "data": { "revoked": 3 } }Both require the manage permission. A mailbox-scoped pb_tok_ may only revoke
tokens for its own mailbox.
Permissions
Permissions are checked per credential. Today the set is coarse:
| Permission | Grants |
|---|---|
send | Sending mail. |
receive (alias read) | Reading and listing messages. |
manage (alias manage_folders) | Managing folders, minting tokens, and other management writes. |
Credentials fail closed. A credential whose permission list is empty holds no
scopes, so every permission check denies it with 403. There is no "unscoped
means full access" default. Name the scopes you actually need: a read-only inbox
token should carry ["read"] and nothing more.
Keeping keys safe
- Never ship a
pb_live_key to a browser, a mobile app, or anything else you do not control. Mint apb_tok_instead. - Give each environment its own key so you can revoke one without an outage everywhere else.
- Give each credential only the scopes it needs. A leaked
send-only key cannot read mail or manage the account, and a credential with no scopes reaches nothing. - Rotate by creating the new key, deploying it, then revoking the old one. Keys are shown once at creation and stored only as a hash, so a lost key cannot be recovered, only replaced.