> ## 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.

# Verify wallet signature

> Verifies a wallet signature over the challenge nonce and returns a JWT token pair with **HTTP 201**. The signed `message` must contain the line `Nonce: <nonce>` using the nonce from `GET /auth/siwx/challenge`; both the minimal single-line format and a fuller EIP-4361 style sign-in message are accepted. A `201` consumes the nonce; a failed verify does not, so a retry may reuse it.

The tenant API key is required here too. A correctly signed request without the `x-tenant-api-key` header fails with a generic `500`. A request whose key value is wrong fails with `401` "x-tenant-api-key not present".

Tokens are also delivered as HttpOnly, Secure, SameSite=None cookies: `access_token` with `Max-Age` 900 (15 minutes, `Path=/`) and `refresh_token` with `Max-Age` 2592000 (30 days, `Path=/api/auth`). The cookie values are server-signed strings, not the raw JWTs.

Every verification failure the API can name returns `401` with a message that says why: "EVM signature verification failed: Signature does not match the expected address." when the signature recovers to a different wallet than `data.accountAddress`, "EVM signature verification failed: invalid raw signature length (...)" when the signature is not a valid encoding, "Nonce not found in the message." when `message` has no `Nonce:` line, and "Invalid or expired nonce." when the nonce is unknown or already used. Only two inputs return the generic `500` `{"statusCode": 500, "message": "Internal server error"}`: a missing `x-tenant-api-key` header and a body missing required fields.



## OpenAPI

````yaml /openapi.json post /auth/siwx/verify
openapi: 3.1.0
info:
  title: Vane API
  version: 1.0.0
  summary: Cross-chain and same-chain swaps filled by an open network of solvers.
  description: >-
    Vane turns a swap into an intent: you declare what you want to receive, the
    API returns a per-order deposit address, and a solver fills the order
    against the escrowed deposit ([how solvers fill
    orders](/features/solver-auctions)).


    **Authentication.** Most endpoints accept a tenant API key sent as the
    `x-tenant-api-key` header (accountless integration). The key pre-filled in
    the API playground is a shared public key for evaluating the API. Production
    integrations use their own tenant key, requested from support
    (help@vane.xyz) and kept server-side. A JWT access token from SIWX
    wallet-signature auth also satisfies these endpoints and attributes orders
    to a user account, which adds order history (`GET /v1/orders`) and points.


    **Error behavior.** Errors use the envelope `{statusCode, message, error}`.
    Field order varies and some errors omit `error`. Most business validation
    failures (an unknown token id, a malformed address, an invalid amount, an
    unknown order UUID) return HTTP `500` with a descriptive message, while
    `400` appears only for malformed query parameters. Match on the message text
    for programmatic handling, not just the status codes.


    **The fields `exchange` and `tradePath`** describe how a quote is priced and
    routed across order-book legs. They say nothing about settlement, which
    solvers perform separately.
  contact:
    name: Vane support
    email: help@vane.xyz
    url: https://vane.xyz
  termsOfService: https://vane.xyz/terms-and-conditions
servers:
  - url: https://api.vane.xyz/api
    description: Production
security:
  - tenantApiKey: []
tags:
  - name: Tokens
    description: The token catalog, swap limits, and quotes.
  - name: Orders
    description: Create swap orders and track them to a terminal state.
  - name: Auth
    description: >-
      Optional SIWX wallet-signature authentication. Adds order history and
      points.
