> ## Documentation Index
> Fetch the complete documentation index at: https://docs.notional.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

## Signature Format

Most `/exchange` actions use a top-level nonce plus a top-level raw ECDSA signature:

```json theme={null}
{
  "action": {
    /* action details */
  },
  "nonce": 1234567890,
  "expiresAfter": 1234569999,
  "signature": {
    "r": "0x1234...",
    "s": "0x5678...",
    "v": 27
  }
}
```

<Note>
  * `reportDeposit` is unsigned. - `acceptTerms` uses a top-level hex signature string and does not
    use a nonce. - `approveAgent`, trading actions, referral actions such as `getReferralCode` and
    `createReferralCode`, and TWAP actions use the `{(r, s, v)}` + `nonce` wrapper shown above. -
    Withdrawals also use the `{(r, s, v)}` + `nonce` wrapper, but the signed payload is the withdrawal
    action fields. - Withdrawals must be signed by the user account, not by an approved API wallet
    (agent).
</Note>

### Nonce

The `nonce` is an integer used to prevent replay attacks. In practice clients typically use a millisecond timestamp:

```javascript theme={null}
const nonce = Date.now();
```

<Warning>
  Each nonce can only be used once. Requests with duplicate nonces will be rejected.
</Warning>

### Signature Components

The signature consists of three components (standard ECDSA):

* `r` - First 32 bytes of signature (hex string)
* `s` - Second 32 bytes of signature (hex string)
* `v` - Recovery ID. Most trading actions accept `0-255`; withdrawals require `27` or `28`.

## Creating Signatures

### Trading Actions

Orders, cancels, modifies, and leverage updates are verified from the raw action payload plus `nonce`. Use the Notional SDK or the exact client implementation for canonical action normalization and signing.

```typescript theme={null}
const action = {
  type: "order",
  orders: [
    {
      a: "00020000",
      b: true,
      p: "50000.0",
      s: "0.1",
      t: { limit: { tif: "Gtc" } },
    },
  ],
  grouping: "na",
};

const request = {
  action,
  nonce: Date.now(),
  signature: {
    r: "0x...",
    s: "0x...",
    v: 27,
  },
};
```

<Note>Use the Notional SDK for exact typed-data definitions and action normalization.</Note>

### Terms Acceptance

`acceptTerms` is the exception to the nonce-based pattern. It sends the typed-data signature as a top-level hex string:

```json theme={null}
{
  "action": {
    "type": "acceptTerms",
    "termsVersion": "2026-03-01",
    "signedAt": 1701234567890,
    "chainId": 42161
  },
  "signature": "0x..."
}
```

### Withdrawals

Withdrawals still use the outer `nonce` + `{ r, s, v }` wrapper, but the signed fields live inside `action`:

```json theme={null}
{
  "action": {
    "type": "withdraw",
    "notionalChain": "Mainnet",
    "signatureChainId": "0xa4b1",
    "destination": "0x...",
    "token": "00012710",
    "amount": "1000.0",
    "source": "balance",
    "time": 1701234567890
  },
  "nonce": 1701234567890,
  "signature": {
    "r": "0x...",
    "s": "0x...",
    "v": 27
  }
}
```

`signatureChainId` selects the EIP-712 domain for signature recovery. The signed withdrawal payload is:

* `notionalChain`
* `destination`
* `token`
* `amount`
* `source`
* `time`

## Agent Approval

Instead of signing each request with your main wallet, you can authorize an **API wallet** (agent) to sign on your behalf.

### Benefits

* **Security**: Keep your main wallet offline
* **Convenience**: No manual signature approval for each trade
* **Automation**: Enable trading bots and automated strategies

### Approving an Agent

Send an `approveAgent` action signed by your **main wallet**:

```json theme={null}
{
  "action": {
    "type": "approveAgent",
    "hyperliquidChain": "Mainnet",
    "signatureChainId": "0xa4b1",
    "agentAddress": "0x...",
    "agentName": "My Trading Bot",
    "nonce": 1234567890
  },
  "nonce": 1234567890,
  "signature": {
    "r": "0x...",
    "s": "0x...",
    "v": 27
  }
}
```

**Request to:**

```bash theme={null}
POST /exchange
```

**Response:**

```json theme={null}
{
  "success": true
}
```

### Using an Approved Agent

Once approved, the agent can sign trading requests on behalf of the user:

1. Agent creates the action payload
2. Agent signs the action with its own private key
3. Backend validates:
   * Signature is valid for agent address
   * Agent is approved for the user
   * Action is authorized

The `userAddress` is recovered from the approval record, not the signature.

API wallets (agents) can place and manage trades, but they cannot initiate withdrawals.

## Validation Process

When a nonce-based signed request arrives, Notional:

1. **Validates signature structure** - Checks r, s, v format
2. **Recovers signer address** - Uses ECDSA recovery
3. **Checks authorization**:
   * If signer = user → **Direct mode** (approved)
   * If signer = agent → **Agent mode** (checks approval table)
4. **Verifies nonce** - Ensures uniqueness
5. **Validates action** - Checks order parameters, margin, etc.

## Error Responses

### Invalid Signature

```json theme={null}
{
  "error": "Invalid signature"
}
```

**HTTP Status:** 401 Unauthorized

### Agent Not Approved

```json theme={null}
{
  "error": "Agent not approved for user"
}
```

**HTTP Status:** 401 Unauthorized

### Duplicate Nonce

```json theme={null}
{
  "error": "Nonce already used"
}
```

**HTTP Status:** 400 Bad Request

## Security Best Practices

<Note>
  * **Never share your private key** - Use hardware wallets for main account - **Limit agent
    permissions** - Only approve agents you trust - **Monitor agent activity** - Review orders placed
    by agents - **Rotate agents** - Revoke and re-approve agents periodically - **Use unique nonces**
  * Ensure timestamp-based nonces are unique
</Note>
