# Private Endpoints

Private endpoints cover trading, balances, account actions, and trading history.

**Service Hosts**

There is no single gateway. Each path prefix maps to its own service:

| Path prefix            | Host                                      |
| ---------------------- | ----------------------------------------- |
| `/orders/*`            | `https://order-service.outpoll.com`       |
| `/api/events/*`        | `https://event-service.outpoll.com`       |
| `/api/user-balances/*` | `https://wallet-mutator-view.outpoll.com` |
| `/api/history/*`       | `https://history-service.outpoll.com`     |
| `/api/deposit/*`       | `https://wallet-mutator-view.outpoll.com` |
| `/api/transactions`    | `https://wallet-mutator-view.outpoll.com` |
| `/auth/*`              | `https://auth-service.outpoll.com`        |

### Authentication

Trading and data endpoints use API key headers.

| Header                  | Description                             |
| ----------------------- | --------------------------------------- |
| `OUTPOLL-API-KEY`       | API key. Starts with `op_k_`            |
| `OUTPOLL-API-SIGNATURE` | Base64URL-encoded HMAC-SHA256 signature |
| `OUTPOLL-API-TIMESTAMP` | Unix timestamp in seconds               |

Build the signature payload as:

```
message = timestamp + method + path + body
```

Use only the path.

Do not include the full URL.

Do not include the query string.

Timestamps must be within **30 seconds** of server time.

### Signature example

Use this worked example for a signed `POST` request.

#### Input values

* API key: `op_k_abc123`
* API secret: `dGVzdF9zZWNyZXRfMTIzNDU2Nzg`
* timestamp: `1712500000`
* method: `POST`
* path: `/orders/market`

{% code title="body.json" %}

```json
{"e":"54ccea1a-16fd-469c-8018-84b375243e8a","o":"b21f6fd8-b9d1-4b9f-bb79-ef141e3dcb76","ba":"a1b2c3d4-...","qa":"078dcd98-928d-479f-8110-ff6d27e44de2","s":"BUY","am":50}
```

{% endcode %}

{% stepper %}
{% step %}

### Build the message

Concatenate `timestamp + method + path + body`.

{% code title="message.txt" %}

```
1712500000POST/orders/market{"e":"54ccea1a-16fd-469c-8018-84b375243e8a","o":"b21f6fd8-b9d1-4b9f-bb79-ef141e3dcb76","ba":"a1b2c3d4-...","qa":"078dcd98-928d-479f-8110-ff6d27e44de2","s":"BUY","am":50}
```

{% endcode %}
{% endstep %}

{% step %}

### Decode the secret

The secret is base64url encoded.

This value is already correctly padded.

{% code title="secret.txt" %}

```
dGVzdF9zZWNyZXRfMTIzNDU2Nzg
```

{% endcode %}

Decode it to raw bytes before signing.
{% endstep %}

{% step %}

### Compute the digest

Generate `HMAC-SHA256(secret_bytes, message)`.

The output is 32 raw bytes.
{% endstep %}

{% step %}

### Encode the signature

Base64url-encode the digest.

Strip any trailing `=` characters.

{% code title="signature.txt" %}

```
k7Hj9xQ2mN4pL1rT5vW8yB3cF6gJ0sD_eI2uA4wK7Zo
```

{% endcode %}
{% endstep %}

{% step %}

### Send the headers

Include the API key, signature, and timestamp headers.

{% code title="headers.http" %}

```http
OUTPOLL-API-KEY: op_k_abc123
OUTPOLL-API-SIGNATURE: k7Hj9xQ2mN4pL1rT5vW8yB3cF6gJ0sD_eI2uA4wK7Zo
OUTPOLL-API-TIMESTAMP: 1712500000
```

{% endcode %}
{% endstep %}
{% endstepper %}

{% hint style="info" %}
The body must match the exact JSON string sent on the wire. Any spacing or field order change will change the signature.
{% endhint %}

#### cURL example

Use this shell example to compute the signature and send the request with `curl`.

{% code title="signed\_market\_order.sh" %}

