Skip to content

API Keys ​

Postmill uses per-user, per-organization API keys for programmatic access. All integrations and MCP clients authenticate with these keys.

Key format ​

Keys follow the format:

pm_live_<43 base62 characters>

Example: pm_live_ABC123...

Security model ​

  • Keys are SHA-256-hashed at rest (never stored in plaintext).
  • The plaintext is shown exactly once at creation and rotation.
  • Each key is owned by a specific user within a specific organization.
  • Authentication inherits the user's actual role in that organization.
  • Keys can have an optional expiry date (expiresAt).
  • Keys can be revoked (soft-delete via revokedAt).

Usage ​

Include the key in the Authorization header as a raw string (no Bearer prefix):

http
Authorization: pm_live_abc123...

This applies to:

  • Public API v1 (/public/v1/*)
  • MCP (/mcp, /mcp/:id, /sse/:id, /media-mcp)

The middleware resolves the key to the owning user and organization. When billing is enabled (at least one payment provider configured), the org must have an active subscription; otherwise the request returns 401.

Management endpoints ​

All endpoints under /user/api-keys are cookie-authenticated and org-scoped. Users can manage only their own keys within the current org.

MethodPathPurpose
GET/user/api-keysList my API keys for this org
POST/user/api-keysCreate a new API key
POST/user/api-keys/:id/rotateRotate an API key
DELETE/user/api-keys/:idRevoke an API key

Create a key ​

http
POST /user/api-keys
Content-Type: application/json

{
  "name": "n8n production",
  "expiresAt": "2027-01-01T00:00:00Z"
}

Response:

json
{
  "plaintext": "pm_live_xxxxxxxx...",
  "prefix": "pm_live_xxxx",
  "name": "n8n production"
}

expiresAt is optional and must be a valid ISO 8601 string.

Rotate a key ​

http
POST /user/api-keys/:id/rotate
Content-Type: application/json

{
  "name": "n8n production",
  "expiresAt": "2027-01-01T00:00:00Z"
}

Rotation revokes the old key and returns a new plaintext key with the same ownership.

Revoke a key ​

http
DELETE /user/api-keys/:id

Response:

json
{
  "success": true
}

Listing response ​

GET /user/api-keys returns the key metadata without the hash:

json
[
  {
    "id": "...",
    "name": "n8n production",
    "prefix": "pm_live_xxxx",
    "expiresAt": "2027-01-01T00:00:00Z",
    "revokedAt": null,
    "lastUsedAt": "2026-06-09T12:00:00Z",
    "createdAt": "2026-06-01T00:00:00Z"
  }
]

MCP scopes ​

When an API key is used with the MCP server, the resolved scopes depend on the user's org role:

  • owner, admin, or a super-admin with no membership in the target org (resolved to owner, start.mcp.ts:334-336): mcp:read, mcp:posts:write
  • editor, member, viewer: mcp:read

Admin-configured mcpsettings.allowedScopes is an allow-list ceiling, not a requirement: every request must hold only mcp:read, and any scope a token carries outside allowedScopes is stripped before per-tool scope checks (so a write tool still needs mcp:posts:write, and only when allowedScopes permits it). mcp:read is always within the ceiling; when allowedScopes is unset there is no ceiling.

Verified against v1.3.0

The AI-native social media management platform — postmill.ai