paths:
  /auth/siwx/verify:
    post:
      tags:
        - Auth
      summary: Verify wallet signature
      description: >-
        Verifies a wallet signature over the challenge nonce and returns a JWT
        token pair with **HTTP 201**. The signed `message` must contain the line
        `Nonce: <nonce>` using the nonce from `GET /auth/siwx/challenge`; both
        the minimal single-line format and a fuller EIP-4361 style sign-in
        message are accepted. A `201` consumes the nonce; a failed verify does
        not, so a retry may reuse it.


        The tenant API key is required here too. A correctly signed request
        without the `x-tenant-api-key` header fails with a generic `500`. A
        request whose key value is wrong fails with `401` "x-tenant-api-key not
        present".


        Tokens are also delivered as HttpOnly, Secure, SameSite=None cookies:
        `access_token` with `Max-Age` 900 (15 minutes, `Path=/`) and
        `refresh_token` with `Max-Age` 2592000 (30 days, `Path=/api/auth`). The
        cookie values are server-signed strings, not the raw JWTs.


        Every verification failure the API can name returns `401` with a message
        that says why: "EVM signature verification failed: Signature does not
        match the expected address." when the signature recovers to a different
        wallet than `data.accountAddress`, "EVM signature verification failed:
        invalid raw signature length (...)" when the signature is not a valid
        encoding, "Nonce not found in the message." when `message` has no
        `Nonce:` line, and "Invalid or expired nonce." when the nonce is unknown
        or already used. Only two inputs return the generic `500`
        `{"statusCode": 500, "message": "Internal server error"}`: a missing
        `x-tenant-api-key` header and a body missing required fields.
      operationId: verifySiwxSignature
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SiwxVerifyRequest'
            example:
              data:
                chainId: '1'
                accountAddress: '0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0'
              message: 'Nonce: b3c1a9e2-4f6d-4a8b-9c2e-1d5f7a3b8c4d'
              signature: >-
                0x4f3c2b1a9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1a0f9e8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c8b7a6f5e4d3c2b1b
      responses:
        '201':
          description: >-
            Signature accepted. The status is `201`, and the JWT token pair is
            also set as HttpOnly cookies.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AuthTokens'
              example:
                accessToken: >-
                  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3YzllNjY3OSJ9.example-access-token
                refreshToken: >-
                  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3YzllNjY3OSJ9.example-refresh-token
        '401':
          description: >-
            Verification failed for a reason the API can name, or the
            `x-tenant-api-key` value is wrong. Branch on `message`: `EVM
            signature verification failed: ...` means the signature does not
            recover to `data.accountAddress` or is not a valid encoding; `Nonce
            not found in the message.` means `message` has no `Nonce:` line;
            `Invalid or expired nonce.` means the nonce is unknown or was
            already used by a `201`; `x-tenant-api-key not present` means the
            header was sent but the key is not valid. None of these consume the
            nonce.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                signatureMismatch:
                  summary: Signature recovers to a different address
                  value:
                    message: >-
                      EVM signature verification failed: Signature does not
                      match the expected address.
                    error: Unauthorized
                    statusCode: 401
                signatureMalformed:
                  summary: Signature is not a valid encoding
                  value:
                    message: >-
                      EVM signature verification failed: invalid raw signature
                      length (argument="signature", value="0xdeadbeef",
                      code=INVALID_ARGUMENT, version=6.15.0)
                    error: Unauthorized
                    statusCode: 401
                nonceLineMissing:
                  summary: 'Message has no Nonce: line'
                  value:
                    message: Nonce not found in the message.
                    error: Unauthorized
                    statusCode: 401
                nonceUsed:
                  summary: Unknown or already used nonce
                  value:
                    message: Invalid or expired nonce.
                    error: Unauthorized
                    statusCode: 401
                wrongTenantKey:
                  summary: x-tenant-api-key sent but not valid
                  value:
                    message: x-tenant-api-key not present
                    error: Unauthorized
                    statusCode: 401
        '500':
          description: >-
            Generic failure with no diagnostic message. Two inputs cause it: a
            correctly signed request with no `x-tenant-api-key` header at all (a
            wrong key value returns `401` instead), and a body missing required
            fields, such as `{}`. Every other verification failure returns `401`
            with a message naming the cause. The nonce is not consumed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                statusCode: 500
                message: Internal server error
      security:
        - tenantApiKey: []
components:
  schemas:
    SiwxVerifyRequest:
      type: object
      description: SIWX verification payload.
      properties:
        data:
          type: object
          description: The signing account.
          properties:
            chainId:
              type: string
              description: >-
                Chain id of the signing wallet, for example `"1"` for Ethereum
                mainnet.
            accountAddress:
              type: string
              description: Address of the signing wallet.
          required:
            - chainId
            - accountAddress
        message:
          type: string
          description: >-
            The exact message that was signed, containing the line `Nonce:
            <nonce>` with the nonce from `GET /auth/siwx/challenge`. Both the
            minimal single-line `Nonce: <nonce>` format and a fuller EIP-4361
            style sign-in message are accepted.
        signature:
          type: string
          description: >-
            Wallet signature over `message` (EVM: `personal_sign` / EIP-191).
            Must recover to `data.accountAddress`.
        referralCode:
          type: string
          description: >-
            Optional referral code to credit the referrer; see [Points and
            referrals](/features/points).
      required:
        - data
        - message
        - signature
    AuthTokens:
      type: object
      description: >-
        JWT token pair. Also delivered as HttpOnly, Secure, SameSite=None
        cookies: `access_token` (15 min, `Path=/`) and `refresh_token` (30 days,
        `Path=/api/auth`). The cookie values are server-signed strings, not the
        raw JWTs, so non-browser clients should use the JSON `accessToken` as a
        Bearer header.
      properties:
        accessToken:
          type: string
          description: >-
            JWT access token. Valid for 15 minutes. Send as `Authorization:
            Bearer <token>`.
        refreshToken:
          type: string
          description: JWT refresh token. Valid for 30 days. Used by `POST /auth/refresh`.
      required:
        - accessToken
        - refreshToken
    Error:
      type: object
      description: >-
        Canonical error envelope. Field order varies and `error` is sometimes
        omitted (the unknown-order `500` returns only `statusCode` and
        `message`); message casing varies too (`Internal Server Error` vs
        `Internal server error`). Most business validation failures return `500`
        with a descriptive message, so match on `message` for programmatic
        handling.
      properties:
        statusCode:
          type: integer
          description: HTTP status code.
        message:
          type: string
          description: >-
            Error description. The most reliable field for distinguishing
            causes.
        error:
          type: string
          description: >-
            HTTP status text, for example `Bad Request`, `Unauthorized`.
            Sometimes absent.
      required:
        - statusCode
        - message
  securitySchemes:
    tenantApiKey:
      type: apiKey
      in: header
      name: x-tenant-api-key
      x-default: c7eccc0aaed64932a85d35658fa55a4fb2d60cd3d2c529cfd643dc676ee82e82
      description: >-
        Vane's shared tenant key. Contact support for a dedicated key if you
        need custom parameters or separate order tracking.

````