Postal Developers
REST API

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/me

The 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.

Response
{
  "keyId": "k7Qw2ZpL",
  "name": "Catalogue sync",
  "userId": "usr_…",
  "scopes": ["read:tracks", "create:tracks"],
  "access": { "allowed": true, "reason": "plan" },
  "expiresAt": "2027-01-01T00:00:00.000Z"
}
FieldMeaning
keyIdThe 8-character id before the dot
nameThe name given at creation
userIdThe account the key acts as
scopesThe key's scopes
access.allowedWhether the owner may manage keys right now
access.reasonplan (paid subscription), flag (api_access override) or none
expiresAtISO 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
Request
{ "name": "Catalogue sync", "scopes": ["read:tracks", "create:tracks"], "days": 365 }
FieldTypeNotes
namestringRequired
scopesstring[]Any of the assignable scopes. Omit for all of them
daysinteger ≥ 1Expiry. Omit for no expiry

Returns { "key": "k7Qw2ZpL.Xj9c…", "id": "k7Qw2ZpL" }. The key is shown only here.

Current key

GET /integration/api-keys

Returns the newest non-revoked key: id, name, scopes, webhookUrl, webhookActive, createdAt, lastUsedAt, expiresAt, and the raw key.

Scope catalog

GET /integration/api-keys/scopes

Returns { "scopes": [ … ] }, the assignable list.

Replace scopes

POST /integration/api-keys/{id}/scopes

Body { "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}/webhook

Body { "url": "https://…", "active": true }. See Webhooks.

Rotate

POST /integration/api-keys/{id}/rotate

Issues 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}/revoke

Permanent. Returns { "ok": true }.

Usage

GET /integration/usage?limit=50&cursor=…&from=2026-09-01&to=2026-09-07

Every request made with your keys: route, method, status, duration, IP and timestamp. Cursor-paginated with items and nextCursor.

On this page