Conventions and permissions

In shortThe 10xGraph REST API response envelope, HTTP and WebSocket authentication, the endpoint permission table, WebSocket close codes and HTTP status codes.

  • 5 min read
  • 5 sections
  • Updated
  • v0.10.0
  • Markdown

Every route in the 10xGraph REST API lives under /v1/ (except /ping), returns the same JSON envelope, authenticates with a Bearer token, and is guarded by a resource and action permission pair. This page lists those shared rules, the full permission table, the WebSocket close codes and the HTTP status codes.

Base URL and response envelope

Successful responses wrap the payload in data and add a metadata object with request_id, timestamp and message. Errors raised by the application return error instead, with the same metadata. The details list is empty unless the error carries extra context (validation errors, for example).

JSON
{
  "data": {},
  "metadata": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-05-23T10:00:00",
    "message": "OK"
  }
}
JSON
{
  "error": {
    "code": "EXPIRED_TOKEN",
    "message": "Token has expired, please login again",
    "details": []
  },
  "metadata": {
    "request_id": "550e8400-e29b-41d4-a716-446655440000",
    "timestamp": "2026-05-23T10:00:00",
    "message": "Token has expired, please login again"
  }
}

Errors from HTTPException (permission denied, not found, empty upload) use the code HTTPException. Request validation failures use VALIDATION_ERROR. Three responses come from middleware and do not use this exact shape: 409, 413 and 429 (see HTTP status codes).

Authentication

When auth is configured in 10xgraph.json, every endpoint except /ping (and the public eval routes) needs a Bearer token. Send it as an Authorization header on HTTP routes. WebSocket clients that cannot set headers have two fallbacks, listed here in order of preference.

Text
Authorization: Bearer <token>
JavaScript
// Preferred for browsers: the token rides in a request header, not the URL
new WebSocket("ws://host/v1/graph/ws", ["10xgraph-bearer", token]);
Text
# Last-resort fallback (WebSocket only): the token lands in URLs and access logs
/v1/graph/ws?token=<token>

The server echoes the 10xgraph-bearer sentinel back on accept, which browsers require to complete the handshake. The older agentflow-bearer sentinel is accepted until 2.0. The ?token= query parameter is read only on WebSocket connections.

If auth is not configured ("auth": null), credentials are not required. Missing, invalid or expired credentials return HTTP 401. A permission failure returns 403. On WebSocket routes both become close code 1008. See authentication for setup.

Permission model

Each endpoint declares one (resource, action) pair. On every request the server calls authorize(user, resource, action) on the configured AuthorizationBackend. The built-in DefaultAuthorizationBackend allows any request whose user has a user_id, so write your own backend for real access control.

Permission reference

The table lists every (resource, action) pair the server enforces. POST /v1/ag-ui exists only when ag_ui.enabled is true in 10xgraph.json. The /v1/evals/* routes are mounted only when MODE is not production.

Endpoint Resource Action
POST /v1/graph/invoke graph invoke
POST /v1/graph/stream graph stream
POST /v1/ag-ui graph stream
WebSocket /v1/graph/ws graph stream
WebSocket /v1/graph/live graph stream
GET /v1/graph graph read
GET /v1/graph/tools graph read
GET /v1/observability/{thread_id} graph read
GET /v1/graph:StateSchema graph read
POST /v1/graph/stop graph stop
POST /v1/graph/fix graph fix
GET /v1/threads/{id}/state checkpointer read
PUT /v1/threads/{id}/state checkpointer write
DELETE /v1/threads/{id}/state checkpointer delete
GET /v1/threads/{id}/messages checkpointer read
GET /v1/threads/{id}/messages/{msg} checkpointer read
POST /v1/threads/{id}/messages checkpointer write
DELETE /v1/threads/{id}/messages/{msg} checkpointer delete
GET /v1/threads checkpointer read
GET /v1/threads/{id} checkpointer read
DELETE /v1/threads/{id} checkpointer delete
POST /v1/store/memories store write
POST /v1/store/search store read
POST /v1/store/memories/{id} store read
POST /v1/store/memories/list store read
PUT /v1/store/memories/{id} store write
DELETE /v1/store/memories/{id} store delete
POST /v1/store/memories/forget store delete
POST /v1/files/upload files upload
GET /v1/files/{id} files read
GET /v1/files/{id}/info files read
GET /v1/files/{id}/url files read
GET /v1/config/multimodal config read
GET /ping (none) (none)
GET /v1/evals/runs (none) (none)
GET /v1/evals/runs/{run_id} (none) (none)

The last three rows are the complete public allowlist. Every other route must carry a RequirePermission guard, and the server refuses to start if one does not.

WebSocket close codes

WebSocket routes report failures with close codes instead of HTTP statuses. 1008 also covers a graph that is on the wrong socket: a realtime graph on /v1/graph/ws, or a turn-based graph on /v1/graph/live.

Code Name When
1000 Normal closure The client disconnected cleanly
1008 Policy violation Authentication or authorization failed, or the graph type does not match the route
1011 Server error Unexpected server error
1013 Try again later Rate limit or connection cap exceeded at the handshake

HTTP status codes

These are the status codes the server returns. Routes document their own specific causes on their pages.

Code When
200 Success
400 Empty file upload, missing filename, or malformed Content-Length
401 Missing, invalid or expired credentials
403 Insufficient permissions, or access to a thread you do not own
404 File, thread, message or eval run not found
409 Another run updated the thread while yours was in flight. Body is {"error": "state_conflict", "detail": "...", "thread_id": ...}. Reload the thread and retry
413 Request body over MAX_REQUEST_SIZE (default 10 MB), or upload over MEDIA_MAX_SIZE_MB (default 25)
415 Uploaded content type not allowed
422 Request validation error (malformed body or invalid parameter)
429 Rate limit exceeded. Body has error.code RATE_LIMIT_EXCEEDED; the Retry-After and X-RateLimit-* headers say when to retry
500 Unexpected server error
503 Checkpointer or storage temporarily unavailable
Last updated for v0.10.0Edit this page on GitHubReport an issue