```bash
API_KEY='op_k_abc123'
API_SECRET='dGVzdF9zZWNyZXRfMTIzNDU2Nzg'
TIMESTAMP='1712500000'
METHOD='POST'
PATH='/orders/market'
BODY='{"e":"54ccea1a-16fd-469c-8018-84b375243e8a","o":"b21f6fd8-b9d1-4b9f-bb79-ef141e3dcb76","ba":"a1b2c3d4-...","qa":"078dcd98-928d-479f-8110-ff6d27e44de2","s":"BUY","am":50}'

MESSAGE="${TIMESTAMP}${METHOD}${PATH}${BODY}"

SECRET_B64="$(printf '%s' "$API_SECRET" | tr '_-' '/+' | awk '{ n = length($0) % 4; if (n == 2) printf "%s==", $0; else if (n == 3) printf "%s=", $0; else printf "%s", $0 }')"
SECRET_HEX="$(printf '%s' "$SECRET_B64" | base64 -d | xxd -p -c 256)"

SIGNATURE="$(
  printf '%s' "$MESSAGE" \
  | openssl dgst -sha256 -mac HMAC -macopt "hexkey:$SECRET_HEX" -binary \
  | openssl base64 -A \
  | tr '+/' '-_' \
  | tr -d '='
)"

curl -X POST "https://order-service.outpoll.com${PATH}" \
  -H "Content-Type: application/json" \
  -H "OUTPOLL-API-KEY: ${API_KEY}" \
  -H "OUTPOLL-API-SIGNATURE: ${SIGNATURE}" \
  -H "OUTPOLL-API-TIMESTAMP: ${TIMESTAMP}" \
  --data "$BODY"
```

{% endcode %}

#### Python signature example

Use this example to sign and send a market order request directly to the order service.

{% code title="signed\_market\_order.py" %}

```python
import base64, hashlib, hmac, json, time, requests

API_KEY = "op_k_abc123"
API_SECRET = "dGVzdF9zZWNyZXRfMTIzNDU2Nzg"
HOST = "https://order-service.outpoll.com"
PATH = "/orders/market"
BODY = {
    "e": "54ccea1a-16fd-469c-8018-84b375243e8a",
    "o": "b21f6fd8-b9d1-4b9f-bb79-ef141e3dcb76",
    "ba": "a1b2c3d4-...",
    "qa": "078dcd98-928d-479f-8110-ff6d27e44de2",
    "s": "BUY",
    "am": 50,
}

body = json.dumps(BODY)
timestamp = str(int(time.time()))
message = timestamp + "POST" + PATH + body
secret_padded = API_SECRET + "=" * (4 - len(API_SECRET) % 4) if len(API_SECRET) % 4 else API_SECRET
secret_bytes = base64.urlsafe_b64decode(secret_padded)
signature = base64.urlsafe_b64encode(
    hmac.new(secret_bytes, message.encode(), hashlib.sha256).digest()
).decode().rstrip("=")

response = requests.post(
    HOST + PATH,
    headers={
        "OUTPOLL-API-KEY": API_KEY,
        "OUTPOLL-API-SIGNATURE": signature,
        "OUTPOLL-API-TIMESTAMP": timestamp,
        "Content-Type": "application/json",
    },
    data=body,
    timeout=10,
)

print(response.status_code, response.text)
```

{% endcode %}

#### JavaScript signature example

Use this example to sign and send the same request with `fetch`.

{% code title="signed\_market\_order.js" %}

```javascript
import crypto from "node:crypto";

const apiKey = "op_k_abc123";
const apiSecret = "dGVzdF9zZWNyZXRfMTIzNDU2Nzg";
const host = "https://order-service.outpoll.com";
const path = "/orders/market";
const body = JSON.stringify({
  e: "54ccea1a-16fd-469c-8018-84b375243e8a",
  o: "b21f6fd8-b9d1-4b9f-bb79-ef141e3dcb76",
  ba: "a1b2c3d4-...",
  qa: "078dcd98-928d-479f-8110-ff6d27e44de2",
  s: "BUY",
  am: 50,
});

const timestamp = String(Math.floor(Date.now() / 1000));
const message = timestamp + "POST" + path + body;
const secret = Buffer.from(
  apiSecret + "=".repeat((4 - apiSecret.length % 4) % 4),
  "base64url",
);
const signature = crypto
  .createHmac("sha256", secret)
  .update(message)
  .digest("base64url");

const response = await fetch(host + path, {
  method: "POST",
  headers: {
    "OUTPOLL-API-KEY": apiKey,
    "OUTPOLL-API-SIGNATURE": signature,
    "OUTPOLL-API-TIMESTAMP": timestamp,
    "Content-Type": "application/json",
  },
  body,
});

console.log(response.status, await response.text());
```

