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

# Create API key challenge

> Returns the EIP-4361 message your account wallet signs with `personal_sign` to create or rotate one key, bound to that exact request and usable once within 5 minutes.

Requires a wallet session or the Totalis app's own sign-in session, sent with exactly one allowed `Origin` (otherwise `FORBIDDEN`); an API key cannot call it (`UNAUTHENTICATED`).



## OpenAPI

````yaml /hyperliquid/openapi.json post /v1/me/api-key-challenges
openapi: 3.1.1
info:
  title: Totalis Hyperliquid API
  description: >-
    REST API for Totalis singles and parlays on Hyperliquid HIP-4 outcome
    markets, for client integrations and external market makers.


    There are three ways in, each sent as `Authorization: Bearer <credential>`:


    - **Scoped API key** (`ttk_`), for servers, bots and market makers. The
    account owner creates it in the Totalis app, or a wallet-session user
    creates one with `POST /v1/me/api-keys` and a fresh wallet signature (see
    API keys). It acts for one account, yours or a maker's, with only the scopes
    it holds, and needs no `Origin`.

    - **Wallet session**, for users who bring their own wallet to a partner's
    web page. The page signs the wallet in with `POST
    /v1/auth/wallet-challenges` and `POST /v1/auth/wallet-sessions`; the session
    is bound to the page's registered `Origin` and must be sent from it. Pages
    may also label their traffic with their publishable browser key in
    `X-Totalis-Partner: tpb_...`.

    - **Partner server key** (`tpk_`), only for `POST
    /v1/partner/wallet-admissions`, which lets a new wallet open its account
    during the invite-only launch. Server to server, never in a browser, and
    never with an `Origin`.


    Public market reads need no credential. A credential only authenticates:
    every bet, cash-out and withdrawal also carries the wallet or maker
    signature the HyperEVM contract verifies. An external wallet sends `POST
    /v1/core-exits` to move its vault cash to HyperCore before a HyperCore
    order.


    For access, partner credentials (origins, browser and server keys), builder
    codes or staging, email
    [founders@totalis.trade](mailto:founders@totalis.trade).


    Amounts are base-10 strings in native USDC atomic units, large identifiers
    are decimal strings, and every command `POST` requires an `Idempotency-Key`
    header.


    Every error is `{error: {code, message, retry, request_id,
    field_violations}}`. `code` is stable; each operation lists the codes it
    returns per status. `retry` is `NEVER` (stop), `BACKOFF` (resend the same
    request with the same `Idempotency-Key` after `Retry-After` seconds) or
    `REFRESH` (re-read state, then send a new request with a new key).
    `field_violations` names invalid inputs by JSON pointer, such as
    `/legs/0/side`.
  version: 152.0.0-maker-withdrawal
servers:
  - url: https://hip4-api.totalis.trade
    description: Production public edge
security: []
tags:
  - name: Markets
    description: HIP-4 markets, their sides and price history. No API key needed.
  - name: RFQs & Quotes
    description: >-
      RFQs and the quotes that answer them: what a taker calls, then what a
      maker calls.
  - name: Positions
    description: Positions the account holds, or its maker backs.
  - name: Account
    description: >-
      The account a key acts for: identity, balances, activity, operations,
      withdrawals, deposits and HyperCore exits.
  - name: Makers
    description: 'The rest of a maker''s setup after Making: capital and collateral.'
  - name: Partners & wallet sign-in
    description: >-
      A partner admits its users' own wallets, which sign in from the partner's
      pages.
  - name: API keys
    description: >-
      Issue, rotate and revoke scoped keys from a wallet session. An API key
      cannot manage keys.
  - name: Deployment
    description: The contract deployment every signature is made against.
  - name: WebSocket
    description: The authenticated WebSocket for account and maker updates.
