Skip to main content
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 and we’ll issue a dedicated API key. Keep any key server-side and out of browser bundles.
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 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.
Send the access token as Authorization: Bearer <token>, or let the browser carry the cookie with credentials: "include".
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.
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

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