links.arjun.tv/docs/end-user/api-auth-and-error-model

API Auth and Error Model

Understand how authentication works across the 10x API, what errors you can expect, and how to handle them.

Auth modes

The API uses four authentication modes. The mode you need depends on the endpoint you are calling.

Auth modeHow to pass itRoute familiesWho uses it
JWTAuthorization: Bearer <jwt-token>/v2/handles/*, /v2/account/domains/*, /v2/billing/*Creators, operators, and owners managing their resources via the dashboard or scripts
PATAuthorization: Bearer patv1_<tokenId>.<secret>/v2/public/handles/{handle}/*Automation scripts, CI/CD pipelines, external integrations
VISITOR_COOKIEAutomatic (browser cookie)/v2/public/pages/*, /v2/visitor/*Visitors accessing paid pages, verifying purchases, managing entitlements
NONENo auth requiredSelected /v2/public/* endpointsPublic runtime: conversions, chain signals, context resolution

When to use JWT vs. PAT

Note

  • Use JWT for interactive sessions and control-plane operations (creating handles, managing collaborators, configuring domains).
  • Use PAT for automated operations on a specific handle (link management, analytics queries, webhook subscriptions).
  • PATs are scoped to a single handle. JWTs can access all handles the user owns or collaborates on.

Need concrete route-by-route guidance? See JWT vs PAT: When to Use Each.

JWT vs PAT in practice

QuestionJWTPAT
Can it call control-plane routes like /v2/handles/*?YesNo (unless an endpoint explicitly supports PAT)
Is it ideal for unattended CI/cron jobs?Not ideal (session token)Yes (scoped automation credential)
Can it create/revoke PATs?Yes (OPERATOR+)No
Is it bound to one handle?NoYes
Can it be scope-limited per integration?Role-based onlyYes, scope-based

Concrete examples

JWT: control-plane call

curl -sS "https://api.10x.in/v2/handles" \
  -H "Authorization: Bearer ${JWT_TOKEN}"

PAT: handle automation call

curl -sS "https://api.10x.in/v2/public/handles/${HANDLE}/links" \
  -H "Authorization: Bearer ${PAT_TOKEN}"

JWT required: create PAT

curl -sS -X POST "https://api.10x.in/v2/handles/${HANDLE}/tokens" \
  -H "Authorization: Bearer ${JWT_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"name":"ci-worker","scopes":["links.read","links.write"],"expiresInDays":30}'

JWT refresh behavior

POST /v2/auth/refresh uses the refresh cookie established at login. For browser clients, send requests with credentials: "include".

For CLI workflows, use a cookie jar:

curl -sS -c cookies.txt -X POST "https://api.10x.in/v2/auth/login" \
  -H "content-type: application/json" \
  -d '{"email":"you@example.com","password":"your-password"}'

curl -sS -b cookies.txt -c cookies.txt -X POST "https://api.10x.in/v2/auth/refresh" \
  -H "content-type: application/json"

Obtaining a JWT

Request

curl -sS -X POST "https://api.10x.in/v2/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com", "password": "your-password"}'

Body

{
  "email": "you@example.com",
  "password": "your-password"
}

Returns an access token. Include it in subsequent requests:

Authorization: Bearer <access-token>

JWTs expire after a set period. When you receive a 401 token_expired error, log in again to get a fresh token.

Obtaining a PAT

See API Tokens and Automations for creating and managing PATs.

Error model

Every error response follows a consistent structure:

{
  "error": {
    "code": "insufficient_scope",
    "message": "Token does not have the required scope: analytics.*",
    "statusCode": 403
  }
}

Error categories

HTTP statusError codesMeaningShould you retry?
400invalid_*, validation_errorRequest is malformed or missing required fieldsNo — fix the request
401missing_bearer_token, invalid_token, token_expiredAuthentication failedNo — fix your credentials or log in again
403insufficient_scope, insufficient_role, feature_lockedAuthorized but not permittedNo — upgrade scope, role, or plan
404not_foundResource does not existNo — check the ID or slug
409conflictResource already exists or state conflictNo — check current state
429rate_limitedToo many requestsYes — back off and retry
5xxVariousServer errorYes — retry with exponential backoff
Auth error quick reference
ErrorCauseFix
401 missing_bearer_tokenNo Authorization headerAdd the header with your JWT or PAT
401 invalid_tokenToken is malformed or the secret is wrongCheck the token format and value
401 token_expiredJWT has expiredLog in again to get a fresh token
403 insufficient_scopePAT does not have the required scopeCreate a new PAT with the needed scope
403 insufficient_roleUser does not have the required role on this handleAsk the handle owner to upgrade your role
403 feature_lockedFeature requires a higher plan tierUpgrade your plan (see Plans, Quotas, and Feature Gates)

Retry policy

Follow these rules for reliable integrations:

ScenarioAction
429 (rate limited)Wait for the time indicated in the Retry-After header, then retry
500, 502, 503, 504Retry with exponential backoff: 1s, 2s, 4s, 8s, 16s (max 5 attempts)
400, 401, 403, 404, 409Do not retry — these are deterministic errors that will not resolve on their own

Rate limits

The API enforces per-handle rate limits. Exact limits depend on your plan tier:

PlanRequests per minute
Free60
Pro300
Business1000

See Plans, Quotas, and Feature Gates for the full plan comparison.

Common integration patterns

Before creating a resource, check if it exists:

    curl -sS "https://api.10x.in/v2/handles/{handle}/links/{slug}" \
      -H "Authorization: Bearer <access-token>"

If you receive 404, proceed with creation. If you receive 200, decide whether to update or skip.

Related

JWT vs PAT

Decision guide with route boundaries and examples.

API Tokens

Create and manage PATs

Endpoint Coverage Map

Full endpoint reference

Error Codes

Common codes and handling guidance

Plans and Quotas

Rate limits and plan comparison

Troubleshooting

Debug common issues