Anyflo API
Anyflo API is the secure entry point for external requests to the Anyflo system. It authenticates clients, enforces permissions, and forwards requests to downstream services.
Request flow
A principal is a unique entity, such as a person or service, that the system can authenticate and authorize.
- The client sends a request with an
Authorization: Bearer anyflo_sk_<secret>header for customer access, or aCf-Access-Jwt-Assertionheader for admin access. - The gateway routes the credentials to the appropriate verifier and resolves the principal into the request context: a
customerIdand scopes, or an admin identity. - After authorization and ownership checks, downstream services receive the resolved identity over private service bindings. They trust the gateway and do not re-authenticate the request.
OpenAPI documentation
Use the API documentation to explore the current specification:
GET /v1/docs— Swagger UI for the OpenAPI specification.GET /v1/openapi.json— the OpenAPI JSON definition for documentation tools, code generation, machine-readable discovery, and AI-agent integration.
Content negotiation
The API supports CBOR and JSON. CBOR is the default and recommended format.
| Format | Request Content-Type | Response Accept |
|---|---|---|
| CBOR (default, recommended) | application/cbor | application/cboror omit |
| JSON | application/json | application/json |
Set Content-Type to match your request body.
To receive JSON responses, set Accept: application/json.
If you omit Accept or set it to application/cbor, responses use CBOR.
BigInt values in JSON
JSON has no native BigInt type, so BigInt values are serialized as decimal strings:
{
"amount": "1000000000000000000"
}
Send BigInt fields as numeric strings in JSON request bodies. CBOR supports BigInt natively.
Authentication
Customers authenticate with opaque API keys in the form anyflo_sk_<secret>:
Authorization: Bearer anyflo_sk_<secret>
The gateway hashes the key with BLAKE3-256 and verifies it through the Coordinator on every request, resolving the customer ID and the key’s scopes. Keys have individual scopes and can be revoked. Rotate a key by adding a replacement and revoking the old key.
Unknown, invalid, or revoked credentials return HTTP 401. A route-scope table enforces authorization and rejects access unless the required scopes are present. Ownership checks at the gateway prevent access to another customer’s resources.
Creating an order
senderIdmust be a valid UUID belonging to the authenticated customer.recipientIdis optional. When provided, it must be a valid UUID belonging to a registered user. When omitted, the order has no associated recipient ID, allowing transfers to unregistered users.
Rate limiting
API requests are rate-limited per actor using Cloudflare’s Rate Limiting binding. Limits count requests over a configured time window and are eventually consistent. Requests over the limit return HTTP 429.
| Tier | Rate limit | Counted by |
|---|---|---|
| Public | 100 requests per second | Client fingerprint (IP address and user agent) |
| Standard customer | 10,000 requests per 60 seconds | Customer ID ( customerId) |
The public rate is expressed as a per-second equivalent; the current gateway configuration enforces it over a 10-second window.
- Authenticated customers — requests are counted by
customerIdand use the customer’s configured tier. The default tier isstandard; unrecognized tier values fall back to it. - Unauthenticated requests — requests use the public tier and are counted by a client fingerprint based on IP address and user agent.
- Failed authentication — requests also count against the public tier, so invalid keys cannot bypass rate limiting.
- Admin requests — these are not currently rate-limited.
Rate-limit binding errors allow requests to continue. The deployment’s gateway configuration defines the request counts and time windows for each tier.
Local API examples
These examples require a gateway running locally on port 8792. Set ANYFLO_API_KEY to a valid API key for your local environment. Each example explicitly requests a JSON response.
Register a user
Use a key with the UsersCreate scope:
curl -X POST http://localhost:8792/v1/customer/register-user \
-H "Authorization: Bearer ${ANYFLO_API_KEY}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"externalUserId": "local-user"}'
Retrieve signed deposit instructions
Replace <order-id> with an accepted order’s ID:
curl "http://localhost:8792/v1/orders/<order-id>?instructions=true" \
-H "Authorization: Bearer ${ANYFLO_API_KEY}" \
-H "Accept: application/json"
Errors
Anyflo Pay returns standard HTTP status codes. The body of an error response includes a code, message, and optional field identifier.
{
"error": {
"code": "quote_expired",
"message": "The selected quote has expired. Request a new quote and retry.",
"field": null
}
}
WebSocket API
Solvers
Solvers connect to the Solver Hub’s /connect endpoint over WebSockets to receive order broadcasts and notifications. The connection is authenticated with a solver JWT.
Customer notifications
The customer app backend connects to the Gateway’s /v1/notification/ws-connect endpoint using a customer API key to receive order status updates.
This is a unidirectional channel: all messages are sent from the Anyflo server to the customer app backend server.
Instructions
- Customer app backend must initiate the WebSocket connection to get order status updates.
- The customer app should then forward the updates to user interface apps.
- Web apps MUST not create a WebSocket connection to the Anyflo server — this would expose the customer API key.
Notification messages
The Notification Hub sends binary messages encoded using CBOR. Decode each message to read its type and payload.
The current message type is OrderStatusChanged.
| Field | Description |
|---|---|
type | OrderStatusChanged |
payload.orderId | UUID of the order |
payload.status | Updated order status |
payload.userId | UUID of the notified customer’s user participating in the order as sender or recipient |
payload.timestamp | Time the notification was generated, as a Unix timestamp in milliseconds |
payload.metadata | Optional object containing additional information |
Subscriptions are scoped to the authenticated customer. A connection receives order status updates for that customer’s users, including orders where a user is the recipient.
Webhooks Coming soon
Subscribe to order lifecycle events without polling.
Webhooks will deliver signed JSON payloads to a URL you register on the dashboard. Each event will include the complete current order object and the event type.
Planned event types
order.quoted— a winning quote was selected.order.awaiting_deposit— sender funds expected at deposit address.order.settling— solver has fronted the destination payout.order.completed— destination payout confirmed.order.failed— order entered terminal failure state.
Signatures will use HMAC-SHA256 over the raw request body with your webhook secret. A timestamp header will provide replay protection.
