CRX API
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 two firms.
What is the base URL?
The relayer base URL is read from NEXT_PUBLIC_RELAYER_URL. It is environment-specific; there is no single hard-coded host in the source.
The production relayer host is not published in this repo. Local development defaults to http://localhost:3001. All paths below are relative to that base.
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.
The 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.
What are the core endpoints?
The complete relayer set. 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 for a pair and notional. |
GET | /firm/:addr | no | no | A firm's general balance and open agreement count. |
GET | /makers | no | no | The approved-maker registry. |
GET | /takers | no | no | The approved-taker registry. |
GET | /health | no | no | Relayer liveness. |
What are the request and response shapes?
Each endpoint, with its parameters and what it returns.
POST /rfq
Open a directed request for quote. Auth required.
- Body:
{ "pair": "USD/INR", "maker": "0x…", "notional": "100000", "side": "buy" } pair— a currency pair label (see Pairs & Marks API, ~3 min).maker— the maker address you are directing the RFQ to.notional— notional in quote units, as a string.side—"buy"or"sell".- Returns:
{ "id": "rfq_…", "status": "open" }.
GET /rfq/inbox
Poll the RFQs aimed at your desk. Auth required.
- Query:
maker(required) — your maker address. - Returns: an array of open RFQs directed at you.
POST /rfq/:id/quote
Price an RFQ. The relayer returns the canonical Terms to sign. Auth required.
- Path:
id— the RFQ id from your inbox. - Body:
{ "rate": "83.20", "valid_until": 1780313385 }. valid_until— Unix timestamp; the quote expires after it.- Returns:
{ "terms": { … } }— the canonical Terms struct to sign in the next step.
POST /rfq/:id/confirm
Confirm a priced RFQ with your signature. Auth required.
- Path:
id— the RFQ id you priced. - Body:
{ "signature": "0x…" }— an EIP-712 signature over the returned Terms. - Returns:
{ "status": "confirmed" }.
GET /rfq/:id/bundle
Fetch the dual-signed bundle the taker submits on-chain to bind. Auth required.
- Path:
id— a confirmed RFQ id. - Returns:
{ "bundle": { … } }.
A bundle is not ready until the agreed quote is anchored on-chain. Anchoring takes a few seconds. A client that polls and gives up too early reads an empty bundle and wrongly concludes no maker answered.
GET /margin
Estimate the initial margin for a pair and notional. Public.
- Query:
pair(required),notional(required, quote units). - Returns:
{ "im": "4200.00" }.
GET /firm/:addr
A firm's off-chain summary. Public.
- Path:
addr— a firm wallet address. - Returns:
{ "balance": "0", "open_acas": 0 }— general balance and open master-agreement count.
GET /makers, GET /takers
The approved registries. Public.
- No parameters.
- Returns: an array of
{ "address": "0x…" }. A maker entry may carry anonlineflag (last poll under 30s).
GET /health
Relayer liveness. Public.
- No parameters.
- Returns:
{ "status": "ok" }.
What is the trade flow, end to end?
The order the endpoints are called in a single trade.
- Taker
POST /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).
Next: Pairs & Marks API (~3 min) — read supported pairs, sessions, and live marks.