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 inx-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
.envfile, 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:EPX0001when the header is missing or its value is not in that format (a stray space, a truncated copy, a project id),EPX0002when the key is unknown, revoked or expired (Errors — gateway codes). The SDK reads both asHubError.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, answersEPX0003(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-TypeandX-Infinity-Subject. A page sendsx-project-id,Content-Type: application/jsonon a POST, andX-Infinity-Subjectwhere the hub requires it; a browser refuses a call that sets any other non-standard header (standard ones such asAcceptneed no pre-flight). - Name the visitor. The seven routes that read
X-Infinity-Subjectrequire 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’sprojectTransportsendsvisitor-and a UUID, kept inlocalStorage. 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 oneEPX0009(401). Both count toward the lockout of the caller’s IP (EPX0081).
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 allowsx-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
createInteropProxywithproxyTransportin the page, Partner backend); - the gateway, with your public project id.
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 ofX-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 inX-Infinity-Subject:
- Seven routes require it:
POST /quote,/quote/preview,/quote/prepare,POST /order,/order/openfor,GET /order/{orderId}/statusandGET /orders. Without it they answerEIN0001(400), referencex-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 /orderslists the orders of the subject you send. Each page echoes that subject indata.subject: refuse a page whosesubjectis 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 withEIN0001, referencex-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’sverifySubjectScopingruns this check.
Your own records
Send the same subject on every call, one of your own such asdefault, 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
- API keys and rate limits — keeping keys safe, and the limits
- Conventions — the base URL, the headers and the envelope
- Errors — gateway codes — every
EPXcode - SDK: Partner backend — the secret key on your server; Browser apps — a page with your project id
- Quickstart