Skip to documentation

API authentication protocol

For direct API clients. CLI and MCP handle token exchange internally.

Connection parameters

App originLocal: https://localhost:3000 · Dev: https://dev.postede.com · Production contract: https://postede.com
OAuth issuer<app-origin>/api/auth
OAuth discovery<app-origin>/.well-known/oauth-authorization-server/api/auth
CLI resource<app-origin>/cli; dedicated registered CLI client
MCP resourceExact operator-registered MCP URL; separate client
API resource<app-origin>/api/agent/v1
FlowAuthorization code, S256 PKCE, exact registered callback, user consent
Public native client authnone; no client secret

Use the discovery response’s authorization and token endpoints. Requires operator-approved registration; dynamic registration is disabled. Match issuer, client and exact resource. Production acceptance is unverified.

Token exchange

POST <app-origin>/api/agent/v1/token-exchange

Content-Type: application/x-www-form-urlencoded · Body limit: 16,384 bytes.

grant_type · requiredurn:ietf:params:oauth:grant-type:token-exchange
subject_token_type · requiredurn:ietf:params:oauth:token-type:access_token
subject_token · requiredResource-bound OAuth access token
resource · requiredExact API resource in the selected environment
scope · optionalSpace-separated subset of granted Postede scopes
Token exchange request
curl -X POST "https://localhost:3000/api/agent/v1/token-exchange" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  --data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token" \
  --data-urlencode "subject_token=$POSTEDE_OAUTH_ACCESS_TOKEN" \
  --data-urlencode "resource=https://localhost:3000/api/agent/v1"

Response and API header

HTTP 200 · Cache-Control: no-store · Pragma: no-cache

Token exchange response
{
  "access_token": "API_ACCESS_TOKEN",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 60,
  "scope": "postede.profile:read postede.projects:read"
}

Scope reflects the grant or requested subset. Keep the API token in process memory; exchange again before its 60-second expiry. Live consent is validated on exchange; scopes and project access apply per API request.

API request header
Authorization: Bearer API_ACCESS_TOKEN

Exchange failures

OAuth error response
{"error":"invalid_token"}
HTTPErrorNext action
400invalid_requestCorrect content type, body size or required fields.
400invalid_targetUse the exact API resource in the same environment.
400invalid_scopeRequest a subset of valid granted scopes.
401invalid_tokenRefresh expired OAuth access; check issuer, audience and client binding. Reconnect if refresh fails.
401invalid_grantReconnect with user consent; the grant is absent or revoked.
429slow_downWait for Retry-After (60 seconds). Exchange limit: 240/minute per actor/client.
503temporarily_unavailableCheck environment registration/configuration or retry after recovery.

Do not retry invalid input unchanged or change environments to bypass a failure. Never include credentials in prompts, logs or support reports.

V-2026-10-05_17.35.45