> ## 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.

# Security model

> What Olympex can and can't do with your funds and credentials, how requests are authenticated, and what your integration is responsible for.

Olympus stands apart from the world below, and Olympex stands apart from your funds. For swaps, the API computes routes and returns unsigned transaction calldata; custody, signing and broadcasting stay with your application and your users. For limit orders and DCA, the maker authorizes Olympex once per token pair and limits it with a token allowance, and Olympex executes the orders within that limit. This page describes those boundaries, how requests to the API are authenticated, and what your integration is responsible for.

## Non-custodial by design

* **No custody.** Olympex never takes custody of user or integrator funds. A swap moves assets from `account`, through the Olympex aggregator contract at `swap.to`, in a transaction your wallet signs. An order execution moves them from the maker's wallet, through the Olympex order contract, and sends the output back to the maker.
* **No keys.** No endpoint accepts, generates or stores a wallet private key or seed phrase.
* **No broadcasting of your swaps.** `POST /swap` returns calldata and never sends it. Single-chain requests accept a `dryRun` flag for compatibility, and it has no effect.
* **Allowances you control.** The contracts in the swap path can spend your tokens only up to the allowance you grant to `contractToApprove`. The order contract can spend a maker's tokens only up to the allowance the maker grants it, and only for pairs the maker has signed.

