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

# Authentication overview

> Which endpoints are public, which are signed, and the three credentials and four headers a signed request needs.

Olympex authenticates requests with an HMAC signature. You create an API account once, keep its three credentials on your server, and sign every request to a signed endpoint with four headers. You sign with the secret key but never send it. The signature binds each request to its method, path, query and body, the current time and a random nonce that the server rejects if it has seen it in the last 5 minutes, so each set of signed headers works once, for the one request you signed.

Every endpoint lives under one base URL:

```text theme={null}
https://api-rest.olympex.io/api/v1
```

Requests and responses are JSON. Endpoints use `GET`, `POST`, `PATCH` and `DELETE`: `POST` and `PATCH` send a JSON body, and `GET` and `DELETE` send none. Send `Content-Type: application/json` on every request, including the public ones and those without a body. Without it, or with another type such as curl's default `application/x-www-form-urlencoded`, a request with a body fails with `403` (`Invalid body hash`) on a signed endpoint or `400` (`Invalid JSON body`) on a public one.

## Public and signed endpoints

One endpoint is public, `POST /accounts`. Every other endpoint is signed.

| Endpoints | Auth | Purpose |
| - | - | - |
| [`POST /accounts`](/api-reference/accounts/create-account) | Public | Create an API account. Returns its API key ID and secret key. |
| [`GET /chains`](/api-reference/chains/list-chains), [`GET /tokens`](/api-reference/tokens/list-tokens), [`POST /support-chain`](/api-reference/chains/check-chain-support) | Signed | List the enabled chains and a chain's tokens, or check one chain. |
| [`POST /quotes`](/api-reference/quotes/get-quote), [`POST /swap`](/api-reference/swap/build-swap), [`POST /tx-status`](/api-reference/transactions/get-transaction-status), [`GET /transactions/{hash}`](/api-reference/transactions/get-transaction) | Signed | Get a quote, get unsigned calldata for its route (never broadcast), get the status of a cross-chain transfer, and get the on-chain status of a transaction. |
| [`GET /limit-order`](/api-reference/limit-orders/list-limit-orders), [`POST /limit-order`](/api-reference/limit-orders/create-limit-order), [`GET /limit-order/{id}`](/api-reference/limit-orders/get-limit-order), [`PATCH /limit-order/{id}`](/api-reference/limit-orders/update-limit-order), [`DELETE /limit-order/{id}`](/api-reference/limit-orders/cancel-limit-order) | Signed | List, create, read, update and cancel limit orders. |
| [`GET /dca-order/strategies`](/api-reference/dca/list-dca-strategies), [`POST /dca-order/strategies`](/api-reference/dca/create-dca-strategy), [`GET /dca-order/strategies/{id}`](/api-reference/dca/get-dca-strategy), [`PATCH /dca-order/strategies/{id}`](/api-reference/dca/update-dca-strategy), [`GET /dca-order/strategies/{id}/orders`](/api-reference/dca/list-dca-strategy-orders), [`GET /dca-order/orders/{id}`](/api-reference/dca/get-dca-order) | Signed | List, create, read and cancel DCA strategies, and read their orders. |

A new API account can call the signed endpoints as soon as `POST /accounts` returns. There is no activation step.

Limit orders and DCA strategies belong to the API key that created them. List endpoints return only yours, and an ID created with another API key returns `404 NOT_FOUND`, as if it didn't exist.

## The three credentials

| Credential | Looks like | Where it comes from | On the wire |
| - | - | - | - |
| API key ID | A UUID, for example `00000000-0000-4000-8000-000000000000` | `apiKey` in the `POST /accounts` response | Sent as `x-api-key-id` on every signed request |
| Secret key | A random string | `secretKey` in the same response, shown only once | Never sent. It is the HMAC key you sign with. |
| Passphrase | The `password` you chose | The `password` field of your `POST /accounts` request | Sent as `x-passphrase` on every signed request |

Keep them in the environment variables `OLYMPEX_API_KEY_ID`, `OLYMPEX_SECRET_KEY` and `OLYMPEX_PASSPHRASE`. The reference implementations and every example in these docs read those names. [Credentials](/authentication/credentials) covers how Olympex stores each one and how you should.

<Warning>
  Protect the passphrase like the secret key. It travels in `x-passphrase` on every signed request, so anything that records your request headers records it. Keep all three credentials on your server.
</Warning>

## The four headers

Every signed request carries these headers, plus `content-type: application/json`:

