> For the complete documentation index, see [llms.txt](https://docs.ojolabs.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ojolabs.xyz/markets/api/authentication.md).

# Authentication

Most API endpoints are public and don't require authentication. When bearer token auth is enabled on the server, write endpoints (order submission, cancellation) require an API key.

## Public Endpoints

All `GET` endpoints are public and do not require authentication, including:

* `/api/v1/markets`
* `/api/v1/orderbook/{marketId}`
* `/api/v1/trades/{marketId}`
* `/api/v1/stats/{marketId}`
* `/api/v1/platform/stats`
* `/api/v1/quick-markets/{asset}`
* `/api/v1/resolution/{marketId}`
* `/api/v1/stream` (WebSocket)

## Bearer Token Authentication

When enabled, write endpoints require an `Authorization` header with a signed bearer token.

### Token Format

Tokens use Ed25519 signatures and have the form:

```
base64url(payload).base64url(signature)
```

The payload is a JSON object:

```json
{
  "kid": "your-key-id",
  "ts": 1735689000,
  "n": "random-32-char-hex-nonce"
}
```

| Field | Type   | Description                                       |
| ----- | ------ | ------------------------------------------------- |
| `kid` | string | API key ID (matches your registered key)          |
| `ts`  | int64  | Unix timestamp when token was created             |
| `n`   | string | Random 32-character hex nonce (replay prevention) |

Tokens are valid for 5 minutes from the `ts` timestamp.

{% hint style="info" %}
Ensure your system clock is accurate when generating tokens. Tokens older than 5 minutes will be rejected.
{% endhint %}

### Making Authenticated Requests

Include the token in the `Authorization` header:

{% code title="curl example" %}

```bash
curl -X POST https://api.turbinefi.com/api/v1/orders \
  -H "Authorization: Bearer <payload>.<signature>" \
  -H "Content-Type: application/json" \
  -d '{ ... }'
```

{% endcode %}

## Registering an API Key

Register for API credentials by proving wallet ownership with an Ethereum signature.

POST to:

```
POST /api/v1/api-keys
```

This endpoint does not require bearer authentication — it uses wallet signature verification instead.

{% stepper %}
{% step %}

### Request: Register API Key

Request body (JSON):

```json
{
  "address": "0xYourWalletAddress",
  "signature": "0x...",
  "name": "my-trading-bot"
}
```

| Field       | Type    | Required | Description                    |
| ----------- | ------- | -------- | ------------------------------ |
| `address`   | address | Yes      | Your Ethereum wallet address   |
| `signature` | string  | Yes      | Signature of the message below |
| `name`      | string  | No       | Friendly name for the key      |

The message to sign:

```
Register API key for Turbine: 0xYourWalletAddress
```

Sign this message using a standard Ethereum personal sign (`eth_sign` or `personal_sign`).
{% endstep %}

{% step %}

### Response: API Key Created

Successful response example:

```json
{
  "success": true,
  "api_key_id": "abc123def456...",
  "api_private_key": "deadbeef...",
  "message": "API key created successfully. Save your private key - it cannot be retrieved later!"
}
```

| Field             | Type   | Description                                           |
| ----------------- | ------ | ----------------------------------------------------- |
| `success`         | bool   | Whether registration succeeded                        |
| `api_key_id`      | string | Your key ID (used in `kid` field of tokens)           |
| `api_private_key` | string | Ed25519 private key hex (save this — shown only once) |
| `message`         | string | Status message                                        |
| {% endstep %}     |        |                                                       |

{% step %}

### Generating Tokens (Client-side)

Use your `api_key_id` and `api_private_key` to generate bearer tokens.

Python example:

{% code title="generate token (python)" %}

```python
import json
import time
import base64
import secrets
from nacl.signing import SigningKey

# Your credentials from registration
key_id = "abc123def456..."
private_key_hex = "deadbeef..."

# Create payload
payload = {
    "kid": key_id,
    "ts": int(time.time()),
    "n": secrets.token_hex(16)
}

# Sign with Ed25519
payload_bytes = json.dumps(payload).encode()
signing_key = SigningKey(bytes.fromhex(private_key_hex))
signature = signing_key.sign(payload_bytes).signature

# Encode token
token = (
    base64.urlsafe_b64encode(payload_bytes).rstrip(b"=").decode()
    + "."
    + base64.urlsafe_b64encode(signature).rstrip(b"=").decode()
)

# Use in requests
headers = {"Authorization": f"Bearer {token}"}
```

{% endcode %}

Use the generated token in the `Authorization` header when calling write endpoints.
{% endstep %}
{% endstepper %}

### Error Responses

<details>

<summary>Error responses for token/auth related failures</summary>

| Status | Error                          | Description                                |
| ------ | ------------------------------ | ------------------------------------------ |
| 401    | `missing authorization header` | No `Authorization` header provided         |
| 401    | `invalid authorization format` | Must be `Bearer <token>`                   |
| 401    | `invalid token format`         | Token is malformed                         |
| 401    | `token expired`                | Token `ts` is older than 5 minutes         |
| 401    | `API key not found`            | The `kid` doesn't match any registered key |
| 401    | `API key is inactive`          | Key has been revoked                       |
| 401    | `API key has expired`          | Key's expiration time has passed           |
| 401    | `invalid signature`            | Ed25519 signature verification failed      |

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ojolabs.xyz/markets/api/authentication.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