Your API credentials authenticate calls to the API. They can't sign for any wallet. They do control the orders created with your API key: a leaked credential lets someone call the API as your account, including creating, changing and cancelling those orders, and the maker's allowance to the order contract is what limits the result. A leak needs an immediate response, described in [Credential lifecycle](#credential-lifecycle).

## Calldata is bound and short-lived

The calldata from `POST /swap` is built for one sender and one moment:

* **Bound to `account`.** Send it from the `account` you passed to `/swap`. Another Olympex swap from the same account can invalidate earlier calldata, so build and send one swap at a time per account.
* **Expiring.** The calldata carries an on-chain expiry, 5 minutes on most routes, and some routes include a market maker's firm quote that expires within seconds. Don't store or queue it: request it right before you send.
* **Protected by a minimum.** Single-chain calldata reverts if the swap would deliver less than `minOutAmount`.

<Warning>
  There is no testnet. Calldata from `POST /swap` is real mainnet calldata, and broadcasting it moves funds.
</Warning>

## Check before you sign

Before a wallet signs a swap transaction, check that:

1. `from` is the `account` you sent to `/swap`.
2. `to` matches `swap.to`, the Olympex aggregator contract, exactly as `/swap` returned it. Record the address you see for each chain; if it changes unexpectedly, stop and confirm with [partners@olympex.io](mailto:partners@olympex.io) before signing.
3. `value` is `"0"` for ERC-20 input. For native-token input, `value` is the native amount the transaction sends, in wei: compare it with the amount the user agreed to.
4. For single-chain swaps, `minOutAmount` is within your slippage of `outAmount`.
5. Any approval goes to `contractToApprove`. It isn't always the same address as `to`.
6. `eth_estimateGas` for the transaction succeeds. A `200` from `/swap` doesn't guarantee that the calldata executes, and a transaction that reverts still costs gas. See [Falling back with `aggregatorOrder`](/concepts/aggregation-and-routing#falling-back-with-aggregatororder).

## Approvals

* For ERC-20 input to a swap, check the current allowance of `contractToApprove` first. If it is lower than the input amount, approve the exact input amount, in base units. Never approve an unlimited amount: an open allowance outlives the swap and exposes your whole balance of that token if the spender is ever compromised.
* Some tokens, such as USDT, require you to set the allowance to 0 before setting a new non-zero value.
* Native-token input needs no approval.
* Set allowances you no longer need back to 0.

The order contract for limit orders and DCA is a different spender, with its own allowance. The next section covers it.

## Order signatures and allowances

A limit order or a DCA strategy needs two authorizations from the maker's wallet: a signature over the token pair, and an allowance to the Olympex order contract. This is what each one allows:

* **The signature authorizes one pair.** It lets the Olympex order contract execute swaps of the token sold for the token bought from the maker's wallet.
* **It doesn't bind the terms.** It covers no amount, price, expiry, chain or specific order. Olympex enforces the amounts, prices and expiry of your orders when it executes them.
* **It outlives your orders.** It stays valid after you cancel an order, and the same signature serves limit orders and DCA for that pair.
* **The allowance is the on-chain limit.** The order contract can't spend more of a token than the maker's allowance to it. That limit applies whatever an order says, including an order changed through a leaked credential.
* **The order contract is an upgradeable proxy operated by Olympex.**

Olympex doesn't check the signature when you create an order: verify it yourself before you send it. The maker must be an externally owned account, because only 65-byte ECDSA signatures are accepted. [Order signatures and allowances](/concepts/order-authorization) has the signing code and the contract address on each chain.

Allowance hygiene for the order contract:

* Approve only what the maker's open orders need, never an unlimited amount. The allowance is shared by every open limit order and active DCA strategy that sells the same token on the same chain, so approve their sum.
* Lower the allowance when you cancel orders or strategies. Cancelling through the API doesn't change it.
* To stop every order that sells a token, set the allowance to 0. Do it immediately if you suspect a credential leak.
* Approve only the order contract address [published for the chain](/concepts/order-authorization#the-order-contract). It isn't the `contractToApprove` that `POST /swap` returns.

## How requests are authenticated

An API account has three credentials:

| Credential | What it is | Sent with requests | What Olympex keeps |
| - | - | - | - |
| API key ID | A UUID that identifies your account | Every signed request, in `x-api-key-id` | The ID itself |
| Secret key | A random string, shown once when you create the account, used as the HMAC key when you sign | Never | An encrypted copy. It can't be shown again. |
| Passphrase | The password you chose when you created the account | Every signed request, in `x-passphrase` | A hash. It can't be shown again. |

Every signed request carries four headers:

| 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.

These checks run on every signed request:

| Check | Where | Rejects |
| - | - | - |
| Credentials and signature | API gateway | An unknown API key ID, a wrong passphrase, an invalid signature or an inactive account, with `403 Forbidden`. |
| Timestamp | API gateway | A timestamp more than 300 seconds from server time, with `403 Forbidden`. |
| Nonce | API gateway | A nonce already seen in the last 5 minutes, with `403 Forbidden`. |
| Body hash | Olympex | A body that doesn't match the signed `bodyHash`, with `403` `FORBIDDEN` `"Invalid body hash"`. |

The signature proves the request was made with your secret key and binds it to its method, path, query and body, so any change to them is detectable. `GET` and `DELETE` requests have no body, so their signature covers the hash of the empty string. The timestamp and nonce checks limit how long, and how often, a signed request is accepted.

The signature covers the method, the path, the canonical query, the timestamp, the nonce and the body hash. It doesn't cover the other headers. Headers signed for one request are rejected on any other method, path or query, and each set is accepted once, until its timestamp is more than 300 seconds old. Treat signed headers as single-use credentials for the one request you signed them for:

* Sign each request right before you send it.
* Never log, store or share signed headers.
* Use a new nonce for every request. The nonce makes each set of headers usable once, within 5 minutes.

<Warning>
  The passphrase travels in `x-passphrase` on every signed request, so any log or trace that records request headers exposes it. Protect it exactly like the secret key.
</Warning>

## Credential lifecycle

New accounts can call signed endpoints immediately; there is no activation step. Credentials don't expire. The API has no rotation, revocation, deletion or scoping feature, and requests aren't restricted by source IP address. Because credentials can't be replaced in place, keep them in one secrets manager so that replacing them is a single change.

Choose a passphrase of at least 24 random characters from a password manager, in printable ASCII with no leading or trailing spaces. [Credentials](/authentication/credentials) explains why other values can create an account you can't use.

If a credential leaks:

<Steps>
  <Step title="Stop open orders (if you use limit orders or DCA)">
    Set each maker's allowance to the order contract to 0 for every token its open orders sell: that stops execution on-chain. Then list the limit orders and DCA strategies created with the leaked credentials, record their IDs and cancel them. Do it before the account is deactivated: a new account can't see orders created with another API key.
  </Step>

  <Step title="Deactivate the account">
    Email [partners@olympex.io](mailto:partners@olympex.io) with your API key ID and ask for the account to be deactivated. Never send the secret key or the passphrase.
  </Step>

  <Step title="Create a new account">
    Create new credentials with [`POST /accounts`](/api-reference/accounts/create-account), for example from a terminal as shown in [Create a test API key](/get-started/create-an-api-key#create-a-key-from-a-terminal), and store them in your secrets manager.
  </Step>

  <Step title="Replace and verify">
    Deploy the new credentials, confirm that signed requests succeed, then delete the old credentials from every system that held them. Create again, under the new account, any orders you still want, and set the allowances to what they need.
  </Step>
</Steps>

## Transport and handling

* **HTTPS only.** The API is served over HTTPS; plain HTTP connections to the API host are refused.
* **Server-side only.** Call signed endpoints from your server. Never ship the secret key or passphrase in a browser, mobile or desktop app.
* **Out of logs.** Redact `x-passphrase` and `x-signature` in HTTP client logs, error reports and traces, and never commit credentials to a repository.
* **Accurate clocks.** Keep your server clock synchronized, for example with NTP. Timestamps more than 300 seconds from server time are rejected.

## Shared responsibility

| Olympex | Your integration |
| - | - |
| Verifies signatures, timestamps, nonces and body hashes on every signed request. | Keeps the API key ID, secret key and passphrase on the server, in a secrets manager, out of logs. |
| Builds calldata bound to `account`, with an on-chain expiry on most routes and, for single-chain swaps, a minimum output. | Checks `from`, `to`, `value` and `minOutAmount` before anyone signs, and sends promptly. |
| Never takes custody of funds or holds keys, and never signs or broadcasts your swap transactions. | Signs and broadcasts swaps, and chooses the gas limit and fees. |
| Executes limit orders and DCA orders only for pairs the maker signed, within the maker's allowance, and enforces each order's amount, price and expiry. | Signs pairs from an EOA, verifies each signature before sending it, and keeps the allowance to the order contract at what open orders need. |
| Keeps only a hash of the passphrase and an encrypted copy of the secret key. | Approves exact amounts and vets the token addresses it offers. |
| Deactivates an account when you report a leak. | Reports leaks immediately, stops open orders, and replaces the credentials. |

## Security reviews and reports

For security questionnaires, or to report a suspected vulnerability, contact [partners@olympex.io](mailto:partners@olympex.io). Include the `meta.requestId`, or the `apigw-requestid` response header for gateway responses, of any request your report concerns. The [privacy notice](/legal/privacy) describes how Olympex handles data.

## What this means for your integration

* Treat the passphrase exactly like the secret key: both stay on your server, in a secrets manager, out of logs. Treat signed headers as single-use, and never log them.
* Verify every transaction before it is signed, and approve exact amounts only.
* For limit orders and DCA, keep the allowance to the order contract at what open orders need, and set it to 0 to stop them all.
* Prepare your leak response before you need it: stop open orders, deactivate through [partners@olympex.io](mailto:partners@olympex.io), create a new account, redeploy.

## Related

<CardGroup cols={2}>
  <Card title="Sign requests" icon="code" href="/authentication/sign-requests">
    The signing algorithm and reference code.
  </Card>

  <Card title="Order signatures and allowances" icon="signature" href="/concepts/order-authorization">
    The pair signature, the allowance and the order contract.
  </Card>

  <Card title="Execute a swap" icon="bolt" href="/guides/execute-a-swap">
    Approve, estimate gas and send safely.
  </Card>

  <Card title="Going to production" icon="list-check" href="/guides/going-to-production">
    The checklist before real volume.
  </Card>
</CardGroup>


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