| Header | Value |
| - | - |
| `x-api-key-id` | Your API key ID. |
| `x-value-info` | Standard base64, on one line, of `timestamp`, `nonce` and `bodyHash` joined with newlines. |
| `x-passphrase` | Your passphrase. |
| `x-signature` | Lowercase hex HMAC-SHA256, keyed with your secret key, of the signed message: `OLPX-HMAC-SHA256-V2`, `timestamp`, `nonce`, the method, the path, the canonical query and `bodyHash`, joined with newlines. |

`timestamp` is the current Unix time in seconds, `nonce` is 24 new random hexadecimal characters, and `bodyHash` is the unpadded base64url SHA-256 of the exact body you send: the empty string for a `GET` or `DELETE`, which has no body. The method is uppercase. The path is the URL path you call, starting with `/api/v1`, without the query string. The canonical query is the query string with its parameters decoded, sorted by key and then by value, and percent-encoded again per RFC 3986, or the empty string when there is none. Headers signed for one request don't work on another, and each nonce works once. [Sign requests](/authentication/sign-requests) has the full algorithm and reference code.

## How signing works

You never send the secret key. You hash the exact body you're about to send (the empty string for a `GET` or `DELETE`) and sign that hash together with the method, the path, the query, the current time and a random nonce, with HMAC-SHA256 keyed with your secret key. Olympex rejects timestamps more than 300 seconds from its clock, looks up your account by API key ID, recomputes the signature over the request it received with the secret key it holds for your account, checks the passphrase, and rejects nonces it has already seen in the last 5 minutes. It then checks that the body it received matches the hash you signed.

<Steps>
  <Step title="Canonicalize the body">
    Serialize the JSON body with object keys sorted at every level and no whitespace. This string, `bodyString`, is exactly what you send. A `GET` or `DELETE` has no body, so its `bodyString` is the empty string.
  </Step>

  <Step title="Hash it">
    `bodyHash` is the SHA-256 of `bodyString`, encoded as unpadded base64url.
  </Step>

  <Step title="Build the message">
    Join `OLPX-HMAC-SHA256-V2`, the Unix time in seconds, 24 new random hexadecimal characters, the method in uppercase, the path (starting with `/api/v1`), the [canonical query](/authentication/sign-requests#canonical-query) and `bodyHash` with newlines.
  </Step>

  <Step title="Sign and send">
    Send `timestamp`, `nonce` and `bodyHash`, joined with newlines, as standard base64 in `x-value-info`, and the lowercase hex HMAC-SHA256 of the message, keyed with your secret key, in `x-signature`. Add `x-api-key-id` and `x-passphrase`. Send `bodyString` as the body of a `POST` or `PATCH`, and no body with a `GET` or `DELETE`. Query parameters go in the URL, and you sign them.
  </Step>
</Steps>

[Sign requests](/authentication/sign-requests) is the normative specification, with reference implementations in TypeScript, Python and bash, [requests without a body](/authentication/sign-requests#requests-without-a-body), two known-answer vectors and a troubleshooting table.

<Tip>
  No credentials yet? [Create a test API key](/get-started/create-an-api-key) in one click, then try any signed endpoint from the [API console](/api-reference/console). If your browser blocks either call, [create the key from a terminal](/get-started/create-an-api-key#create-a-key-from-a-terminal) and send requests with [Copy as cURL](/api-reference/console#copy-as-curl).
</Tip>

## What this means for your integration

* Sign on your server. Browser, mobile and desktop apps call your backend, and your backend calls Olympex.
* Sign every attempt, retries included, right before you send it, on a server whose clock is synchronized with NTP. The server rejects a timestamp more than 300 seconds off and a nonce it has seen in the last 5 minutes, so stored headers stop working.
* Treat signed headers as single-use credentials for the one request you signed them for: never log, store or share them.
* Start from a reference implementation and test your signer against the [known-answer vectors](/authentication/sign-requests#known-answer-vector) before you send real traffic.

## Related

<CardGroup cols={2}>
  <Card title="Credentials" icon="key" href="/authentication/credentials">
    What the API key ID, secret key and passphrase are, and how to store them.
  </Card>

  <Card title="Sign requests" icon="code" href="/authentication/sign-requests">
    The algorithm, requests without a body, reference code, known-answer vectors and troubleshooting.
  </Card>

  <Card title="Errors and retries" icon="circle-exclamation" href="/authentication/errors-and-retries">
    The error envelope, gateway responses and what to retry.
  </Card>

  <Card title="Limits" icon="gauge" href="/authentication/limits">
    Timestamp window, nonce reuse, timeouts, order allowances and value ranges.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.