What is checked before a request reaches route-specific logic, what a client can verify itself before ever calling, and what the hub deliberately leaves unchecked.

What is checked, in order

  1. The gateway — the credential (x-api-key or x-project-id, one of them), the page’s origin for a project id, whether the route is open to that credential, the body’s size (64 KiB) and the gateway’s rate limits. Its refusals carry an EPX code and a real HTTP status: see Errors — gateway codes (not restated here).
  2. Body decode — malformed JSON never reaches the hub’s routes: its framework refuses it, EMC0028 reference Body. Well-formed JSON the form can’t take (a wrong JSON type such as 1.5 for an integer field) is EIN0001 reference body, and so is a body the framework never read (no JSON Content-Type) on a route that reads one.
  3. Form fields — validated in struct order; only the first failing field is reported. error_reference is the field’s JSON wire name (amount, sourceAsset, …), not an internal field name.
  4. Route-specific resolution — corridor/asset resolution and so on. The worked example is POST /quote’s own resolution order: see Request — not restated here.
  5. The rate-limit charge — validation passing is what triggers the hub’s own charge, before the quote pipeline or the order service does any work, so the quote, preparation and order lookups come after it. See Handling rate limits.

What you can verify before calling

What the hub does not check

Bodies are decoded leniently: unknown JSON fields are ignored, not refused. Malformed JSON is the framework’s EMC0028 reference Body; well-formed JSON of a wrong type is EIN0001 reference body; and only the first failing field is reported. What that implies for client-side validation:
  • EVM addresses are not format-checked at quote time. A non-EVM recipient/sourceAccount that doesn’t decode is EIN0001 on that field, but a malformed EVM address is admitted and fails later, not as EIN0001 (Get quote — Request).
  • On-chain id spelling is not validated. Uppercase hex or a missing 0x isn’t an error — it simply finds nothing, and on /fill that reads like “not filled yet” (known issue). See Get on-chain fill — Behavior.
  • chainDomain on fill-by-tx is not validated. An unknown or miscased domain finds no watermark and answers EIN0051, not EIN0001.

Framework refusals before the hub runs

These happen in the hub’s framework, before any route code:
  • Malformed JSON — EMC0028 reference Body.
  • A GET that carries a body with a JSON Content-Type — EMC0005. Without a JSON Content-Type the body is not read, and the GET is served.
  • A POST with an empty body and a Content-Type of exactly application/json — EMC0005. A body over the hub’s size limit reads as empty, so it is refused the same way. With a parameter (application/json; charset=utf-8) or no Content-Type, the empty body reaches the route, which answers EIN0001 reference body.
Every POST route takes a JSON object: never send an empty body.

Validating against the catalog

Build pickers from GET /catalog rather than a hand-kept table — a page can read it with your project id, so pickers render before your user signs in to anything, and it’s rebuilt only on restart. What native, nativeKey, swapRouter and wrappedKey mean for what a chain accepts as a source is Get catalog — Response — not restated here.

See also