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

# Authentication

> The tenant API key header, the optional SIWX wallet sign-in flow, and which endpoints need which credential.

Most integrations need exactly one credential: a tenant API key sent as the `x-tenant-api-key` header on pricing and order-creation calls. The key in the examples below is Vane's shared tenant key, and it's all a normal integration needs. If you're integrating Vane into a wallet or another product and need custom parameters or separate order tracking, contact [support](/resources/support) and we'll issue a dedicated API key. Keep any key server-side and out of browser bundles.

```bash theme={null}
curl 'https://api.vane.xyz/api/v1/tokens/quote?depositTokenId=158&settleTokenId=231&amount=0.005&amountIsDeposit=true' \
  -H 'x-tenant-api-key: c7eccc0aaed64932a85d35658fa55a4fb2d60cd3d2c529cfd643dc676ee82e82'
```

A `401` with the message `Valid access token required.` means the tenant key is missing or invalid, so fix the header rather than your token setup.

A `500` `Internal server error` on a request that works from curl usually means a browser sent it. The API rejects cross-site requests from any `Origin` other than `vane.xyz`, its subdomains, and `localhost` with a port, and that check runs before authentication. Calls from a page on another domain go through your own backend; clients that send no `Origin` header, such as curl, server code, and mobile apps, are never affected.

## Wallet sign-in (SIWX)

SIWX is optional and under normal circumstances you should not use it; the tenant key covers the whole swap flow. It is documented here for completeness. Contact [support](/resources/support) if you have questions about it.

1. `GET /auth/siwx/challenge` returns a single-use nonce; no credentials needed. Fetch a fresh one per sign-in: a `201` from verify consumes the nonce and any later use gets `401` `Invalid or expired nonce.`, while a failed verify leaves it usable, so a retry after a bad signature can reuse it.
2. The wallet signs the message `Nonce: <nonce>` with its standard message-signing method (`personal_sign` on EVM wallets).
3. `POST /auth/siwx/verify` exchanges the signature for tokens. The tenant key is required here. Success is `201` with `{accessToken, refreshToken}`, plus two HttpOnly cookies: `access_token` (15 minutes, `Path=/`) and `refresh_token` (30 days, `Path=/api/auth`). A bad signature, a message without a `Nonce:` line, or a used nonce returns `401` with a message naming the cause. A wrong tenant key returns `401` `x-tenant-api-key not present`; leaving the header out entirely returns a generic `500`.

```bash theme={null}
NONCE=$(curl -s https://api.vane.xyz/api/auth/siwx/challenge | jq -r .nonce)

# wallet signs "Nonce: $NONCE", then:
curl -X POST https://api.vane.xyz/api/auth/siwx/verify \
  -H 'x-tenant-api-key: c7eccc0aaed64932a85d35658fa55a4fb2d60cd3d2c529cfd643dc676ee82e82' \
  -H 'Content-Type: application/json' \
  -d '{"data":{"chainId":"1","accountAddress":"0xYourWallet"},"message":"Nonce: '$NONCE'","signature":"0xSignature"}'
```

Send the access token as `Authorization: Bearer <token>`, or let the browser carry the cookie with `credentials: "include"`.

<Warning>
  `POST /auth/refresh` currently rejects every refresh. Replaying the `refresh_token` cookie exactly as set at sign-in returns `401` `Invalid or expired refresh token.`, even seconds after the `201`, while the matching `access_token` still passes `GET /auth/me`. Adding the tenant key, an `Origin` header, a JSON body, or a Bearer access token changes nothing. Until this is fixed, re-run the sign-in flow from the challenge when the 15-minute access token lapses.
</Warning>

`POST /auth/refresh` is meant to reissue both tokens before the 15-minute access lifetime runs out. The refresh token travels one way only: the HttpOnly `refresh_token` cookie exactly as set at sign-in. A raw JWT in a cookie, a Bearer header, or a JSON body all return `401` `Refresh token not found.` Browsers replay the cookie automatically; other clients keep a cookie jar. If a refresh fails with any `401`, re-run the sign-in flow from the challenge.

## Which endpoints need what

| Endpoints                                                                                                                      | Credential                                    |
| ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------- |
| `GET /v1/tokens`, `GET /v1/orders/{uuid}`, `GET /auth/siwx/challenge`, the [status stream](/api-reference/order-status-stream) | None                                          |
| `GET /v1/tokens/swap-limits`, `GET /v1/tokens/quote`, `POST /v1/orders/create`                                                 | Tenant key; a Bearer access token also passes |
| `POST /auth/siwx/verify`                                                                                                       | Tenant key                                    |
| `GET /v1/orders`, `GET /auth/me`                                                                                               | Access token (Bearer header or cookie)        |
| `POST /auth/refresh`                                                                                                           | The `refresh_token` cookie                    |

Keep sending the tenant key even when a user is signed in. Access tokens lapse after 15 minutes; the key doesn't expire.
