The API is served by the hub’s gateway, under https://proxy-api.infinimesh.net/v1 on the public test hub. Every call carries one credential header, and nothing else is needed to authenticate: there is no sign-in, no session and no token to renew.
Getting access. Integration access to the Infinity hub is by request: email [email protected]. Your organization, its project and its keys are then managed in the hub dashboard: proxy-admin.infinimesh.net for the test network. The API itself has no sign-up and no key routes.

The two credentials

Send exactly one of them on every call. A request carrying both is refused with EPX0007 (400). Both belong to your organization, and the gateway names your organization to the hub on every call, from the credential: an order placed with one is the same order to the other. On the seven routes that read it, the hub also requires the X-Infinity-Subject header: which of your users, or which visitor of a page, the call is for (Your users).

Secret API keys

A secret key is made in the hub dashboard. It goes in x-api-key on every call, and opens every route: quotes, orders, status, the order history, the on-chain views and refunds.
  • It stays on your servers. Whoever holds it acts as your organization: they can quote and submit orders in its name and read its whole order history. Keep it in a secret store or a git-ignored .env file, never in a page, an app bundle or a repository.
  • Its format is c8n_sk_ followed by exactly 43 base64url characters (letters, digits, - and _).
  • A refused key is answered 401: EPX0001 when the header is missing or its value is not in that format (a stray space, a truncated copy, a project id), EPX0002 when the key is unknown, revoked or expired (Errors — gateway codes). The SDK reads both as HubError.isCredentialRefused: an operator matter, which no retry fixes.
  • Stop on a refused key. 20 refused credentials within a minute from one IP make the gateway answer EPX0081 (429) to every call from that IP for the rest of that minute, valid keys included: a server that keeps retrying a wrong key locks itself out (API keys and rate limits).
  • Never from a web page. The gateway does not take a key from a browser (No keys in web pages).
  • Rotating is done in the hub dashboard: make the new key, move your servers to it, then revoke the old one. More on keys and their rate limits: API keys and rate limits.

Project ids for web pages

The project id is the credential a web page uses to call the gateway directly, with no server of yours in between. It is public by design: shipping it in a browser bundle is safe. Each organization has one, shown on its project page in the hub dashboard, and it does not expire.
  • Every route but the order history is open. The catalog, the quotes, order submission, both status views, the on-chain views and the Solana refund route answer a project id as they answer a key, so a page can follow the orders it submits to the end, and refund them itself. The order history, GET /orders, answers EPX0003 (404): it would show every visitor all of your orders. Keep the ids of the orders a page submits; the history needs your server and its secret key.
  • Set only the headers the pre-flight allows. The gateway’s CORS pre-flight allows exactly three request headers: x-project-id, Content-Type and X-Infinity-Subject. A page sends x-project-id, Content-Type: application/json on a POST, and X-Infinity-Subject where the hub requires it; a browser refuses a call that sets any other non-standard header (standard ones such as Accept need no pre-flight).
  • Name the visitor. The seven routes that read X-Infinity-Subject require it from a page too. Send a random id per visitor, kept in the browser, so each visitor’s quotes and statuses stay apart: the SDK’s projectTransport sends visitor- and a UUID, kept in localStorage. It labels a visitor and proves nothing (Your users).
  • Order reads are not private. Anyone holding your project id and one of your order ids can read that order’s status and its chain facts. Nothing lists the ids, so a page learns only the ones it submitted or was given. (The on-chain views and the refund route are not scoped to your organization in the first place: chain data is public, and a refund pays only the order’s own user.)
  • Rate limits apply per visitor IP, plus a pool shared by every visitor of your project (API keys and rate limits).
  • A malformed project id is EPX0008 (401), an unknown one EPX0009 (401). Both count toward the lockout of the caller’s IP (EPX0081).
With the SDK, a page uses projectTransport (Browser apps).

Origin lists

