Authentication & Scopes
One API key per TrustPager workspace, with scope-based permissions and optional approval gating.
API key format
- Prefix: all keys start with
tp_live_ - Header:
Authorization: Bearer tp_live_... - Create or rotate: https://app.trustpager.com/settings/api
- Visibility: the full key is shown only once at creation. Lose it and you must create a new one.
Scopes
Scopes are stamped on the key at creation and follow you across both MCP and REST surfaces. Format: <resource>:<action>.
contacts:read,contacts:write,contacts:delete— and similar per resourceopportunities:read,opportunities:write,opportunities:deleteadmin— superscope; grants everything
Full list of per-resource scopes: see the OpenAPI spec under each endpoint's x-scopes.
Approval queue
Keys with -approval scope variants (e.g. contacts:write-approval) queue write operations for human review instead of executing
immediately. Use these when an AI agent should be able to PROPOSE changes that a human ratifies.
- Write operations return HTTP 202 with an
approval_idin the response body. - The action is queued, NOT executed. Do not retry.
- Check status:
GET /approvals/:id - Humans approve / reject at https://app.trustpager.com/settings/api?tab=approvals
You can also opt IN to review per request: add "require_approval": true to the body of any write, and it queues for
approval (HTTP 202 + approval_id) even when your key has permission to execute it directly. Useful when an agent wants a human
to confirm a specific action. Approving it later runs the action exactly as sent.
Pagination
List endpoints return a cursor-paginated envelope:
{ data, pagination: { has_more, next_cursor } }. Pass the next_cursor value back as the
?after= query parameter to fetch the next page. Default page size is 25, maximum 100 (?limit=).
Idempotency
Send an Idempotency-Key header on write requests (POST/PATCH/PUT/DELETE) to prevent duplicate records if a
request is retried. The same key replays the original response instead of executing the operation again.
Credit costs
Reads are free. Writes are billed; AI generation and sends (email, SMS, voice) are billed more. The MCP surface is ~10× cheaper per call than REST and is the recommended surface for interactive AI work — see Using AI.
Error responses on auth failure
401 and 403 responses are AI-friendly — they tell the caller exactly what to do:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="trustpager", error="invalid_token", error_description="Missing Authorization header"
Link: <https://docs.trustpager.com>; rel="help", <https://docs.trustpager.com/openapi.json>; rel="describedby"
Content-Type: application/json
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing Authorization header",
"details": {
"docs": "https://docs.trustpager.com",
"api_base_url": "https://api.trustpager.com/functions/v1/api/v1",
"auth_scheme": "Authorization: Bearer tp_live_...",
"fix": "Include a valid API key in the Authorization header..."
}
}
} Rate limits
Default: 60 requests per minute, 10,000 per day per key. Configurable per key. On limit hit you'll see HTTP 429 with a
Retry-After header and a RATE_LIMITED error code.
IP allowlist (optional)
You can restrict a key to specific IP addresses on the key creation form. Requests from other IPs return HTTP 403 with
error.code: "FORBIDDEN" and message "IP address not allowed". Leave the allowlist empty to allow any IP.
Error codes
Every error response carries a stable error.code alongside the HTTP status, a human message, and a
details.fix hint. Branch on the code, not the message text.
| HTTP | code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | A field is missing or invalid. Fix the request body and retry. |
| 400 | AMBIGUOUS_REFERENCE | A name was given where a UUID is required and it matches existing records. Retry with the UUID. |
| 401 | UNAUTHORIZED | Missing or invalid API key. |
| 402 | INSUFFICIENT_CREDITS | Not enough credits for this operation. Reads are free. |
| 403 | FORBIDDEN | The key lacks the required scope, or the caller IP is not allowed. |
| 404 | NOT_FOUND | The record does not exist, or belongs to a different workspace. This is a genuine miss, not a transient error. |
| 409 | CONFLICT | The write conflicts with an existing record (e.g. a value that must be unique is already in use). |
| 413 | SOURCE_TOO_LARGE | The payload or source content exceeds the allowed size. |
| 429 | RATE_LIMITED | Too many requests. Wait for the Retry-After header, then retry. |
| 500 | INTERNAL_ERROR | Unexpected server-side error. Safe to retry. |
| 503 | TEMPORARILY_UNAVAILABLE | A transient database or connection issue (timeout, dropped connection). This is not a problem with your request or IDs. Wait a moment and retry the exact same request; a Retry-After header is included. |