{% endcode %}

#### OutpollClient

Use a path-to-host map when one client needs to call multiple private services.

{% code title="outpoll\_client.py" %}

```python
import hmac, hashlib, base64, time, json, requests

SERVICE_MAP = {
    "/api/events": "https://event-service.outpoll.com",
    "/api/categories": "https://event-service.outpoll.com",
    "/api/tags": "https://event-service.outpoll.com",
    "/api/coins": "https://wallet-mutator-view.outpoll.com",
    "/api/user-balances": "https://wallet-mutator-view.outpoll.com",
    "/api/history": "https://history-service.outpoll.com",
    "/orders": "https://order-service.outpoll.com",
    "/auth": "https://auth-service.outpoll.com",
}

class OutpollClient:
    def __init__(self, api_key: str, api_secret: str):
        self.api_key = api_key
        self.api_secret = api_secret

    def _host(self, path: str) -> str:
        for prefix, host in SERVICE_MAP.items():
            if path.startswith(prefix):
                return host
        return "https://event-service.outpoll.com"

    def _sign(self, method: str, path: str, body: str = "") -> dict:
        timestamp = str(int(time.time()))
        message = timestamp + method + path + body
        padded = self.api_secret + "=" * (4 - len(self.api_secret) % 4) if len(self.api_secret) % 4 else self.api_secret
        secret_bytes = base64.urlsafe_b64decode(padded)
        signature = base64.urlsafe_b64encode(
            hmac.new(secret_bytes, message.encode(), hashlib.sha256).digest()
        ).decode().rstrip("=")
        return {
            "OUTPOLL-API-KEY": self.api_key,
            "OUTPOLL-API-SIGNATURE": signature,
            "OUTPOLL-API-TIMESTAMP": timestamp,
            "Content-Type": "application/json",
        }

    def get(self, path: str, params: dict = None):
        headers = self._sign("GET", path)
        return requests.get(self._host(path) + path, headers=headers, params=params)

    def post(self, path: str, data: dict = None):
        body = json.dumps(data) if data else ""
        headers = self._sign("POST", path, body)
        return requests.post(self._host(path) + path, headers=headers, data=body)

    def delete(self, path: str):
        headers = self._sign("DELETE", path)
        return requests.delete(self._host(path) + path, headers=headers)
```

{% endcode %}

### Sections

* [API Keys](https://docs.outpoll.com/api/rest-api/private-endpoints/api-key-management/api-keys) for key creation, inspection, and revocation.
* [Orders](https://docs.outpoll.com/api/rest-api/private-endpoints/orders) for limit, market, and TP/SL orders.
* [Portfolio](https://docs.outpoll.com/api/rest-api/private-endpoints/portfolio) for portfolio-related endpoints.
* [Positions](https://docs.outpoll.com/api/rest-api/private-endpoints/positions) for open positions and active orders.
* [Balances](https://docs.outpoll.com/api/rest-api/private-endpoints/balances) for balance queries.
* [Deposit](https://docs.outpoll.com/api/rest-api/private-endpoints/deposit) for the USDC deposit address.
* [History & Stats](https://docs.outpoll.com/api/rest-api/private-endpoints/history-and-stats) for history coverage.
* [History](https://docs.outpoll.com/api/rest-api/private-endpoints/history) for orders, trades, transactions, activity, and profile metrics.

### Example IDs

Examples use readable placeholders:

* `evt_btc_100k_2026` for an event
* `mkt_btc_100k_yes_no` for a market
* `asset_btc_100k_yes` for the YES asset
* `asset_usdc` for USDC
* `ord_7f3a9c2d` for an order
* `tpsl_4e2b1a90` for a TP/SL order

### Shared rules

See [FAQ](https://docs.outpoll.com/api/faq) for rate limits, pagination, and common error handling.
