Skip to main content
API Preview
Developers

Error Reference

The API uses standard HTTP status codes. Error responses include a JSON body with a detail field describing the error. Some endpoints include structured error data for programmatic handling.

Error Response Format

All error responses return a JSON body with a single error object carrying a machine-readable code, a human-readable message, a broad category, help text, and the request ID for support correlation.

json
{
  "error": {
    "code": "MACHINE_READABLE_CODE",
    "message": "Human-readable error description",
    "category": "auth",
    "help": "What to do about it.",
    "request_id": "9f8e7d6c-...",
    "details": {}
  }
}

Error Fields

NameTypeDescription
error.code*stringMachine-readable error code for programmatic handling (e.g., "UNAUTHORIZED", "VALIDATION_ERROR", "RATE_LIMIT_EXCEEDED").
error.message*stringHuman-readable description of what went wrong.
error.category*stringBroad classification: auth, validation, rate_limit, permission, not_found, conflict, server, network, or config.
error.help*stringActionable guidance for resolving the error.
error.request_idstringRequest ID for support correlation. May be null.
error.details*objectStructured detail specific to the error (e.g. field-level validation errors). Empty object when not applicable.

HTTP Status Codes

The ~alter API uses standard HTTP status codes to indicate success or failure. Successful requests return 2xx codes. Client errors return 4xx codes. Server errors return 5xx codes.

StatusCodeDescription
400Bad RequestInvalid request body, parameters, or business rule violation
401UnauthorizedMissing, expired, or invalid authentication credentials
402Payment Requiredx402 payment required for a paid tool or query
403ForbiddenAuthenticated but insufficient permissions or consent not granted
404Not FoundResource does not exist or is not accessible
409ConflictResource already exists (duplicate email, already claimed stub, etc.)
422Unprocessable EntityRequest body validation failed
429Too Many RequestsRate limit exceeded - check Retry-After header
500Internal Server ErrorUnexpected server error - contact support
503Service UnavailableDependency unavailable (database, Redis, signing keys)

Common Error Examples

Validation error (422)

Returned when the request body fails Pydantic validation. The error.details.errors array carries one entry per failing field.

json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "category": "validation",
    "help": "Check the highlighted fields and correct any errors.",
    "request_id": "9f8e7d6c-...",
    "details": {
      "errors": [
        {
          "field": "body.email",
          "message": "value is not a valid email address",
          "type": "value_error.email"
        }
      ]
    }
  }
}

Authentication error (401)

json
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Could not validate credentials",
    "category": "auth",
    "help": "You need to sign in to access this.",
    "request_id": "9f8e7d6c-...",
    "details": {}
  }
}

Permission error (403)

json
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Consent not granted for trait profile access",
    "category": "permission",
    "help": "You don't have permission for this action.",
    "request_id": "9f8e7d6c-...",
    "details": {}
  }
}

Conflict error (409)

json
{
  "error": {
    "code": "RESOURCE_CONFLICT",
    "message": "An account with this email already exists",
    "category": "conflict",
    "help": "This record already exists. If you believe this is wrong, contact support.",
    "request_id": "9f8e7d6c-...",
    "details": {}
  }
}

Rate Limiting

Rate limiting uses a Redis-backed sliding window counter and is applied per authenticated role, not by a free/pro tier split. A member or org admin gets 120 requests/minute and 5,000/day; org viewers and auditors get 60/minute and 2,000/day; unauthenticated requests run in the lowest bucket at 40/minute and 20,000/day. When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header.

Rate Limit Headers

NameTypeDescription
X-RateLimit-LimitstringMaximum requests allowed in the window
X-RateLimit-RemainingstringRequests remaining in the current window
X-RateLimit-ResetstringUnix timestamp when the window resets
Retry-AfterstringSeconds until the next request is allowed (only on 429)

Rate limit exceeded response (429)

bash
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1709856000
Retry-After: 42
Content-Type: application/json

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Try again in 42 seconds.",
    "category": "rate_limit",
    "help": "You're making requests too quickly. Wait a moment and try again.",
    "request_id": "9f8e7d6c-...",
    "details": {}
  }
}

Respect the Retry-After header and implement exponential back-off for retries.

402 Payment Required

Paid MCP tools and credential queries return 402 Payment Required when invoked without a valid x402 payment proof. The response includes pricing metadata so the client can construct a payment.

json
{
  "detail": "Payment required for paid tool access",
  "code": "PAYMENT_REQUIRED",
  "pricing": {
    "amount": "0.30",
    "currency": "USD",
    "protocol": "x402",
    "recipient": "alter_treasury",
    "payment_url": "https://mcp.truealter.com/api/v1/payments/create",
    "tool": "query_trait_profile",
    "description": "Trait profile query - 75% accrues to the data subject; indicative only, server-authoritative at settlement"
  }
}

Free allowance: Each identity record carries a one-time free allowance, sized to its engagement level. A free read still earns the queried member recognition, a settled $0 signal toward how legible they are, but not Identity Income. Once the allowance is spent, x402 payment is required, and only paid reads earn income.

JSON-RPC Error Codes (MCP)

The MCP Identity Server uses JSON-RPC 2.0 for communication. Errors follow the standard JSON-RPC error format with ~alter-specific application codes in the -32000 to -32002 range.

CodeDescription
-32700Parse error - invalid JSON
-32600Invalid request - missing required fields
-32601Method not found - unknown MCP method
-32602Invalid params - tool argument validation failed
-32603Internal error - server-side failure
-32000Payment required - x402 payment needed for a paid tool
-32001Rate limit exceeded
-32002Authentication required

JSON-RPC error response format

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32000,
    "message": "Payment required for paid tool access",
    "data": {
      "tool": "query_trait_profile",
      "pricing": {
        "amount": "0.30",
        "currency": "USD",
        "protocol": "x402"
      }
    }
  }
}

Error Handling Best Practices

Always check the status code

Do not assume a 200 response. Check for 2xx before parsing the response body as a success object.

Implement token refresh proactively

Rather than waiting for a 401 response, decode the JWT expiry and refresh the token before it expires. See Token Refresh for the rotation flow. This avoids failed requests in flight.

Respect rate limit headers

Read X-RateLimit-Remaining on every response. If approaching zero, slow down before hitting the 429 limit.

Handle 402 gracefully for MCP

If building an MCP client, parse the pricing metadata from payment-required errors and present payment options to the user or agent. See the x402 example flow for the complete negotiation sequence.

Retry on 503 with back-off

Service unavailable errors are typically transient. Retry with exponential back-off (1s, 2s, 4s) up to 3 attempts.

Docs | ~alter