> For the complete documentation index, see [llms.txt](https://titan-exchange.gitbook.io/titan/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://titan-exchange.gitbook.io/titan/developer-doc/special-order-types/reference/authentication.md).

# Authentication

The two headers that authenticate every request, and the three request tiers.

The API uses two headers. There's no OAuth, no token exchange, and no partner-signed JWT — your backend holds one API key and identifies users by the same id you already use for them. The only signature anywhere in the flow is the user's one-time SIWS at [onboarding](/titan/developer-doc/special-order-types/guides/onboarding.md).

| Header         | Required on                   | Purpose                                                                                                        |
| -------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `X-Titan-Key`  | **every** request             | Authenticates your integration (your tenant). Keep it server-side; never ship it to a browser.                 |
| `X-Titan-User` | every **user-scoped** request | Your stable id for the end-user — the same value you onboarded as `sub`. Titan maps it to that user's manager. |

Every header originates on your backend. Nothing goes from the end-user's browser directly to Titan.

| Header              | Value                                                                                                                                                           |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `X-Titan-Key`       | Your partner API key. Never expose to browsers.                                                                                                                 |
| `X-Titan-User`      | Your stable id for the user the request acts on. Required on user-scoped routes.                                                                                |
| `Content-Type`      | `application/json` on POST/PATCH.                                                                                                                               |
| `X-Idempotency-Key` | Optional on selected POSTs. A stable, unique-per-operation string. See [Idempotency](/titan/developer-doc/special-order-types/reference/limits.md#idempotency). |
| `X-Request-Id`      | Optional. If set, Titan keeps it in logs and echoes it back as `x-request-id`. Otherwise one is generated.                                                      |

## Request tiers

| Tier             | Headers                        | Endpoints                                                                           |
| ---------------- | ------------------------------ | ----------------------------------------------------------------------------------- |
| **Public**       | none                           | `GET /health`                                                                       |
| **Partner-only** | `X-Titan-Key`                  | `POST /partner/onboard`, `GET /partners/me/*` (reporting)                           |
| **User-scoped**  | `X-Titan-Key` + `X-Titan-User` | Everything that acts on a specific user — balances, orders, withdrawals, `GET /me`. |

{% hint style="info" %}
Reporting is **tenant-scoped, not user-header-scoped**. The `/partners/me/*` endpoints use `X-Titan-Key` only and return rows across your whole tenant. To narrow them to one user, pass `?userId=<the userId returned by POST /partner/onboard>` — that `userId` is the opaque Titan id, **not** your `sub` / `X-Titan-User`. This is the main reason to store the `userId` onboarding returns.
{% endhint %}

## User consent

A user's external wallet is bound to their manager by a SIWS signature collected once at `POST /partner/onboard`. After that, you act on their behalf within the on-chain policy by sending `X-Titan-User` — no further per-request user signature. The policy pins the manager to order-execution swaps — DCA cycles and trigger fills — plus withdrawals only to that user's own external wallet, so neither you nor Titan can move funds anywhere else.

## Auth errors

These apply to every authenticated endpoint:

| HTTP | `error.code`             | When                                                                                                                                         |
| ---- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
| 401  | `INVALID_API_KEY`        | Missing or unknown `X-Titan-Key`.                                                                                                            |
| 401  | `KEY_REVOKED`            | Your key was revoked.                                                                                                                        |
| 401  | `ENV_MISMATCH`           | Key issued for a different environment.                                                                                                      |
| 401  | `UNAUTHORIZED`           | On a user-scoped route: `X-Titan-User` is missing, or the supplied id was never onboarded. Call `POST /partner/onboard` for that user first. |
| 403  | `PRODUCT_DISABLED`       | The product is disabled on your key.                                                                                                         |
| 403  | `PRODUCT_EXPIRED`        | Your product grant has expired.                                                                                                              |
| 403  | `TENANT_SUSPENDED`       | Your tenant is suspended (recoverable; contact Titan).                                                                                       |
| 403  | `TENANT_DELETED`         | Your tenant is deleted.                                                                                                                      |
| 403  | `TENANT_NOT_PROVISIONED` | Key valid, but no tenant row yet — onboarding step missing on Titan's side.                                                                  |

## Verify your plumbing

`GET /me` resolves the identity for the current request and is the quickest smoke test that `X-Titan-Key` + `X-Titan-User` map to the user you expect:

```json
{
  "success": true,
  "data": { "userId": "<opaque user id>", "walletAddress": "GZk2v…", "sessionId": "" }
}
```

`sessionId` is always empty for partner integrations — ignore it. Rely on `userId` / `walletAddress`, and treat both as opaque strings (the field names are stable; their format may change).

## Related pages

* [Onboarding (SIWS)](/titan/developer-doc/special-order-types/guides/onboarding.md) — how the `X-Titan-User` → manager mapping gets created
* [Endpoints](/titan/developer-doc/special-order-types/reference/endpoints.md) — the tier each route belongs to
* [Error Codes](/titan/developer-doc/special-order-types/reference/error-codes.md) — the complete error catalog
