Files
routstr-core/docs/api/overview.md
Paperclip Deployment Engineerandthefux 9d0cd41e5a fix(upstream): report upstream 5xx as 424 + UPSTREAM_UNAVAILABLE, not node-down
CORE-UPSTREAM-5XX-NOT-NODE-DOWN

An upstream-attributable failure (provider 5xx, EHBP timeout, transport error
after a Cashu token was redeemed) was forwarded to the caller as the
provider's own 5xx. Callers read that as *this node* being down: they marked
the node unhealthy, dropped it from rotation, or refused to retry an upstream
blip that the node had already retried across candidates.

The node is healthy in those cases — it accepted the request, authenticated
it, reserved payment, tried every candidate, and reverted the reservation.
That is now visible from the response alone:

  status            424 (UPSTREAM_ERROR_STATUS)
  error.code        UPSTREAM_UNAVAILABLE
  header            X-Routstr-Error-Scope: upstream
  error.upstream_status / error.details.upstream_status
                    the provider's own status, preserved

Applied in routstr/upstream/base.py (forward_upstream_error_response and the
post-redemption x-cashu paths for both chat-completions and responses),
routstr/payment/helpers.py (create_error_response / create_upstream_error_response),
routstr/proxy.py (424 is retryable across candidates on the bearer, EHBP and
unauthenticated-GET loops), routstr/upstream/ehbp.py and
routstr/upstream/tinfoil.py (attestation host). New module
routstr/core/error_scope.py holds the contract constants and the mapping.

Deliberately unchanged:
  * rate limits keep 429 + UPSTREAM_RATE_LIMIT (including a rate limit wrapped
    in a provider 5xx envelope: status 429, code unchanged) — the retry hint is
    worth more than the status class;
  * provider-side 4xx passes through unchanged;
  * genuine node faults stay 500 and carry no scope header (UpstreamError gained
    scope=, set to "node" on internal-exception paths) so "node broken" is still
    distinguishable from "upstream broken".

The new status is exported through CORS (x-routstr-error-scope) so browser
clients can read the attribution.

Tests: 15 stale assertions of the old 5xx contract updated (renames keep their
intent: refund still happens, bodies still redacted, pinned requests still do
not fall back), plus new acceptance coverage for 424 + scope header +
upstream_status on the provider, bearer, x-cashu, /v1/messages, EHBP and
unauthenticated-GET paths, node faults staying 500 with no header, and failover
past a 424 to a healthy candidate returning 200.

Docs: docs/api/errors.md (status table, new "Upstream attribution" section,
upstream error examples, retry list now includes 424), docs/api/overview.md
and docs/api/endpoints.md.

Full unit suite: 1649 passed, 1 skipped.
2026-09-23 15:16:44 +00:00

7.9 KiB

API Reference Overview

Routstr Core provides a complete OpenAI-compatible API with additional endpoints for payment management. This reference covers all available endpoints, authentication methods, and response formats.

Base URL

https://api.routstr.com/v1

All API endpoints are prefixed with /v1 for versioning.

Authentication

Routstr uses API keys for authentication. Include your key in the Authorization header:

Authorization: Bearer sk-...

API Key Format

  • Prefix: sk-
  • Length: 32 characters
  • Example: sk-1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p

Cashu Tokens as Authentication

You can also use a Cashu eCash token directly in the Authorization header. The server hashes the token internally; this hash represents your API key identity and carries the token's balance.

Authorization: Bearer cashuAeyJ0b2tlbiI6W3...

Content Types

Request

  • Required: Content-Type: application/json
  • Encoding: UTF-8
  • Maximum Size: 10MB (configurable)

Response

  • Type: application/json or text/event-stream (for streaming)
  • Encoding: UTF-8
  • Compression: gzip (if accepted)

Rate Limiting

Rate limits are applied per API key:

  • Requests: 1000 per minute
  • Tokens: 1,000,000 per hour
  • Concurrent: 10 simultaneous requests

Rate limit headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200

Error Responses

All errors follow a consistent format:

{
  "error": {
    "type": "insufficient_balance",
    "message": "Insufficient balance for request",
    "code": "payment_required",
    "details": {
      "required": 154,
      "available": 100
    }
  }
}

Error Types

