> For the complete documentation index, see [llms.txt](https://vnx.gitbook.io/vnx-global/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://vnx.gitbook.io/vnx-global/developer/api/auth.md).

# Auth

Generate an ECDSA key pair in the portal, then sign every `/private/*` request with three headers — public key, nonce, and a SHA256 signature over the path, canonical body, and nonce.

## Before you begin

* A key pair generated in the VNX account portal at [my.vnx.io](https://my.vnx.io). See Quickstart: prerequisites.
* A test or production base URL.

## Required headers

Public `GET` endpoints need only a `User-Agent`. Every `/private/*` request requires all of the following plus `User-Agent`:

| Header              | Value                                          |
| ------------------- | ---------------------------------------------- |
| `x-app-public-key`  | Your ECDSA public key                          |
| `x-app-nonce`       | Always-increasing uint64 (UNIX ms recommended) |
| `x-app-signed-data` | base64url(ECDSA-SHA256 signature)              |

The `User-Agent` header is mandatory on **all** requests, public and private.

## The nonce

`x-app-nonce` must strictly increase across requests and **cannot be reset to a lower value**. Using the current UNIX time in milliseconds is the simplest scheme that satisfies this.

## Building the signed data

The signature is computed over a single string:

```
URI path  +  canonical-JSON POST data  +  nonce
```

**Canonical JSON** means: recursively sort object keys lexicographically; arrays keep their order. Sign the resulting string with ECDSA-SHA256, then base64url-encode the signature and place it in `x-app-signed-data`.

## Steps

1. **Build the canonical request string** from the URI path, the canonical-JSON form of the body, and the nonce.
2. **Sign** it with your private key (ECDSA-SHA256).
3. **base64url-encode** the signature.
4. **Attach** the three `x-app-*` headers plus `User-Agent` and send.

### Signed request example

```http
POST /api/v1/private/addOrder HTTP/1.1Host: api.vnx.ioContent-Type: application/x-www-form-urlencodedUser-Agent: MyClient/1.0x-app-public-key: <public_key>x-app-nonce: 1708881600001x-app-signed-data: <base64url_signature>timestamp=2024-02-25T12%3A00%3A00Z&clordid=order123&symbol=VCHF%2FCHF&side=Buy&ordtype=Limit&timeinforce=FOK&orderqty=100&price=0.95
```

Note the content type: most `/private/*` endpoints take `application/x-www-form-urlencoded`; only `/private/addPaymentRail` takes `application/json`.

## Verify

Call a signed read-only endpoint such as `/private/accountBalance` (empty body). A `200` response with a `balances` array confirms your signing and headers are correct.

## Common errors

| `code` / `status`       | Meaning                               | Fix                                             |
| ----------------------- | ------------------------------------- | ----------------------------------------------- |
| `400` invalid signature | Signed string or canonical JSON wrong | Re-check path + canonical body + nonce ordering |
| `400` bad nonce         | Nonce not greater than the last used  | Use a strictly increasing uint64 (UNIX ms)      |
| `401` unknown key       | Public key not recognised             | Use the key generated in the portal             |
| `429` rate limited      | Over 1 request/sec per key            | Backoff; pace to ≤ 1 req/sec                    |

## Next steps

* Balances
* Trading
* Quickstart: first API call

## Related

* [API Overview](/vnx-global/developer/api/overview.md)
* [Prerequisites](/vnx-global/developer/quickstart/prerequisites.md)
* [Glossary](/vnx-global/glossary.md)
