Skip to content

System Endpoints

Health Check

GET /health

Authentication: None

Returns service health plus the live executor_id and whether signature verification is ready.

{
  "status": "healthy",
  "version": "v1",
  "timestamp": "2026-04-15T08:00:00+00:00",
  "executor_id": 0,
  "relayer_executor_id": 0,
  "signature_ready": true
}

Notes:

  • status is healthy when signature verification is ready and the locally resolved executor_id matches the chain-side probe.
  • status is degraded when signature verification is not yet ready, or when executor_id and relayer_executor_id disagree (an active mismatch surfaces drift before any order is rejected).
  • relayer_executor_id may be null when the chain-side probe is temporarily unavailable. A null value alone is not a degradation signal; only an active disagreement with executor_id is.
  • Use executor_id when generating composite uuid_int values.
  • The route returns 503 if a backing dependency is unavailable.

Server Time

GET /system/time

Authentication: None

{
  "timestamp": 1713168000
}

Use this value to set expiration and deadline fields for signed payloads.

Token Registry

GET /tokens

Authentication: None

{
  "tokens": [
    {
      "currency": "USD",
      "symbol": "USDC",
      "address": "0xDcaEcdd8Db64f4316A11917Ad0162DEBD935285b",
      "decimals": 6,
      "min_trade_amount_raw": "8800000",
      "min_trade_amount": "8.800000"
    },
    {
      "currency": "EUR",
      "symbol": "EURC",
      "address": "0xd3BdB2CE9cD98566EFc2e2977448c40578371779",
      "decimals": 6,
      "min_trade_amount_raw": "0",
      "min_trade_amount": "0"
    }
  ]
}

The response shape is an object with a tokens array.

min_trade_amount_raw mirrors the on-chain minAmount stored per token on the Sera contract — the minimum from-token amount (in raw units) that a match will accept. A submission whose input falls below this floor is rejected pre-flight with HTTP 400 and detail.code = "AMOUNT_BELOW_MIN" on both /swap/quote and /orders. "0" means no floor. The decimal companion min_trade_amount is min_trade_amount_raw / 10^decimals.

Market Registry

GET /markets

Authentication: None

Returns the active markets with quote/base metadata.

{
  "markets": [
    {
      "symbol": "EURC/USDC",
      "base_symbol": "EURC",
      "quote_symbol": "USDC",
      "base_address": "0xd3BdB2CE9cD98566EFc2e2977448c40578371779",
      "quote_address": "0xDcaEcdd8Db64f4316A11917Ad0162DEBD935285b",
      "tick_precision": 4,
      "quantity_precision": 2,
      "base_decimals": 6,
      "quote_decimals": 6,
      "min_ask_amount_raw": "0",
      "min_ask_amount": "0",
      "min_bid_quote_amount_raw": "8800000",
      "min_bid_quote_amount": "8.800000"
    }
  ]
}

base_address and quote_address are the ERC-20 contract addresses that uniquely identify the pair. base_symbol / quote_symbol are the display tickers and match the symbol string. Pair orientation is canonicalised server-side so that base_address < quote_address bytewise (lowercased).

min_ask_amount_raw / min_bid_quote_amount_raw derive from the per-token on-chain minimum (see /tokensmin_trade_amount_raw):

  • ASK floor is the base-token minimum — price-independent. A sell order rejects if qty * 10^base_decimals < min_ask_amount_raw.
  • BID floor is surfaced on the quote side because the effective base minimum depends on price: min_base(P) = min_bid_quote_amount / P. A buy order rejects if qty * price * 10^quote_decimals < min_bid_quote_amount_raw.

Decimal companions (min_ask_amount, min_bid_quote_amount) are the raw values scaled by the respective token's decimals for display.

FX Rate

GET /fx/rate

Authentication: None

Aggregated FX rate for an ISO currency pair plus the day-over-day delta. The endpoint reads the same provider-quote pipeline that swap pricing uses (time-cluster + outlier filter + median across providers), without applying the swap-pricing post-aggregation freshness gate — the actual as_of timestamp is returned so the caller can judge freshness.

Query Parameters

Name Type Required Description
base string yes ISO currency code (e.g. USD). Case-insensitive, alphabetic only.
quote string yes ISO currency code (e.g. SGD). Case-insensitive, alphabetic only.

