Authentication
OAuth and organization API keys for MCP, Public API keys, and SMAO-issued Distributor API keys.
The two REST APIs use independent key types — a Public API key never works
against Distributor API v1 and vice versa. Both are sent the same way:
Authorization: Bearer <key>. Newly issued Public API keys are ~43-character
base64url strings with no prefix; legacy smao_-prefixed Public API keys remain
valid.
MCP Connector — OAuth or organization API key
For an interactive AI client, add https://api2.smao.ai/mcp as a remote MCP
connector and use OAuth. The client discovers SMAO's authorization server,
opens the browser login and consent flow, and binds the connection to the
organization you select. Disconnect and reconnect to switch organizations.
Clients that support custom HTTP headers can instead send a Public API
organization key as Authorization: Bearer <key>. Both methods apply the
current organization role and plan entitlements on every request. See the
MCP setup and access rules for client examples, scopes, and
revocation.
Public API — organization API keys
Create keys yourself in the dashboard under Settings → API Keys → New key. Each key gets a Role that scopes what it can do — pick the narrowest role that covers your use case. Keys belong to one organization; every request is scoped to that organization's data.
curl https://api2.smao.ai/api/v1/ping \
-H "Authorization: Bearer YOUR_API_KEY"A missing or invalid key returns 401:
{ "error": { "code": "unauthorized", "message": "Missing API key" } }A valid key whose role lacks permission for the endpoint returns 403 with
error.code: "forbidden". See Errors for the full envelope.
The raw key is shown once, at creation. Store it in a secret manager — it cannot be retrieved later.
Key handling:
- One key per integration. Separate keys let you revoke one consumer without breaking the others.
- Rotate by overlap. Create a new key, deploy it, then delete the old one. Both work in parallel in between, so rotation needs no downtime.
- Revoke immediately on leak. Deleting a key invalidates it at once.
Distributor API v1 — distributor keys
Distributor keys are issued by SMAO during onboarding — there is no self-service issuance. The raw key is shown exactly once at creation; the server stores a SHA-256 hash plus the first 12 characters as an identification prefix — never the full key.
Every v1 key carries these fixed permissions:
organizations:readorganizations:writeusage:readpartners:readpartners:write
Permission sets are immutable after issuance. Keys expire, by default after 365 days. Request a replacement key from SMAO before expiry and switch over — old and new keys work in parallel until the old one expires or is revoked, so rotation needs no downtime.
curl https://api2.smao.ai/api/distributor/v1/partners \
-H "Authorization: Bearer YOUR_DISTRIBUTOR_KEY"Auth failures return 401 in the Distributor error envelope, with an
errorCode telling you why:
{ "status": "error", "errorCode": "INVALID_API_KEY", "message": "Invalid or revoked API key." }errorCode | Meaning |
|---|---|
MISSING_API_KEY | No Authorization: Bearer header, or an empty key. |
INVALID_API_KEY | Key unknown or revoked. |
API_KEY_EXPIRED | Key past its expiry date — request a replacement from SMAO. |
API_VERSION_NOT_ALLOWED | Key metadata does not authorize Distributor API v1. |
Only GET /health is public. Every other Distributor endpoint requires a valid
v1 key and its documented permission. Continue with the
Distributor v1 quickstart.
REST API comparison
| Public API | Distributor API v1 | |
|---|---|---|
| Issuance | Self-service: dashboard Settings → API Keys | By SMAO during onboarding |
| Scoping | One organization | All organizations managed by the distributor |
| Permissions | Role-based, chosen per key | Five fixed v1 scopes |
| Expiry | None | 365 days by default |
| Rotation | Create new key, deploy, delete old | Request replacement from SMAO; parallel until old key expires or is revoked |
| Rate limits | Per key defaults: read 1000/min, write 200/min, POST /calls 60/hour | Per key: 60 req/min default (configurable per key) |
| 401 shape | { "error": { "code": "unauthorized", ... } } | { "status": "error", "errorCode": "...", "message": "..." } |
For the Public API, matched operations that pass authentication and the feature
gate return X-RateLimit-Limit, X-RateLimit-Remaining, and
X-RateLimit-Reset after the limiter succeeds. Auth failures, feature-gate
failures, unmatched routes, and limiter-storage failures do not carry the full
header set. Details in Errors.