Each project has a list of the web origins allowed to use its project id. It is empty by default: A refused origin is EPX0010 (403). That answer carries the CORS headers, so a page can read the error instead of seeing an opaque CORS failure. Each entry is an exact origin: scheme, host and port, with no wildcards. http origins are accepted, so list http://localhost:3000 next to your production origin to develop locally. Only project ids have origin lists. A secret key has none: no web page can use one (next section).

No keys in web pages

A secret key in a web page is readable by every script on that page: yours, your dependencies’ and any injected one. The gateway does not take one from a browser, on any network: its CORS pre-flight never allows x-api-key, and its answers to key calls carry no CORS headers, so the browser refuses the call. A page talks to one of these:
  • your backend, which holds the secret key and forwards the page’s calls (a proxy server: the SDK’s createInteropProxy with proxyTransport in the page, Partner backend);
  • the gateway, with your public project id.
The SDK’s hubTransport refuses to start in a web page. Its dangerouslyAllowBrowser: true option lifts that for a test key on a hub that accepts browser calls; the hub’s gateway does not.

Headers

Nothing else is needed. The gateway builds every hub request itself: RequestId, User-Agent, Accept-Language: en (so the hub’s messages are always in English), your organization (X-Infinity-Organization, from the credential) and its own credential for the hub. Nothing else you send reaches the hub but the path, the body, X-Infinity-Subject, and the query string of GET /orders and GET /onchain/{orderId}/refund. Every answer names the call in the X-Request-Id header and in the body’s request_id: quote it when you contact the hub team. The full header rules: Conventions — request headers.

Your users

The credential tells the hub your organization, never which of your users a call is for. That is the job of X-Infinity-Subject, which the hub requires on the seven routes that read it. If your service has users of its own, keep them apart one of two ways: name each user to the hub in the subject, and the hub keeps them apart itself; or send one subject for everyone, and keep your own record of who created each quote and order.

The subject: the hub keeps them apart

From your server, with your secret key, name the user a call is for in X-Infinity-Subject:
  • Seven routes require it: POST /quote, /quote/preview, /quote/prepare, POST /order, /order/openfor, GET /order/{orderId}/status and GET /orders. Without it they answer EIN0001 (400), reference x-infinity-subject. The hub keeps each subject’s quotes, orders and history apart, so send the same subject for a quote, its order and its status reads.
  • GET /orders lists the orders of the subject you send. Each page echoes that subject in data.subject: refuse a page whose subject is not the one you sent.
  • The other routes do not read it. The catalog and the on-chain views are scoped to no one, and the hub does not scope GET /status/{orderId} by subject: a backend that serves several users answers that route for the user’s own orders only.
  • The gateway forwards it unchanged, every value you send, and never adds one. It sets your organization itself, from the credential, so a subject can never reach another organization’s orders.
  • Choose it as your stable id for the person: the same across sign-ins and wallets, never reused for someone else, one value per call, of 1 to 128 letters, digits and . _ @ + ~ - (^[A-Za-z0-9._@+~-]{1,128}$). The hub refuses any other value with EIN0001, reference x-infinity-subject.
  • Check that it works before you serve history: list orders for a subject none of your users has, such as canary- followed by a fresh UUID. The answer must hold no orders and echo that subject; if it does not, serve no history and tell the hub team. The SDK’s verifySubjectScoping runs this check.
Never trust a subject from a web page. A page must send one on the seven routes, and the gateway forwards it, but the project id is public: anyone can send it with any subject and pass for any visitor, or any of your users. From a page, a subject keeps visitors apart and proves nothing (the SDK’s projectTransport sends a random id per visitor). Scope calls to a signed-in user from your server only.

Your own records

Send the same subject on every call, one of your own such as default, and the hub keeps one history for all your users: GET /orders with that subject lists every order, whoever placed it, and the status reads answer for any of them. Keep your users apart yourself: record who created each quote and order, submit a quote only for the user it was made for, answer status reads for the user’s own orders only, and build each user’s history from that shared listing. The SDK’s createInteropProxy does either for a partner backend (userScope: "subject", or "ownership", its default, which sends hubTransport’s one subject, default unless you set another), and answers GET /status/{orderId} for the user’s own orders only either way (Partner backend — keeping your users apart).

See also