# Conventions

> Base URL, amounts, addresses, request ids and error bodies, shared by every endpoint.

## Base URL and paths

Every endpoint is under `https://api.hyperstream.dev/v2/`. The `/v2` prefix is a fixed part of the path, not a version you can change or leave out. The same routes without it return a plain-text `404 Not Found`.

The rest of these docs write paths as `/v2/…`.

## Requests and responses

- Bodies are JSON. Send `Content-Type: application/json` on `POST` requests.
- Quotes and builds are single-use answers. Do not cache them.
- Every response carries an `x-request-id` header (a UUID). Log it. When you contact Khalani about a request, include it with the `orderId` and the time of the call.

## Amounts

Every amount is a decimal string of integer base units, never a JSON number. On a 6-decimal token, `"1000000"` is 1.0. Request amounts must match `^[1-9]\d*$`.

Each amount is in the base units of one token on one chain, and decimals can differ between chains for the same token. Read `decimals` from `GET /v2/params` rather than assuming.

| Fields | Denominated in |
|---|---|
| `spend.amount`, `amount`, `srcAmount`, `minSrcAmount`, `maxSrcAmount`, `srcAmountSpoke`, `protocolFee`, `fullFee`, `refundSource`, `accruedFee` | The **source** token |
| `receive.amount`, `outcomeAmount`, `maxOutcomeAmount`, `settledOutcome` | The **destination** token |
| `depositFee`, `withdrawFee` (in `GET /v2/params`) | The listed token itself |
| `intentOutcomeRate` | Not an amount: a rate, destination per source unit, scaled by 10^18 |
| `terms.flatFee` (inside a quote's `build`) | Not a display amount: 18-decimal hub units. Pass it back unchanged |

A few fields are plain JSON numbers: chain ids, `decimals`, `seq`, `operatorBps`, `commissionBps`, `orderTtl`, and response timestamps (`at`, `deadline`). `build.terms.expiresAt` in a quote is a string of Unix seconds. Pass it back unchanged.

## Addresses

- Tokens are identified by chain id plus contract address, never by symbol.
- EVM addresses come back checksummed from `/v2/params` and `/v2/quote`, and lowercase in orders. Compare addresses case-insensitively.
- TRON addresses come back in base58. The API accepts base58 or hex41.
- `depositor` is always an EVM address. Send it lowercase or exactly EIP-55 checksummed; any other mixed case is a `400`.

## Error bodies

Errors come in four JSON shapes. Only the first has an `error` key, so log `error ?? message`.

| Source | Body |
|---|---|
| The route itself (most `4xx`/`5xx`) | `{ "error": string, "detail"?: object }` |
| Request validation | `{ "message": "Validation failed", "name": "ValidationException", "details": [{ "field", "message", "code" }] }` |
| Malformed JSON, maintenance, unhandled `500` | `{ "message": string, "name": string }` |
| Rate limit | `{ "errorCode": "RATE_LIMITED", "message": string }` |

An unknown path, such as one missing the `/v2` prefix, returns a plain-text `404 Not Found`, not JSON. See [Errors and retries](/guides/errors/) for what each status means and whether to retry.
