Base URL: /api. All responses are JSON. Errors share the shape
{ error: string, code: string, details?: unknown }.
GET /api/healthLiveness probe.
{ "status": "ok", "service": "benzuko", "time": "<iso>" }GET /api/offersReturns P2P offers for a side.
Query: side = buy |
sell (default buy), validated by offersQuerySchema. |
{ "data": Offer[], "count": number }BAD_REQUEST — invalid side.RATE_LIMITED — includes Retry-After header.Offer shape: see types/index.ts.
curl "http://localhost:3000/api/offers?side=sell"
POST /api/tradesOpens an escrow-locked trade.
createTradeSchema:
{ side, asset, amount, price, currency, counterparty, method }.{ "data": Trade } with status: "locked".BAD_REQUEST — malformed JSON.VALIDATION_ERROR — includes details (Zod flatten).RATE_LIMITED.curl -X POST http://localhost:3000/api/trades \
-H 'Content-Type: application/json' \
-d '{"side":"buy","asset":"USDT","amount":500,"price":119.85,"currency":"BDT","counterparty":"Rahim T.","method":"Wise"}'
Fixed-window, in-memory (lib/rate-limit.ts), keyed by x-forwarded-for.
Defaults: 60 requests / 60s. Swap for Redis/Upstash in production.
| Code | HTTP | Meaning |
| — | — | — |
| BAD_REQUEST | 400 | Malformed input |
| VALIDATION_ERROR | 422 | Zod validation failed |
| RATE_LIMITED | 429 | Too many requests |