Type Status Code Description
invalid_request 400 Malformed request
authentication_failed 401 Invalid or missing API key
insufficient_balance 402 Not enough balance
forbidden 403 Access denied
not_found 404 Resource not found
rate_limit_exceeded 429 Too many requests
internal_error 500 Server error
upstream_error 424 Upstream API error — the provider failed, this node is healthy. Carries error.code = UPSTREAM_UNAVAILABLE, the X-Routstr-Error-Scope: upstream header, and the provider's own status in error.upstream_status. Rate limits stay 429 + UPSTREAM_RATE_LIMIT. See Error Handling

Endpoint Categories

AI/ML Endpoints

Standard OpenAI-compatible endpoints:

  • Models: /v1/models
  • Model paths: /v1/models/paths, /v1/models/paths/model?model_id=...
  • Responses: /v1/responses
  • Chat Completions: /v1/chat/completions
  • Embeddings: /v1/embeddings
  • System One: /v1/systemone (TypeSafe decision models)
  • Completions: /v1/completions (planned)
  • Images: /v1/images/generations (planned)
  • Audio: /v1/audio/transcriptions (planned)

Payment Endpoints

Routstr-specific payment management:

  • Balance: /v1/balance/*
  • Node Info: /v1/info

Admin Endpoints

Protected administrative functions:

  • Dashboard: /admin/
  • API Management: /admin/api/*

Request Headers

Standard Headers

Header Required Description
Authorization Yes Bearer token with API key
Content-Type Yes Must be application/json
Accept No Response format preference
Accept-Encoding No Compression support
X-Request-ID No Client-provided request ID

Custom Headers

Header Description
X-Cashu eCash token for per-request payment

X-Cashu: Stateless Per-Request Payment

Instead of using Authorization: Bearer sk-..., you can send a Cashu token directly in the X-Cashu header. The response will include an X-Cashu-Refund header with your change.

curl https://api.routstr.com/v1/chat/completions \
  -H "X-Cashu: cashuA3s8jKx9..." \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'

The response includes your change in the same header:

X-Cashu: cashuA7k2mNp4...

This is fully stateless—no session, no /v1/balance/refund call needed. However, streaming does not work with X-Cashu because the refund can only be calculated after the full response is generated. If you lose the X-Cashu response header before claiming your change, you can reclaim the refund via POST /v1/wallet/refund by supplying the original payment token in the x-cashu header.

Response Headers

Standard Headers

Header Description
Content-Type Response format
Content-Length Response size
X-Request-ID Unique request identifier
X-Cashu Change token (when request used X-Cashu header)

Streaming Responses

For endpoints supporting streaming, responses use Server-Sent Events:

data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}

data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1694268190,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"content":" there"},"finish_reason":null}]}

data: [DONE]

OpenAPI Specification

The complete OpenAPI 3.0 specification is available at:

GET /openapi.json

Interactive documentation:

GET /docs        # Swagger UI
GET /redoc       # ReDoc

SDK Support

Routstr is compatible with official OpenAI SDKs:

Python

from openai import OpenAI

client = OpenAI(
    api_key="sk-...",
    base_url="https://your-node.com/v1"
)

JavaScript/TypeScript

import OpenAI from 'openai';

const openai = new OpenAI({
    apiKey: 'sk-...',
    baseURL: 'https://your-node.com/v1'
});

cURL

curl https://your-node.com/v1/chat/completions \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"Hello"}]}'

Webhook Support

Configure webhooks for events:

POST /v1/webhooks
{
  "url": "https://your-app.com/webhook",
  "events": ["balance.low", "key.expired"],
  "secret": "whsec_your_secret"
}

Events are sent with signature verification:

X-Webhook-Signature: sha256=...

API Versioning

  • Current version: v1
  • Version in URL path: /v1/endpoint

Status Codes

Code Meaning
200 Success
201 Created
204 No content
400 Bad request
401 Unauthorized
402 Payment required
403 Forbidden
404 Not found
424 Upstream provider failed (X-Routstr-Error-Scope: upstream)
429 Rate limited
500 Server error (no scope header)
502 Gateway failure
503 Service unavailable

CORS Support

CORS is enabled with configurable origins:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400

Compression

Responses are compressed with gzip when:

  • Client sends Accept-Encoding: gzip
  • Response is larger than 1KB
  • Content type is compressible

Batch Requests (planned)

Process multiple operations in one request. Coming soon.

Node Info

Get node metadata:

GET /v1/info

Supported models and pricing are available at /v1/models. Upstream provider path discovery is available at /v1/models/paths and /v1/models/paths/model?model_id=....

Next Steps