> For the complete documentation index, see [llms.txt](https://docs.hello.trade/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hello.trade/developer-tools/rest-api/authenticated-endpoints.md).

# Authenticated Endpoints

All authenticated endpoints require EIP-712, EIP-2612, or canonical-JSON `personal_sign` signatures depending on the endpoint.

**Base URL**: `https://api.app.hello.trade/api`

***

## POST /api/referral/apply

Apply an invite code to grant a wallet app access. Without app access, the wallet cannot authenticate against any other authed write endpoint — see [Authentication & Account Model — App Access Gate](/developer-tools/authentication-and-account-model.md#app-access-gate).

### Headers

| Header         | Description                                                                                            |
| -------------- | ------------------------------------------------------------------------------------------------------ |
| `content-type` | `application/json`                                                                                     |
| `x-signature`  | viem `personal_sign` over the canonical-JSON payload below                                             |
| `x-timestamp`  | The same `timestamp` value used in the signed payload (`Date.now()` ms)                                |
| `x-nonce`      | The same `nonce` value used in the signed payload — must be unique per request, `[A-Za-z0-9_-]{8,128}` |

### Signed Payload (canonical JSON)

```json
{
  "body":{
    "code":"HELLO12",
    "walletAddress":"0xabc..."
    },
  "method":"POST",
  "nonce":"<unique>",
  "path":"/referral/apply",
  "timestamp":1715500000000,
  "walletAddress":"0xabc..."
}
```

| Field           | Description                                                                                                                                                                               |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `body`          | The full request body, with keys sorted alphabetically. `null` for bodyless requests. Bodies must be flat JSON objects — nested objects are not deterministically ordered by this scheme. |
| `method`        | Literal `"POST"`                                                                                                                                                                          |
| `nonce`         | Matches the `x-nonce` header                                                                                                                                                              |
| `path`          | Literal `"/referral/apply"`                                                                                                                                                               |
| `timestamp`     | Matches `x-timestamp`; ms since epoch; server enforces a ±5 minute window                                                                                                                 |
| `walletAddress` | Lowercased wallet that is applying the code                                                                                                                                               |

Top-level keys must appear in alphabetical order with no whitespace; keys inside `body` must also be sorted alphabetically. The server canonicalizes identically before recovering the signer, so any tampering with body fields invalidates the signature.

### Request Body

```json
{
  "walletAddress": "0xabc...",
  "code": "FRIEND7"
}
```

| Field           | Type   | Description                                                                                            |
| --------------- | ------ | ------------------------------------------------------------------------------------------------------ |
| `walletAddress` | string | Lowercased wallet address (must match the `walletAddress` in the signed payload)                       |
| `code`          | string | Invite code, `[A-Z0-9-]{1,7}`. Must match the `code` field inlined under `body` in the signed payload. |

### Response — Success

HTTP status `201 Created`. `kind` is `"attributed"` for affiliate codes and `"system"` for admin-minted codes; the `"system"` branch omits `referrerAddress`.

```json
{
  "kind": "attributed",
  "refereeAddress": "0xabc...",
  "referrerAddress": "0xdef...",
  "code": "FRIEND7"
}
```

```json
{
  "kind": "system",
  "refereeAddress": "0xabc...",
  "code": "WAITLIST"
}
```

### Response — Errors

| HTTP  | Body shape                                                       | Notes                                                                                            |
| ----- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `400` | `{"error":"walletAddress and code are required"}`                | Malformed body                                                                                   |
| `401` | `{"error":"Unauthorized: <reason>"}`                             | Signature problem (missing headers, expired timestamp, replay, or signer mismatch)               |
| `422` | `{"error":"ALREADY_REFERRED"}`                                   | Wallet has already applied a referral code. Attribution is first-touch and immutable.            |
| `422` | `{"error":"CODE_NOT_FOUND"}`                                     | No referral code matches the submitted string                                                    |
| `422` | `{"error":"CODE_REVOKED"}`                                       | Code is valid but has been revoked. The affiliate likely rotated it; ask for a current code.     |
| `422` | `{"error":"CODE_EXHAUSTED"}`                                     | Code has reached its `maxUses` limit                                                             |
| `422` | `{"error":"SELF_REFERRAL"}`                                      | The signer owns the code they're trying to apply                                                 |
| `503` | `{"error":"Referral service unavailable, please retry"}`         | Transient transport failure between api-gateway and the referral service. Safe to retry.         |
| `504` | `{"error":"Referral service timed out, please try again later"}` | Upstream is reachable but slow. Back off before retrying — tight retry loops can worsen a wedge. |

### TypeScript example

```typescript
import { createWalletClient, custom } from "viem";

const wallet = createWalletClient({
  account: walletAddress as `0x${string}`,
  transport: custom(window.ethereum),
});

const timestamp = Date.now();
const nonce = crypto.randomUUID().replace(/-/g, "");

// The request body, with keys sorted alphabetically. The same object is
// inlined into the signed payload AND sent on the wire — any divergence
// invalidates the signature.
const body = {
  code: inviteCode,
  walletAddress: walletAddress.toLowerCase(),
};

const message = JSON.stringify({
  body,
  method: "POST",
  nonce,
  path: "/referral/apply",
  timestamp,
  walletAddress: walletAddress.toLowerCase(),
});

const signature = await wallet.signMessage({ message });

const res = await fetch("https://api.app.hello.trade/api/referral/apply", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-signature": signature,
    "x-timestamp": String(timestamp),
    "x-nonce": nonce,
  },
  body: JSON.stringify(body),
});
```

After a successful apply, re-check `GET /api/user/access-status/:walletAddress` if you want a positive read before issuing further signed requests.

***

## POST /api/margin/deposit

Deposit collateral using ERC-2612 permit and vault authorization.

### Request

```json
{
  "amount": "1000.00",
  "instrument": null,
  "marginMode": "Cross",
  "permitSignature": {
    "sig": "0x...",
    "payload": "0x05..."
  },
  "depositSignature": {
    "sig": "0x...",
    "payload": "0x07..."
  }
}
```

| Field              | Type           | Description                                                    |
| ------------------ | -------------- | -------------------------------------------------------------- |
| `amount`           | string         | Deposit amount (human-readable, e.g., "1000.50")               |
| `instrument`       | string \| null | Instrument symbol for isolated margin, `null` for cross margin |
| `marginMode`       | string         | `"Cross"` or `"Isolated"`                                      |
| `permitSignature`  | Object         | ERC-2612 permit signature (type `0x05`)                        |
| `depositSignature` | Object         | Vault deposit authorization signature (type `0x07`)            |

**Important Notes:**

* Requires **two signatures**: Permit (token approval) and Deposit (vault authorization)
* `permitSignature.payload` contains encoded Permit struct (token nonce must be fetched from token contract)
* `depositSignature.payload` contains encoded Deposit struct (includes `market`, `flags`, `deadline`)
* **Cross margin**: `instrument: null`, `marginMode: "Cross"`, and in the Deposit signature `market: 0`, `flags` bit 0 = `0`
* **Isolated margin**: `instrument` set to the market's symbol, `marginMode: "Isolated"`, and in the Deposit signature `market` = the instrument ID, `flags` bit 0 = `1`. The collateral lands in that market's isolated margin.

For signature construction, see [Signatures - Deposits](/developer-tools/signatures.md#deposits).

### Response

```json
{
  "command_id": 100001,
  "status": "Submitted"
}
```

| Field        | Type   | Description               |
| ------------ | ------ | ------------------------- |
| `command_id` | u64    | Command ID for tracking   |
| `status`     | string | `Submitted` or `Rejected` |

Track deposit completion via `DepositMargin` execution report on WebSocket.

***

## POST /api/margin/withdrawal

Withdraw collateral.

### Request

```json
{
  "amount": "500.00",
  "instrument": null,
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x03..."
  }
}
```

| Field        | Type           | Description                                                    |
| ------------ | -------------- | -------------------------------------------------------------- |
| `amount`     | string         | Withdrawal amount (human-readable, e.g., "500.25")             |
| `instrument` | string \| null | Instrument symbol for isolated margin, `null` for cross margin |
| `marginMode` | string         | `"Cross"` or `"Isolated"`                                      |
| `signature`  | Object         | EIP-712 withdrawal signature (type `0x03`)                     |

**Important Notes:**

* `signature.payload` contains encoded Withdrawal struct with `owner`, `amount`, `market`, `flags`, `nonce`, `deadline`
* **Cross margin**: `instrument: null`, `marginMode: "Cross"`, and in the signature `market: 0`, `flags` bit 0 = `0`
* **Isolated margin**: `instrument` set to the market's symbol, `marginMode: "Isolated"`, and in the signature `market` = the instrument ID, `flags` bit 0 = `1`. Withdraws from that market's isolated margin; alternatively transfer to cross first and withdraw from there.

For signature construction, see [Signatures - Withdrawals](/developer-tools/signatures.md#withdrawals).

### Response

```json
{
  "command_id": 100002,
  "status": "Submitted"
}
```

Track withdrawal completion via `WithdrawMargin` execution report on WebSocket.

***

## POST /api/margin/transfer

Move collateral between cross margin and a market's isolated margin. No tokens move on- or off-chain — the transfer reallocates collateral within the account.

### Request

```json
{
  "amount": "250.00",
  "instrument": "BTC",
  "direction": "crossToIsolated",
  "signature": {
    "sig": "0x...",
    "payload": "0x08..."
  }
}
```

| Field        | Type   | Description                                      |
| ------------ | ------ | ------------------------------------------------ |
| `amount`     | string | Transfer amount (human-readable, e.g., "250.00") |
| `instrument` | string | Instrument symbol of the isolated market         |
| `direction`  | string | `"crossToIsolated"` or `"isolatedToCross"`       |
| `signature`  | Object | EIP-712 margin transfer signature (type `0x08`)  |

**Important Notes:**

* `signature.payload` contains the encoded MarginTransfer struct with `owner`, `amount`, `market`, `flags`, `nonce`, `deadline`
* `market` in the signature is the instrument ID of the isolated market (both directions)
* `flags` bit 0 carries the **source** account's margin mode — `0` for `crossToIsolated`, `1` for `isolatedToCross` — and must match `direction`
* `crossToIsolated` draws from cross **withdrawable** (free collateral); `isolatedToCross` draws from the market's free isolated margin

For signature construction, see [Signatures - Margin Transfers](/developer-tools/signatures.md#margin-transfers).

### Response

```json
{
  "command_id": 100004,
  "status": "Submitted"
}
```

Track transfer settlement via `TransferMargin` execution report on WebSocket. The same operation is also available over the trading WebSocket as a `transferMargin` message (acked with `marginTransferred`).

***

## POST /api/update\_leverage

Update leverage for an instrument.

### Request

```json
{
  "leverage": 1000,
  "instrument": "NVDA",
  "marginMode": "Cross",
  "signature": {
    "sig": "0x...",
    "payload": "0x04..."
  }
}
```

| Field        | Type   | Description                                     |
| ------------ | ------ | ----------------------------------------------- |
| `leverage`   | u32    | Leverage × 100 (e.g., 1000 = 10x)               |
| `instrument` | string | Instrument symbol                               |
| `marginMode` | string | `"Cross"` or `"Isolated"`                       |
| `signature`  | Object | EIP-712 leverage update signature (type `0x04`) |

**Important Notes:**

* `signature.payload` contains encoded LeverageUpdate struct with `account`, `market`, `leverage`, `flags`, `nonce`, `deadline`
* `flags` bit 0 must match `marginMode` (`0` = Cross, `1` = Isolated)
* Leverage is tracked per margin mode: the same instrument can hold different leverage cross vs isolated

For signature construction, see [Signatures - Leverage Updates](/developer-tools/signatures.md#leverage-updates).

### Response

```json
{
  "command_id": 100003,
  "status": "Submitted"
}
```

Track leverage update completion via `UpdateLeverage` execution report on WebSocket.

***

## Common Errors

| HTTP Status | Description                                                     |
| ----------- | --------------------------------------------------------------- |
| 400         | Bad Request (invalid parameters, signature verification failed) |
| 404         | Not Found (account not found)                                   |
| 500         | Internal Server Error                                           |

Error response format:

```json
{
  "error": "Error message"
}
```

For detailed error descriptions, see [Error Codes](/developer-tools/error-codes.md).
