Read the catalog, get a quote, sign it, submit it, then watch it fill — the gasless escrow lane (EVM, Permit2), the lane every other route page’s signPayload example is built from. Each step below pairs the exact curl from the linked route page with the exact JSON it answers. For the concepts behind each step, start at API reference and Concepts.

Before you start

Check these first:
  • You have a secret API key (c8n_sk_…), made for your project in the hub dashboard, in the HUB_API_KEY variable of your shell. Every call below sends it in x-api-key: there is no sign-in and no session (Authentication).
  • The paying wallet holds the source token (WETH here, at least amount) and has approved it to Permit2 (0x000000000022D473030F116dDEE9F6B43aC78BA3, the verifyingContract of Step 2’s signPayload). The hub checks neither: it quotes and forwards, and the solver then declines the signed order at Step 3 with EIN0021, reference http 422 (SOLVER_REJECTED). Nothing is opened or spent.
  • The quote names the paying wallet in sourceAccount. Without it, as in Step 2, the recipient is quoted as the payer: witness.user and the payload’s payer are the recipient. A different wallet can still sign; the order then opens on the signer’s tokens, but the recipient becomes the order’s user, which is also who a refund pays. Step 2 omits sourceAccount only because its signer is the recipient.
Base URL. https://proxy-api.infinimesh.net/v1: the public test hub (test networks only), which every call below uses; your production address comes with your integration access.
Keep the secret key off web pages. Run these calls from a terminal or your backend. In your app, a page calls either your backend’s proxy server, which holds the key and forwards its calls (Partner backend), or the gateway directly with your public project id, which opens every route but the order history (Authentication — project ids).
Every call sends one credential header, and a POST sends a JSON body with its content type (Conventions — request headers): Nothing else: the gateway sets the headers the hub itself needs. Each answer’s request_id (also in the X-Request-Id header) is the gateway’s id for the call; keep it for support. The hub requires the subject on the seven routes that read it, and keeps each subject’s quotes and orders apart: send the same one for a quote, its order and its status (Authentication — your users). How the ids tie together, step to step:

Steps

1

Configure: your key, then the catalog

Make a secret API key in the hub dashboard, and put it in your shell without leaving it in the history. It is the only credential these calls need.
GET /catalog — build pickers from this, never a hand-kept table (full reference):
2

Quote

POST /quote (full reference):
Amounts are base-unit decimal strings, never floats.
No executionTx or swapInput (this lane discloses neither); no auction (a firm quote, not a decaying one).
The deadlines inside the payloads (deadline, expires, orderExpiresAt, validUntil) fall in the year 2100 because this sample is built from a test fixture; the hub refuses an orderExpiresAt more than 24 h ahead. A live quote carries orderExpiresAt and witness.expires about 1 800 s after issuedAt. Neither extends the quote’s own expiresAt.
A winner discloses at most one of signPayload and executionTx — this walkthrough is the signed lane. For the proxy lanes, the open flow, and what each one hands you: Step execution.
The quote is valid for about 30 s and is never refreshed — sign promptly.
3

Execute: sign, then submit

Sign the disclosed signPayload with eth_signTypedData_v4, verbatim — never rebuild it — after checking it, then prefix the raw signature with the 0x00 scheme byte. With the SDK, executeQuote runs this whole step, checks included (SDK quickstart); its helpers do the parts:
Sign the signPayload object as disclosed; do not rebuild it from your own fields. EIP-712 hashes the typed values, so key order and whitespace change nothing, but a rebuilt object can drop or alter a field, and the solver then declines the signature (EIN0021). The 0x00 scheme byte in front of the 65-byte ECDSA signature is what makes 66 bytes.
POST /order/openfor (full reference):
state is the upstream’s initial state, not a fill guarantee.
Declined? EIN0021 means the solver refused the signed order and nothing was opened: re-quote and sign again. Never re-post the declined body — the repeat answers EIN0050 and then locks the quote on EIN0052.
Expired? EIN0005 on this call is normal flow — the quote’s ~30 s window passed; re-quote and re-sign, don’t retry the same signature.
Lost the answer? hubOrderId is derivable from quoteId alone (hex(sha256("infinity:v1:order:" + quoteId))) — read the status by that alias before re-posting. Handling errors. The gateway answers EPX0052 (504) when a submission takes longer than 10 s: that is a lost answer too, with an unknown outcome. The gateway keeps a submission running if your client disconnects, but cuts it at those 10 s, and a forward cut in flight leaves the quote answering EIN0052 until an operator reconciles it (known issue). Give this call a client timeout longer than 10 s, so that you get the gateway’s answer.
4

Monitor

GET /order/{orderId}/status — the upstream’s own view of the order (full reference):
Poll every 2–3 s; the view is cached for 2 s, so anything faster buys nothing. Stop at finalized, failed or refunded; settled is not terminal and can still move on. Don’t wait for one of them forever: the upstream can stop at executed while the order fills and settles on chain (known issue). Once it reports executed, follow the order on GET /onchain/{orderId}/fill; its filled and settled facts are the chain’s answer.
GET /onchain/tx/{chainDomain}/{txHash}/order — resolve the reported fill transaction into what the hub’s chain observers actually saw (full reference):
The upstream view above is advisory; this one is observed — show money to a user from here, not from Step 4’s first call. Its onchainOrderId is Step 3’s orderId: on this lane the ack already carries the on-chain id, and GET /orders lists it as onchainOrderId from submission. The Permit2 quote in Step 2 discloses none. Once the Open is observed, watermarks covers only the Open’s output chain, here eip155-56.
5

Optimize

No new call — three habits that keep you comfortably under the rate limit (Handling rate limits) and off the wallet-prompt-for-nothing path:

Status lifecycle

The three status views answer different questions and lag differently: GET /order/{orderId}/status is the upstream’s own advisory report; GET /status/{orderId} is the hub’s own event-derived projection (open-flow orders only have this one); GET /onchain/{orderId}/fill is what the hub’s chain observers actually saw, and is the only one of the three that proves anything. Poll the first by orderId (or the derivable hubOrderId) every 2–3 s; stop at finalized, failed or refunded, and from executed on read the third as well: the upstream can stop reporting there (known issue). Full detail, the id-to-view table, and the terminal states per view: Order lifecycle.

From a web page

The same flow runs in a browser with your public project id instead of the key: send x-project-id (and Content-Type on a POST) in place of x-api-key, from an origin your project lists. Every step works as above, the on-chain views included. Only the order history, GET /orders, needs the key: a page keeps the ids of the orders it submits, and reads the history through your backend (Authentication — project ids).

See also