paths:
  /v1/me/api-key-challenges:
    post:
      tags:
        - API keys
      summary: Create API key challenge
      description: >-
        Returns the EIP-4361 message your account wallet signs with
        `personal_sign` to create or rotate one key, bound to that exact request
        and usable once within 5 minutes.


        Requires a wallet session or the Totalis app's own sign-in session, sent
        with exactly one allowed `Origin` (otherwise `FORBIDDEN`); an API key
        cannot call it (`UNAUTHENTICATED`).
      operationId: createPersonalApiKeyChallenge
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KeyChallengeRequest'
            example:
              action: CREATE
              request:
                label: Trading bot
                scopes:
                  - account:read
                  - account:stream
                  - quote:accept
                  - rfq:read
                  - rfq:write
                expires_in_days: 30
      responses:
        '201':
          description: >-
            Five-minute, one-use SIWE challenge (purpose=issue_key) the account
            owner signs for the exact key request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeyChallenge'
              example:
                challenge_id: 0199a1b2-c3d4-7e5f-8a6b-7c8d9e0f1a33
                owner: '0x1111111111111111111111111111111111111111'
                message: >-
                  hip4-api.totalis.trade wants you to sign in with your Ethereum
                  account:

                  0x1111111111111111111111111111111111111111


                  Issue a Totalis API key. It grants scoped API access, not
                  financial signing authority.


                  URI: https://hip4-api.totalis.trade/v1/me/api-keys

                  Version: 1

                  Chain ID: 999

                  Nonce:
                  5e1b9d3a7c2f8e4b6a0d1c9f3e7b5a2d8c4f6e1a9b3d7c5f2e8a4b6d0c1f9e3a

                  Issued At: 2026-10-16T12:00:00Z

                  Expiration Time: 2026-10-16T12:05:00Z

                  Request ID: 0199a1b2-c3d4-7e5f-8a6b-7c8d9e0f1a33

                  Resources:

                  - urn:totalis:purpose:issue_key

                  - urn:totalis:environment:production

                  - urn:totalis:vault:0x0cbffe7b40b64d5fd04f1288f4c9fe0fc50e2f83

                  -
                  urn:totalis:account:participant:0199a1b2-c3d4-7e5f-8a6b-7c8d9e0f1a30

                  - urn:totalis:action:create

                  -
                  urn:totalis:request:0xa1b9cf025fe7ed666e08ddd7fe03d7adf02634fb29933a5e85302055f0164567
                expires_at: '2026-10-16T12:05:00Z'
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControlNoStore'
        '400':
          description: >-
            - `INVALID_REQUEST` (retry `NEVER`): The body, a path parameter or a
            header is malformed or fails validation. `field_violations` names
            invalid body fields.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControlNoStore'
        '401':
          description: >-
            No session, or an API key (`ttk_`), which never manages keys.


            - `UNAUTHENTICATED` (retry `NEVER`): The credential is missing,
            invalid, expired or revoked.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControlNoStore'
        '403':
          description: >-
            A missing, repeated or unregistered `Origin`.


            - `FORBIDDEN` (retry `NEVER`): The credential is valid but lacks the
            scope, permission or role this operation needs.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControlNoStore'
        '404':
          description: >-
            - `NOT_FOUND` (retry `NEVER`): The resource does not exist or is not
            visible to this credential.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControlNoStore'
        '409':
          description: >-
            - `IDEMPOTENCY_KEY_REUSED` (retry `NEVER`): The idempotency key was
            already used with different input.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControlNoStore'
        '429':
          description: >-
            - `RATE_LIMITED` (retry `BACKOFF`): The request quota for this
            credential or route is exhausted.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControlNoStore'
            Retry-After:
              $ref: '#/components/headers/ApiKeysRetryAfter'
        '503':
          description: >-
            - `AUTHORIZATION_UNAVAILABLE` (retry `BACKOFF`): Identity or
            credential authority storage is unavailable.

            - `DEPENDENCY_STALE` (retry `BACKOFF`): A projection or upstream the
            request depends on (chain projection, HyperCore state, database) is
            behind its freshness bound or unavailable.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Error'
          headers:
            Cache-Control:
              $ref: '#/components/headers/CacheControlNoStore'
            Retry-After:
              $ref: '#/components/headers/ApiKeysRetryAfter'
      security:
        - walletSession: []
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        format: uuid
        pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
      description: >-
        Lowercase UUIDv7 naming this command, unique per caller and route. A
        retry with the same key and body returns the original result; the same
        key with a different body returns `409 IDEMPOTENCY_KEY_REUSED`.
      example: 0198a6f6-82a5-7abc-9f2a-4f0fb437493e
  schemas:
    KeyChallengeRequest:
      oneOf:
        - type: object
          additionalProperties: false
          properties:
            action:
              const: CREATE
              description: '`CREATE` for a new key.'
            request:
              $ref: '#/components/schemas/UnsignedCreateKeyRequest'
              description: The exact create request, without `proof`, that the owner signs.
          required:
            - action
            - request
          title: Create
        - type: object
          additionalProperties: false
          properties:
            action:
              const: ROTATE
              description: '`ROTATE` to replace a key.'
            key_id:
              $ref: '#/components/schemas/Uuid'
              description: The key to rotate.
            request:
              $ref: '#/components/schemas/UnsignedRotateKeyRequest'
              description: >-
                The exact rotation request, without `proof`, that the owner
                signs.
          required:
            - action
            - key_id
            - request
          title: Rotate
    KeyChallenge:
      type: object
      additionalProperties: false
      properties:
        challenge_id:
          $ref: '#/components/schemas/Uuid'
          description: Send back as `proof.challenge_id`.
        owner:
          $ref: '#/components/schemas/LowercaseNonZeroAddress'
          description: >-
            The address that must sign: the account wallet for a personal
            account, the controller for a maker.
        message:
          type: string
          minLength: 1
          maxLength: 8192
          description: >-
            The EIP-4361 message to sign with `personal_sign`, exactly as
            returned; check its URI, account, action and key before signing.
        expires_at:
          $ref: '#/components/schemas/CommonTimestamp'
          description: When the challenge stops being accepted, 5 minutes after issue.
      required:
        - challenge_id
        - owner
        - message
        - expires_at
      description: >-
        An EIP-4361 (SIWE) message with purpose=issue_key. Its resources bind
        the environment, vault, account, owner version (makers), action, key
        (rotation) and the request digest.
    Error:
      additionalProperties: false
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetail'
          description: The one error this request failed with.
      required:
        - error
      type: object
    UnsignedCreateKeyRequest:
      type: object
      additionalProperties: false
      properties:
        label:
          $ref: '#/components/schemas/KeyLabel'
          description: Your name for the key, 1 to 80 characters.
        scopes:
          $ref: '#/components/schemas/ScopeList'
          description: >-
            Permissions the key holds; each must apply to the account, as the
            capabilities route lists.
        expires_in_days:
          $ref: '#/components/schemas/KeyExpiryDays'
          description: Lifetime in days, 1 to 90, or 0 for no expiry; 30 when omitted.
      required:
        - label
        - scopes
      description: >-
        The exact key request the account owner signs. Scopes must apply to the
        account.
    Uuid:
      type: string
      format: uuid
      pattern: ^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
    UnsignedRotateKeyRequest:
      type: object
      additionalProperties: false
      properties:
        overlap_seconds:
          $ref: '#/components/schemas/RotationOverlapSeconds'
          description: >-
            How long the old key keeps working after the rotation, 0 to 86400
            seconds; 3600 when omitted.
        expires_in_days:
          $ref: '#/components/schemas/KeyExpiryDays'
          description: >-
            The replacement's lifetime in days, 1 to 90, or 0 for no expiry; 30
            when omitted.
      required: []
      description: >-
        The exact rotation request the account owner signs; the replacement
        inherits the old key's account, label and scopes.
    LowercaseNonZeroAddress:
      type: string
      pattern: ^0x[0-9a-f]{40}$
      description: Never `0x0000000000000000000000000000000000000000`.
    CommonTimestamp:
      type: string
      format: date-time
      pattern: ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}([.][0-9]{1,9})?Z$
      maxLength: 30
    ErrorDetail:
      additionalProperties: false
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
          description: Stable registered code; branch on this, not on message or status.
        field_violations:
          description: >-
            Each invalid input by JSON pointer; empty when the error is not
            about a specific input.
          items:
            $ref: '#/components/schemas/FieldViolation'
          type: array
        message:
          description: Human-readable; may change without notice.
          minLength: 1
          type: string
        request_id:
          description: >-
            UUIDv7 of this request, also returned in the X-Request-ID header.
            Quote it when reporting a problem.
          format: uuid
          pattern: >-
            ^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
          type: string
        retry:
          $ref: '#/components/schemas/Retry'
          description: What the caller should do next; fixed by the registry for each code.
      required:
        - code
        - message
        - retry
        - request_id
        - field_violations
      type: object
    KeyLabel:
      type: string
      minLength: 1
      maxLength: 80
    ScopeList:
      allOf:
        - type: array
          items:
            $ref: '#/components/schemas/Scope'
          minItems: 1
          maxItems: 32
          uniqueItems: true
        - {}
        - {}
        - {}
      description: >-
        Replay needs a feed the key can read: account:stream or rfq:read for a
        personal account, vault:read, quote:read or rfq:read for a maker.
    KeyExpiryDays:
      type: integer
      minimum: 0
      maximum: 90
      default: 30
      description: >-
        0 means Forever (no expiry); 1–90 sets a finite lifetime in days.
        Omission defaults to 30.
    RotationOverlapSeconds:
      type: integer
      minimum: 0
      maximum: 86400
      default: 3600
    ErrorCode:
      description: >-
        Stable HTTP error code. Each code has one HTTP status and one retry
        value.
      enum:
        - INVALID_REQUEST
        - INVALID_QUERY
        - INVALID_CURSOR
        - INVALID_IDEMPOTENCY_KEY
        - ORIGIN_REQUIRED
        - SUBPROTOCOL_REQUIRED
        - UNSUPPORTED_BROWSE_CONTRACT
        - UNAUTHENTICATED
        - FORBIDDEN
        - ORIGIN_NOT_ALLOWED
        - ACCESS_REQUIRED
        - NOT_FOUND
        - IDENTITY_NOT_FOUND
        - QUOTE_NOT_FOUND
        - INVITE_CODE_NOT_FOUND
        - IDEMPOTENCY_KEY_REUSED
        - STATE_CONFLICT
        - INVITE_CODE_EXHAUSTED
        - STALE_CURSOR
        - CURSOR_EXPIRED
        - CURSOR_RESET
        - RECURRING_DEFINITION_CONFLICT
        - EMBEDDED_WALLET_CONFLICT
        - SECRET_UNAVAILABLE
        - ROTATION_IN_PROGRESS
        - TELEMETRY_CONFLICT
        - RFQ_NOT_CANCELLABLE
        - RFQ_NOT_OPEN
        - RFQ_GENERATION_STALE
        - RFQ_GENERATION_IN_FLIGHT
        - QUOTE_EXPIRED
        - QUOTE_CANCELLED
        - QUOTE_REPLACED
        - QUOTE_SIGNER_CHANGED
        - QUOTE_CAPACITY_EXCEEDED
        - CORE_ACCOUNT_NOT_READY
        - CORE_ACCOUNT_ACTIVATION_REQUIRED
        - CORE_EXIT_ABANDONED
        - CORE_MOVE_IN_FLIGHT
        - USERNAME_TAKEN
        - USERNAME_ALREADY_SET
        - INVALID_SIGNATURE
        - WALLET_CONTRACT_UNSUPPORTED
        - WRONG_AUTHORITY
        - KEY_LIMIT_REACHED
        - USERNAME_NOT_ALLOWED
        - RATE_LIMITED
        - CONNECTION_LIMIT
        - RFQ_INTENT_LIMIT
        - QUOTE_EXPOSURE_LIMIT
        - DEPENDENCY_STALE
        - AUTHORIZATION_UNAVAILABLE
        - SOURCE_UNAVAILABLE
        - CONTRACT_UNAVAILABLE
        - MARKET_DATA_UNAVAILABLE
        - BROWSE_UNAVAILABLE
        - FINANCIAL_ACTIONS_DISABLED
        - CORE_EXIT_UNAVAILABLE
        - COLLATERAL_REDUCTION_UNAVAILABLE
        - SPONSORSHIP_UNAVAILABLE
        - CONTRACT_SIGNATURE_OVERLOADED
        - QUOTE_EXPOSURE_STALE
        - COMBO_AUTOMATIC_DISABLED
        - EMBEDDED_WALLET_UNAVAILABLE
        - CONNECTION_DRAIN
      type: string
    FieldViolation:
      additionalProperties: false
      properties:
        code:
          description: >-
            REQUIRED: the input is missing. INVALID: it is present but malformed
            or out of range. UNEXPECTED: the input is not accepted here.
          enum:
            - REQUIRED
            - INVALID
            - UNEXPECTED
          type: string
        field:
          description: >-
            JSON pointer into the body, or into the query parameters as one flat
            object for requests without a body, for example /legs/0/stake or
            /limit.
          pattern: ^(/([^~/]|~[01])*)+$
          type: string
      required:
        - field
        - code
      type: object
    Retry:
      description: >-
        NEVER: stop. BACKOFF: resend the same request with the same
        Idempotency-Key after Retry-After. REFRESH: re-read state, then send a
        new request with a new Idempotency-Key.
      enum:
        - NEVER
        - BACKOFF
        - REFRESH
      type: string
    Scope:
      type: string
      enum:
        - identity:read
        - account:read
        - account:stream
        - rfq:read
        - rfq:write
        - quote:write
        - quote:read
        - replay:read
        - vault:read
        - vault:write
        - quote:accept
        - position:cashout
        - funding:deposit
        - funding:withdraw
        - sponsorship:use
        - maker:access:read
        - operations:read
        - operations:submit
      description: >-
        One permission a key can hold. Each operation lists the scopes it
        requires, and a key holds only scopes that apply to its account, as the
        capabilities routes list. `operations:read` is reserved and never
        issued; `maker:access:read` is no longer issued and has no effect on a
        key that still carries it.
  headers:
    CacheControlNoStore:
      description: Never cache API-key management responses.
      required: true
      schema:
        type: string
        const: no-store
    ApiKeysRetryAfter:
      description: Seconds to wait before resending the same request.
      required: true
      schema:
        type: integer
        minimum: 0
  securitySchemes:
    walletSession:
      type: http
      scheme: bearer
      description: >-
        Wallet session: the `access_token` from `POST /v1/auth/wallet-sessions`,
        for a user who brings their own wallet to a partner's web page. It acts
        for that wallet's account with no scope limit, and is bound to the
        `Origin` it signed in from: send exactly one `Origin` header with that
        value, or the request is `ORIGIN_REQUIRED` or `UNAUTHENTICATED`. It
        lasts at most 30 days and ends after 7 idle days or at `DELETE
        /v1/auth/wallet-sessions/current`.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.