Key management
Create, scope, rotate and revoke keys, and read usage, from a dashboard session.
One endpoint describes the key you are calling with; the rest manage keys.
Describe the calling key
GET /api/v1/meThe only endpoint on this page that takes an API key (X-API-Key). It
needs no scope and returns no personal data beyond the user id, so it is the
cheapest way to check that a key works and what it may do. The Postal MCP
server uses it to validate a key and to show its scopes on the connect page.
{
"keyId": "k7Qw2ZpL",
"name": "Catalogue sync",
"userId": "usr_…",
"scopes": ["read:tracks", "create:tracks"],
"access": { "allowed": true, "reason": "plan" },
"expiresAt": "2027-01-01T00:00:00.000Z"
}| Field | Meaning |
|---|---|
keyId | The 8-character id before the dot |
name | The name given at creation |
userId | The account the key acts as |
scopes | The key's scopes |
access.allowed | Whether the owner may manage keys right now |
access.reason | plan (paid subscription), flag (api_access override) or none |
expiresAt | ISO date, or null for no expiry |
A revoked, expired or unknown key gets the usual 401 with invalid_key or
expired_key. access.allowed: false does not stop the key: keys keep
authenticating after the owner's plan lapses, the owner only loses the
settings screens.
Managing keys
The remaining endpoints manage API keys. They are authenticated with the web app's session token, not with an API key, because a key must never be able to mint or widen another key. Most integrations never call them; the app's Settings → Integrations and Tools → Integration screens cover the same operations.
Base path: https://api.postal.music/integration. Send
Authorization: Bearer <session token>.
Create a key
POST /integration/api-keys{ "name": "Catalogue sync", "scopes": ["read:tracks", "create:tracks"], "days": 365 }| Field | Type | Notes |
|---|---|---|
name | string | Required |
scopes | string[] | Any of the assignable scopes. Omit for all of them |
days | integer ≥ 1 | Expiry. Omit for no expiry |
Returns { "key": "k7Qw2ZpL.Xj9c…", "id": "k7Qw2ZpL" }. The key is shown only
here.
Current key
GET /integration/api-keysReturns the newest non-revoked key: id, name, scopes, webhookUrl,
webhookActive, createdAt, lastUsedAt, expiresAt, and the raw key.
Scope catalog
GET /integration/api-keys/scopesReturns { "scopes": [ … ] }, the assignable list.
Replace scopes
POST /integration/api-keys/{id}/scopesBody { "scopes": [ … ] }. Unknown scopes return 400 invalid_scope. Keys
carrying act-as-user or a platform:* scope cannot be edited.
Set webhook
POST /integration/api-keys/{id}/webhookBody { "url": "https://…", "active": true }. See Webhooks.
Rotate
POST /integration/api-keys/{id}/rotateIssues a new id and secret and invalidates the old ones. Returns
{ "id", "createdAt", "key" }. Scopes and webhook settings carry over.
Revoke
POST /integration/api-keys/{id}/revokePermanent. Returns { "ok": true }.
Usage
GET /integration/usage?limit=50&cursor=…&from=2026-09-01&to=2026-09-07Every request made with your keys: route, method, status, duration, IP and
timestamp. Cursor-paginated with items and nextCursor.