Response

{
  "pair": "USD/SGD",
  "rate": "1.2843",
  "as_of": 1777372800.12,
  "rate_24h_ago": "1.2901",
  "as_of_24h_ago": 1777286400.05,
  "change_pct": "-0.4496"
}
  • pair always reflects the requested orientation (base/quote), regardless of which direction the underlying provider data came from.
  • rate is the median across providers within the configured time-cluster
  • outlier-filter window.
  • as_of is the unix timestamp of the newest provider quote in the cluster.
  • rate_24h_ago, as_of_24h_ago, and change_pct are nullable when no quote was found within ±1h of T-24h. The endpoint still returns 200 in that case — coverage gaps are not a freshness failure.
  • change_pct is signed and rendered to 4 decimal places.

Auto-Inversion

When only the inverse pair has data at the current anchor (e.g. the caller asks for SGD/USD but the scraper only stores USD/SGD), the rate is computed as 1 / inverse_rate and quantised to 8 decimal places. The historic anchor is locked to the same direction — orientations are never mixed across the two anchors.

Same Currency

?base=USD&quote=USD returns rate=1, rate_24h_ago=1, change_pct=0 without querying the data store.

Status Codes

Code Meaning
200 Success — rate is always present; historic fields may be null.
400 base/quote missing or non-alphabetic.
503 No provider data for either direction within the current-anchor recency window (default 4h).

Permit Metadata

GET /permit/metadata

Authentication: API Key required

Query parameters:

  • token: ERC-20 token address
  • owner: wallet that may sign the permit
  • spender: allowance target, typically the live sor_address or vault_address from GET /config
  • amount (optional): raw uint256 amount to compare against current allowance

Example response for a supported token:

{
  "token": "0xDcAEcdd8Db64f4316A11917Ad0162DEBD935285b",
  "owner": "0x...",
  "spender": "0x...",
  "current_allowance_raw": "0",
  "required": true,
  "permit_supported": true,
  "nonce": 4,
  "domain": {
    "name": "USD Coin",
    "version": "2",
    "chainId": 11155111,
    "verifyingContract": "0xDcAEcdd8Db64f4316A11917Ad0162DEBD935285b"
  }
}

Unsupported tokens return permit_supported: false and omit nonce / domain. The optional required flag is only present when you supply amount.

Public Config

GET /config

Authentication: None

Returns the public bootstrap values needed for signing and transaction building.

{
  "chain_id": 11155111,
  "sera_address": "0xd0fc92d8eF9bE26D7288fCa1D6458f675e72B83a",
  "vault_address": "0x5d6ffA9b9710C7Ab69105496a0fD8C48DfF40110",
  "sor_address": "0xAfDb9f6071feD09941930246E78ce301DF7d1ace",
  "domain_separator": "0x...",
  "eip712_domain": {
    "name": "Sera",
    "version": "1",
    "chainId": 11155111,
    "verifyingContract": "0xd0fc92d8eF9bE26D7288fCa1D6458f675e72B83a"
  },
  "limits": {
    "vl_batch": { "min": 2, "max": 50 }
  }
}

This endpoint intentionally stays available even during partial startup. Missing values are returned as null; clients should retry rather than treating null fields as a hard failure.

limits carries public API caps that may vary per deployment. Read the active values at runtime via GET /config instead of hardcoding. Today this contains limits.vl_batch.{min,max} (VL batch placement size); future caps will live under additional sub-keys.

Verify Signature

POST /verify-signature

Authentication: None

Useful for testing EIP-712 order signatures before placing an order.

Request Body

{
  "owner_address": "0x...",
  "side": "bid",
  "amount": "1000.0",
  "price": "1.085",
  "from_address": "0x...",
  "to_address": "0x...",
  "order_id": "00000000-0000-4000-8000-000000000001",
  "uuid_int": "6427948336465191935941739505432058208337171677044006212075520",
  "signature": "0x...",
  "expiration": 1713254400
}

from_address is the market base token and to_address is the market quote token. expiration is required and must satisfy the same bounded future rule as POST /orders.

Response

{
  "valid": true,
  "recovered_address": "0x...",
  "expected_address": "0x...",
  "error": null
}