CRX API
The relayer's HTTP API brokers quotes, prices margin, and serves the registries. It is the only off-chain service you call to quote and bind. The questions below cover the base URL, auth, every endpoint, the trade flow, errors, and limits.
What is the CRX API?
The relayer's HTTP API. It brokers requests for quote, prices margin, and serves the public maker and taker registries. It is the only off-chain service a client calls to quote and bind a trade. It never takes custody; the binding happens on-chain, between you and CRX.
What is the base URL?
The relayer base URL is read from NEXT_PUBLIC_RELAYER_URL.
| Environment | Base URL |
|---|---|
| Production | https://app.crxfx.com/svc/relayer |
| Local development | http://localhost:3001 |
The production relayer is served same-origin behind nginx (the /svc/relayer path-prefix on app.crxfx.com) so browser calls carry no CORS and need no Local Network Access prompt. All paths below are relative to the base. Try a call now:
curl https://app.crxfx.com/svc/relayer/health
# {"status":"ok"}Or run it here, edit the inputs, press Try It, and read the live response. Every endpoint in the API Reference has its own page with a panel like this:
GET /healthRelayer liveness. The simplest live call. No params, no token.
No parameters.
Bearer token · optional
curl -X GET "https://app.crxfx.com/svc/relayer/health"ResponsePress Try It! to run the call and read the live response.
How does authentication work?
Two POST calls: a challenge, then a login. The login returns a JWT valid for one hour. Some reads are public and need no token.
| Step | Method | Path | Body | Returns |
|---|---|---|---|---|
| 1. Challenge | POST | /auth/challenge | { "address": "0x…" } | { "message": "Sign in to CRX relayer.\nAddress: …\nNonce: …\nExpires: …" } |
| 2. Login | POST | /auth/login | { "address": "0x…", "signature": "0x…" } | { "token": "eyJ…", "expires_at": 1780313385 } |
signatureis apersonal_signover the exactmessagefrom step 1.- Send the token as
Authorization: Bearer <token>on every authenticated call. expires_atis a Unix timestamp (seconds). The token lives ~1 hour.
NoteThe trade path itself needs no session. Quoting and binding are authorized by the two EIP-712 Terms signatures, not by a JWT. Auth gates the maker's inbox and RFQ writes, not the bind.
Both steps are live in the reference: POST /auth/challenge and POST /auth/login.
What are the core endpoints?
The complete relayer set, each its own page in the API Reference with a live Try-It panel. auth = a Bearer token is required. write = the call changes live venue state.
| Method | Path | Auth | Write | Summary |
|---|---|---|---|---|
POST | /auth/challenge | no | no | Get the exact message your wallet must sign to log in. |
POST | /auth/login | no | no | Exchange a personal-sign signature for a 1-hour JWT. |
POST | /rfq | yes | yes | Open a directed RFQ as a taker. |
GET | /rfq/inbox | yes | no | Poll the directed RFQs waiting for your desk to price. |
POST | /rfq/:id/quote | yes | yes | Price an RFQ (unsigned). The relayer returns canonical Terms. |
POST | /rfq/:id/confirm | yes | yes | Confirm a priced RFQ with your EIP-712 signature over Terms. |
GET | /rfq/:id/bundle | yes | no | The dual-signed bundle the taker submits on-chain to bind. |
GET | /margin | no | no | Vol-scaled initial-margin schedule for a pairId and notional. |
GET | /firm/:addr | no | no | A firm's on-chain collateral summary. |
GET | /firm/:addr/profile | no | no | A firm's desk name and role, or null. |
GET | /firms | no | no | Every onboarded firm with its desk name and role. |
GET | /makers | no | no | The approved-maker registry. |
GET | /takers | no | no | The approved-taker registry. |
GET | /calendar/:pair | no | no | A pair's settlement calendar and on-chain pairId. |
GET | /health | no | no | Relayer liveness. |
What is the trade flow, end to end?
The order the endpoints are called in a single trade.
- Taker
POST /rfq→{ rfq_id }. - Maker
GET /rfq/inbox→ sees the RFQ. - Maker
POST /rfq/:id/quote→ relayer returns canonical Terms. - Maker
POST /rfq/:id/confirmwith an EIP-712 signature over Terms. - Taker
GET /rfq/:id/bundle→ the dual-signed bundle, once anchored. - Taker submits the bundle on-chain to bind. The position is live.
The same sequence, explained: RFQ Relayer (~3 min).
What do errors look like?
Every error is one JSON shape: { "error": "<message>" }, with the HTTP status carrying the class.
| Status | Meaning |
|---|---|
400 | bad request: malformed body or parameters |
401 / 403 | missing or insufficient auth on a token-gated read |
404 | not found: unknown RFQ id, firm, or pair |
409 | market closed: the pair is out of session |
429 | rate limited: back off and retry |
502 | upstream failure (chain or oracle read), retryable |
500 | internal: not retryable as-is |
What are the rate limits?
One limit exists: 100 RFQ submissions per firm per minute (60-second sliding window) on POST /rfq. Exceed it and you get 429. No other endpoint is limited.
No mandated poll interval. For reference: the relayer's own chain discovery runs every 30 s and margin quotes are cached for 30 s, so polling /margin faster than that buys nothing. Bundle polling after a quote is the exception: poll every second or so, since the anchor confirms in seconds.
Is there an SDK?
No published package yet: no npm, PyPI, or crates release. Until one ships, call this API directly with fetch (the examples above are the whole pattern) and read the chain over RPC with any EVM client (viem, ethers, web3.py, cast) against Base Sepolia (chain id 84532).
There is no installable CRX SDK. The closest thing to a typed client is the endpoint registry, frontend/lib/developers/endpoints.ts: the one description of every relayer route the browser calls (method, path, auth, write, fields, sample response). It drives the API explorer and the live crx-try panels, so it cannot drift from the running service. Read it as the contract.
On-chain read shapes: Positions & Settlement API (~3 min). Contract addresses: Live deployments (~1 min).