Skip to page content
Docs
Make Agent Fast documentation

Scopes and errors

Grant least privilege and handle every stable public API error code.

At a glance

Grant least privilege and handle every stable public API error code.

API request pathScoped key → versioned resource → signed response
App / SDKBearer keymaf_live_…/api/v1/…ResourceJSON
33 scopesStable codesRequest IDs

Every public API operation requires one explicit scope. A valid credential without that scope receives 403 insufficient_scope; it is never silently upgraded to broader access.

Scope reference#

ScopeAllows
account:readRead the authenticated account, plan, entitlements, and wallet summary
tickets:readRead support tickets and their messages
tickets:writeUpdate tickets, assign teammates, and add internal notes or customer replies
team:readRead team members, pending invitations, and permitted audit events
team:writeCreate or revoke invitations and manage non-owner team roles
integrations:readInspect authorized GitHub App installations and repositories
integrations:writePrepare integration changes and create draft pull requests
sites:readList and retrieve sites
sites:writeCreate, update, publish, unpublish, and delete sites
agents:readRead agent settings
agents:writeUpdate agent behavior, language, voice, and embed settings
knowledge:readList knowledge sources and FAQs
knowledge:writeCreate, update, and delete knowledge sources and FAQs
conversations:readList conversations and retrieve their messages
conversations:writeTake a conversation over: reply to a visitor as the business, pause the agent on it, and hand it back
leads:readList captured leads
leads:writeCreate, update, qualify, and accept leads into the opportunity pipeline
pipeline:readRead stages, accounts, opportunities, activities, attribution, and CRM sync state
pipeline:writeConfigure stages; create or update opportunities and accounts; import or retry CRM outcomes
analytics:readRead site analytics events
connectors:readList connector state without returning secrets
connectors:writeCreate, update, enable, disable, and remove connectors
domains:readRemoved. Kept only for compatibility; every /domains route returns 409 because custom domains are not offered
domains:writeRemoved. Kept only for compatibility; every /domains route returns 409
broadcasts:readList broadcasts and delivery state
broadcasts:writeCreate drafts and queue a broadcast send
notifications:readList owner notifications
notifications:writeMark notifications read or unread
monetization:readList monetization products
monetization:writeCreate, update, activate, deactivate, and delete products
usage:readRead metered usage events
webhooks:readList webhook endpoints and delivery state
webhooks:writeCreate and delete webhook endpoints

Use read scopes for reporting jobs. Add a write scope only when the integration performs that mutation. For example, a lead export normally needs sites:read and leads:read, not sites:write or agents:write.

Scope failures#

{
  "error": {
    "code": "insufficient_scope",
    "message": "The API key does not grant the required scope.",
    "details": { "required": ["sites:write"] },
    "request_id": "6c0b2f2e-..."
  }
}

Add the missing scope to the key under Settings → Developer → Edit, or create a replacement key and update the server secret. Editing changes the key's name and scopes in place; its expiry and token stay the same. Keys issued through OAuth cannot be edited; reconnect the application to change what it was granted.

Error envelope#

All API errors use the same top-level shape:

type ApiError = {
  error: {
    code: string;
    message: string;
    details?: unknown;
    request_id: string;
  };
};

Branch on error.code, not the English message. Messages can improve without a version change. Log error.request_id and the x-request-id response header with the operation name, status, and retry attempt—but never log the authorization header or request secrets.

Status and code reference#

HTTPStable codeMeaningNormal response
400invalid_requestValidation failed or a field is unsupportedCorrect the fields in details; do not retry unchanged input
400invalid_jsonBody is not a JSON object or contains invalid JSONSerialize one valid JSON object
401authentication_requiredBearer header is missingAttach a server-side credential
401invalid_api_keyKey is invalid, expired, revoked, or its owner no longer existsReplace or rotate the key
403subscription_requiredThe owner does not have an active paid planRestore the subscription before retrying
403insufficient_scopeThe credential lacks the operation's scopeCreate a least-privilege replacement credential
403account_pausedAPI access is paused for the accountResume API access or contact the account owner
404not_foundEndpoint or owned resource was not foundVerify the path and tenant-owned ID
409conflictCurrent resource state prevents the actionRead current state before retrying
413payload_too_largeThe JSON body exceeds the 256 KB request capSend a smaller body; split large content into several requests
415invalid_requestMutation body is not application/jsonSend Content-Type: application/json
429rate_limit_exceededThe key's one-minute plan bucket is exhaustedWait for Retry-After and apply jitter
500internal_errorThe request could not be completedRetry a safe or idempotent operation and report the request ID
503outcome_unknownA mutation may have completed, but its result could not be recorded; details.retryable is falseDo not retry. Read the resource to see whether the change happened; the same idempotency key keeps returning this result

Resources outside the credential owner's account can return 404 instead of revealing whether another tenant's ID exists.

Validation details#

Field validation returns paths that can be shown next to your own form controls:

{
  "error": {
    "code": "invalid_request",
    "message": "Request validation failed.",
    "details": [
      { "path": "content.headline", "message": "String must contain at least 1 character(s)" }
    ],
    "request_id": "6c0b2f2e-..."
  }
}

Mutation bodies must be JSON objects. Arrays, empty bodies, form data, and JSON sent with a text content type are rejected.

Retry decisions#

  • Retry 429 after the number of seconds in Retry-After.
  • Retry 500 only for safe reads or writes protected by the same Idempotency-Key.
  • Retry the in-progress idempotency 409 after its Retry-After: 2 header.
  • Never retry 503 outcome_unknown blindly. Check the resource first; use a new idempotency key only after confirming the change did not happen.
  • Do not retry 400, 401, 403, 404, 413, or a state conflict until the cause changes.

See Rate limits for backoff behavior and API reference for the required scope on each operation.