openapi: 3.1.0
info:
  title: Silhouette RFQ REST API
  description: Public REST API for Silhouette's RFQ desk
  contact:
    name: Silhouette API support
    url: https://docs.silhouette.exchange
  license:
    name: Proprietary
    url: https://silhouette.exchange/terms
  version: 1.0.0
paths:
  /v1/auth/api-keys:
    get:
      tags:
        - auth
      summary: List the caller's API keys.
      description: |-
        Returns the account's live credentials — those neither revoked nor past their
        expiry — newest first. Metadata only: the secret is never returned, as it
        leaves the server once at issuance. Use this to audit which keys are
        outstanding before revoking one with `DELETE /v1/auth/api-keys/{accessKey}`.
      operationId: listApiKeys
      parameters:
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: The caller's live API keys, newest first.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiKeysResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
    post:
      tags:
        - auth
      summary: SIWE login.
      description: |-
        The sign-in step. The account's wallet signs the SIWE (EIP-4361) message
        embedding the challenge nonce and the Silhouette domain and chain id; on a
        valid signature Silhouette mints an HMAC credential pair — a public access
        key and a base64 secret returned **once** — that authenticates every
        subsequent request. The credential is valid for two weeks by default, or for
        a caller-requested lifetime (`ttlMs`) up to a three-month maximum; a
        longer request is rejected. Issuance is autonomous; there is no manual key
        issuance. Public: the SIWE signature is the authentication.

        A browser request must carry an `Origin` belonging to one of Silhouette's
        own frontends; any other origin is refused, this reference and its explorer
        included. Sign in from the Silhouette app, or from outside a browser, where
        no `Origin` is sent.
      operationId: siweLogin
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LoginRequest"
        required: true
      responses:
        "200":
          description: Login succeeded; a fresh HMAC credential pair is returned.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LoginResponse"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Invalid signature, or an unknown/expired challenge nonce.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: The request's `Origin` is not one of Silhouette's own frontends (`ORIGIN_NOT_ALLOWED`), so it may not mint a credential.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    delete:
      tags:
        - auth
      summary: Revoke all API keys.
      description: |-
        Retires every live key for the caller's account — a sign-out-everywhere. The
        explicit `all=true` query flag is required; a bare `DELETE` on the collection
        is rejected `400`, so an accidental request cannot wipe an account's
        credentials.
      operationId: revokeAllApiKeys
      parameters:
        - name: all
          in: query
          description: Must be `true`; guards the collection-wide revoke against an accidental bare DELETE.
          required: true
          schema:
            type: boolean
          example: true
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "204":
          description: All of the caller's keys were revoked.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
        "400":
          description: The `all=true` flag was missing.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/auth/api-keys/{accessKey}:
    delete:
      tags:
        - auth
      summary: Revoke one API key.
      description: |-
        Retires a single one of the caller's keys by its access key. Scoped to the
        caller's account: a key that does not exist or belongs to another account is
        a 404. The retired key stops authenticating immediately — this is the
        containment action for a leaked secret.
      operationId: revokeApiKey
      parameters:
        - name: accessKey
          in: path
          description: The access key to revoke.
          required: true
          schema:
            type: string
          example: sil_a1b2c3d4
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "204":
          description: The key was revoked.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: No such key for the caller's account.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/auth/challenge:
    post:
      tags:
        - auth
      summary: Request a SIWE login challenge.
      description: |-
        Returns a single-use nonce for the client to embed in the SIWE message it
        signs at `POST /v1/auth/api-keys`. Public.
      operationId: authChallenge
      responses:
        "200":
          description: A single-use login challenge nonce.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChallengeResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/rfq/balances:
    get:
      tags:
        - balances
      summary: Get balances.
      description: Returns the signed account's token balances.
      operationId: getBalances
      parameters:
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Balances for the signed account.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/GetBalancesResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/deposits:
    get:
      tags:
        - funding
      summary: List deposits.
      description: Returns deposits observed and credited by Silhouette for the signed account.
      operationId: listDeposits
      parameters:
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deposits page.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DepositsPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/deposits/{depositId}:
    get:
      tags:
        - funding
      summary: Get a deposit.
      description: |-
        Returns a single deposit owned by the signed account. A chain transaction
        can carry several deposits, so the lookup is by stable deposit ID.

        A deposit belonging to another account answers `404`, exactly as an
        identifier naming nothing does, so the response never confirms that somebody
        else's deposit exists.
      operationId: getDeposit
      parameters:
        - name: depositId
          in: path
          description: Stable, opaque deposit identifier, always carrying the `dep_` prefix.
          required: true
          schema:
            type: string
          example: dep_0193b6f2a4417b118000cde456789012
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Deposit detail.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Deposit"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: No such deposit for this account (`NOT_FOUND`) — either no deposit carries the identifier, or it belongs to another account.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/funding:
    get:
      tags:
        - funding
      summary: Get funding details.
      description: |-
        The chain to send on, the one address every deposit goes to, the
        confirmations a deposit waits out, and the tokens that can be funded.
        A deposit credits the account that **sent** it, so send from the address
        that account signs with: a transfer forwarded by an exchange credits that
        exchange, and nothing here recovers it. A withdrawal always pays out to the
        account's own address. Answers `404` where an operator has not stated these
        details, rather than serving a default that would misdirect funds.
      operationId: getFunding
      responses:
        "200":
          description: Funding details.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FundingInfo"
        "404":
          description: Funding details are not configured.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/rfq/instruments:
    get:
      tags:
        - instruments
      summary: List supported instruments.
      description: |-
        Returns instruments currently tradable through Silhouette, sorted by
        `instrumentId` ascending and cursor-paginated. Each instrument carries the
        latest top of book (`topOfBook`), giving a client a value before it
        subscribes. It is present while a maker is quoting the instrument, and is a
        snapshot rather than proof that quoting is live at the moment of the read —
        consult its `ts` wherever that difference decides something. The live feed
        is the `prices` WebSocket topic.
      operationId: listInstruments
      parameters:
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Supported instruments.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InstrumentsPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/rfq/instruments/{instrumentId}:
    get:
      tags:
        - instruments
      summary: Get a supported instrument.
      description: Instrument ID input is case-insensitive; responses use canonical uppercase IDs.
      operationId: getInstrument
      parameters:
        - name: instrumentId
          in: path
          description: Instrument ID, case-insensitive.
          required: true
          schema:
            type: string
          example: hype-usdc-spot
      responses:
        "200":
          description: Supported instrument.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Instrument"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Not found.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/rfq/ledger:
    get:
      tags:
        - balances
      summary: Get the account's ledger.
      description: |-
        Returns the signed account's ledger entries, newest first: the append-only
        audit rows behind its balances. Each entry carries a signed `delta`
        (positive credit, negative debit), the token, the `source` event class, and
        the time the entry was recorded. Cursor-paginated.
      operationId: getLedger
      parameters:
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Ledger page.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LedgerPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/maker/quotes:
    get:
      tags:
        - rfq
      summary: List the maker's quotes.
      description: |-
        Returns the authenticated maker's own quotes, cursor-paginated and
        optionally filtered by `status`. A maker polls this to learn outcomes: a
        `SELECTED` quote on a settlement mode that settles with a Silhouette-signed
        Permit2 carries that authorisation to relay on-chain; modes whose
        settlement needs no Silhouette signature carry no permit fields.

        This collection is also where a maker reads the promised deliveries it
        owes. `?status=PENDING_DELIVERY` returns exactly the quotes with an open
        obligation, each carrying `windowStartsAt` and `windowEndsAt` and the owed
        token and amount on its `makerPays` leg. The `PENDING_DELIVERY` frames on
        the WebSocket `quoteStatus` channel are the prompt to deliver, but they are
        a live-socket signal a maker can miss across a restart, a dropped
        connection, or a failed delivery attempt, so a promised maker polls this
        filter on a cadence comfortably inside its own window and delivers anything
        still listed. A delivery observed after `windowEndsAt` is refunded, never
        settled. Requires a maker-scoped token.
      operationId: listMakerQuotes
      parameters:
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
        - name: status
          in: query
          description: Filter by quote status. `PENDING_DELIVERY` is the maker's own open delivery obligations.
          required: false
          schema:
            $ref: "#/components/schemas/QuoteStatus"
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: The maker's quotes (a `SELECTED` quote carries its permit when its settlement mode uses one).
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MakerQuotesPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Token is not maker-scoped.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
    post:
      tags:
        - rfq
      summary: Submit a quote.
      description: |-
        Submits a firm quote for an open RFQ. The quote is validated before it is
        accepted: a `202` means it passed validation and joined the RFQ's
        collection (in the rare case the verdict does not arrive in time, or is
        lost after the quote has been taken in, a `202` means accepted pending
        validation), a `400` carries the code for the refusal, and a `404` covers
        an RFQ the maker may not quote. A submission
        needs no idempotency key: a maker holds one live quote per adapter on an
        RFQ, so a fresh submission replaces the terms standing for that adapter
        rather than adding a second quote. On a `202` pending validation,
        reconcile against `GET /v1/rfq/maker/quotes` before re-submitting, since
        what already stands may be the terms you would replace. The quote then
        competes with every other maker's quote for the same RFQ and wins on best
        price; the winner is selected at the RFQ deadline. Requires a maker-scoped
        token.
      operationId: createMakerQuote
      parameters:
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateMakerQuoteRequest"
        required: true
      responses:
        "202":
          description: "Quote validated and accepted into the RFQ's collection — or accepted pending validation, when the verdict did not arrive in time or was lost after the quote had been taken in. A pending-validation quote may or may not have joined the auction, so read the maker's quotes to find out rather than re-submitting: a submission needs no idempotency key, because a fresh one replaces whatever stands for the same adapter."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
        "400":
          description: "The quote was refused and nothing was persisted. `ADAPTER_NOT_OPERATED`: the `settlementMode` names an adapter the maker does not operate. `SETTLEMENT_PAYLOAD_REQUIRED`, `SETTLEMENT_PAYLOAD_UNEXPECTED`, `SETTLEMENT_PAYLOAD_INVALID`: the `settlement` payload is missing, sent on a mode that settles from the maker's own inventory, or not the shape its mode routes it to. `QUOTE_REJECTED` carries the validation verdict as its message: an RFQ no longer open, a quote arriving at or after `auctionEndsAt`, an `instrumentId` or `side` disagreeing with the RFQ's, or a payload the third party or the quote's own economics refuse. A late quote's verdict carries both `auctionEndsAt` and the instant the quote was judged, read from the same clock, so a maker can tell a slow submission from a skewed one."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Token is not maker-scoped.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: "No such RFQ for this maker (`NOT_FOUND`): either no RFQ carries the `rfqId`, or it sits on an instrument the maker is not approved for. Answering both alike keeps the response from confirming an RFQ outside the maker's approvals; `GET /v1/rfq/maker/requests` lists every RFQ it may quote."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "413":
          description: Request body exceeds the 1 MiB limit.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: "The quote never reached validation, or validation faulted before recording anything: nothing was persisted and a retry is safe."
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/maker/quotes/{quoteId}:
    get:
      tags:
        - rfq
      summary: Get one of the maker's quotes.
      description: |-
        Returns one of the authenticated maker's own quotes, by id, in any
        lifecycle status — a `SELECTED` quote carries its settlement
        authorisation, exactly as on the maker's quote list. Another maker's
        quote answers `404`, exactly as an identifier naming nothing does, never
        confirming it exists. Requires a maker-scoped token.
      operationId: getMakerQuote
      parameters:
        - name: quoteId
          in: path
          description: Opaque quote identifier, always carrying the `qt_` prefix.
          required: true
          schema:
            type: string
          example: qt_0193b6f19a207e448000def987654321
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: The maker's quote (a `SELECTED` quote carries its permit when its settlement mode uses one).
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MakerQuote"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Token is not maker-scoped.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: No such quote for this maker (`QUOTE_NOT_FOUND`) — either no quote carries the identifier, or it belongs to another maker.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/maker/quotes/{quoteId}/cancel:
    post:
      tags:
        - rfq
      summary: Cancel a quote.
      description: |-
        Retracts one of the maker's own still-`SUBMITTED` quotes before the RFQ's
        auction selects a winner — and so before the maker holds any signed permit.
        The quote is marked `CANCELLED` and dropped from the auction, the maker
        (`quoteStatus`) and the RFQ owner (`quotes`) are notified over the
        WebSocket, and the response carries the `CANCELLED` quote. To re-price
        instead, submit a fresh quote for the same RFQ, which replaces the prior
        terms. A quote frozen into a delivery cascade cannot be cancelled until the
        cascade resolves. Requires a maker-scoped token.
      operationId: cancelMakerQuote
      parameters:
        - name: quoteId
          in: path
          description: Opaque quote identifier, always carrying the `qt_` prefix.
          required: true
          schema:
            type: string
          example: qt_0193b6f19a207e448000def987654321
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Quote cancelled; dropped from the auction.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MakerQuote"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Token is not maker-scoped.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Quote not found.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Quote is no longer open to cancel, or is a live cascade-chain member on an RFQ awaiting delivery.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/maker/requests:
    get:
      tags:
        - rfq
      summary: List open RFQs to quote.
      description: |-
        Returns the `PENDING` RFQs on the instruments the authenticated maker is
        approved for, cursor-paginated. A maker polls this at its own
        cadence, then submits quotes for the ones it wants. Each item carries
        `autoAccept` so the maker can tell whether a quote can be accepted before
        the deadline. Requires a maker-scoped token.
      operationId: listMakerRequests
      parameters:
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Open RFQs the maker may quote.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OpenRfqsPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Token is not maker-scoped.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/quotes/{quoteId}:
    get:
      tags:
        - rfq
      summary: Get a quote.
      description: |-
        Returns one quote on the signed account's own RFQ, by id, in any
        lifecycle status — the taker-facing view, with no Permit2 authorisation
        and no maker identity. A quote on another account's RFQ answers `404`,
        exactly as an identifier naming nothing does, never confirming it
        exists. A maker reads its own quote from
        `GET /v1/rfq/maker/quotes/{quoteId}` instead.
      operationId: getQuote
      parameters:
        - name: quoteId
          in: path
          description: Opaque quote identifier, always carrying the `qt_` prefix.
          required: true
          schema:
            type: string
          example: qt_0193b6f19a207e448000def987654321
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: The quote, as the taker sees it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Quote"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: No such quote for this account (`QUOTE_NOT_FOUND`) — either no quote carries the identifier, or it sits on another account's RFQ.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/requests:
    get:
      tags:
        - rfq
      summary: List RFQ requests.
      description: |-
        Returns the signed account's RFQs, most recent first — every lifecycle
        state by default, or one state when `status` is given. A settled trade is
        an RFQ in `SETTLED`, so `status=SETTLED` is the executed-trade view.
      operationId: listRfqRequests
      parameters:
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
        - name: status
          in: query
          description: Filter by RFQ lifecycle status. `SETTLED` selects the executed trades.
          required: false
          schema:
            $ref: "#/components/schemas/RfqStatus"
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: RFQs page.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RfqsPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
    post:
      tags:
        - rfq
      summary: Submit an RFQ.
      description: |-
        Accepts an RFQ for orchestration. The RFQ is created in `PENDING`
        and settles asynchronously; poll `GET /v1/rfq/requests/{id}`
        or the WebSocket for the outcome. Omitting `autoAccept` takes the default
        (`false`), so the taker must explicitly accept one quote; clients
        that want deadline selection send `autoAccept: true`.

        Idempotent on `idempotencyKey`: a submission repeating a key the account
        has already used — a network replay or a client retry — creates nothing
        and returns `202` with the original RFQ's id and its current status. Send
        a fresh UUID per intended submission.
      operationId: createRfqRequest
      parameters:
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateRfqRequestInput"
        required: true
      responses:
        "202":
          description: Accepted for orchestration.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateRfqRequestResponse"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Insufficient balance (`INSUFFICIENT_BALANCE`), or the request exceeds what one part of the balance can cover (`EXCEEDS_WITHDRAWABLE`).
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "413":
          description: Request body exceeds the 1 MiB limit.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: Solvency-locked.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/requests/{id}:
    get:
      tags:
        - rfq
      summary: Get an RFQ.
      description: |-
        Returns a single RFQ owned by the signed account, in any lifecycle
        state.
      operationId: getRfqRequest
      parameters:
        - name: id
          in: path
          description: Opaque RFQ identifier, always carrying the `rfq_` prefix.
          required: true
          schema:
            type: string
          example: rfq_0193b6f17c107d6c8000abc123456789
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: RFQ detail.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Rfq"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Not found.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/requests/{id}/accept:
    post:
      tags:
        - rfq
      summary: Accept a quote.
      description: |-
        Explicitly accepts one quote on the taker's own open RFQ, in either
        selection mode: on an `autoAccept: true` request this is the early exit
        from the auction the close would otherwise fill. Acceptance commits to
        that quote so the RFQ can settle before the auction ends; funds not
        already locked at submission are locked here. It is valid only while
        the request is `PENDING` and the chosen quote is still `SUBMITTED` and
        inside its acceptance window. An acceptance whose commit fails before
        anything reaches the settlement venue fails only the chosen quote and
        leaves the request `PENDING`, so another quote may be accepted until
        the auction ends; a perp matched execution that broadcast and produced
        no fill ends the whole request `FAILED`. The maker retains the option
        not to deliver, in which case the RFQ fails (cascading to another maker
        where one exists).
      operationId: acceptRfqQuote
      parameters:
        - name: id
          in: path
          description: Opaque RFQ identifier, always carrying the `rfq_` prefix.
          required: true
          schema:
            type: string
          example: rfq_0193b6f17c107d6c8000abc123456789
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AcceptRfqQuoteRequest"
        required: true
      responses:
        "202":
          description: Quote accepted; settlement proceeds.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Rfq"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Request or quote not found.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: "`QUOTE_VERSION_MISMATCH` when `expectedVersion` no longer matches the quote — re-read the quotes and accept the version you want. Otherwise the request is not `PENDING`, the quote is no longer acceptable, the balance is insufficient (`INSUFFICIENT_BALANCE`), or the acceptance exceeds what one part of the balance can cover (`EXCEEDS_WITHDRAWABLE`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "413":
          description: Request body exceeds the 1 MiB limit.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/requests/{id}/cancel:
    post:
      tags:
        - rfq
      summary: Cancel an RFQ.
      description: |-
        Cancels the taker's own still-open (`PENDING`) RFQ, releasing any funds
        locked at submission (auto-accept locks at submit; a taker-driven request
        holds no lock until acceptance) and driving it to `CANCELLED`. Valid only
        while the request is `PENDING`: once a winner is selected or the request is
        settling, the maker may hold a signed permit, so Silhouette, not the taker,
        owns the outcome. Returns the `CANCELLED` RFQ.
      operationId: cancelRfqRequest
      parameters:
        - name: id
          in: path
          description: Opaque RFQ identifier, always carrying the `rfq_` prefix.
          required: true
          schema:
            type: string
          example: rfq_0193b6f17c107d6c8000abc123456789
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: RFQ cancelled; any locked funds released.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Rfq"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Request not found.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Request is no longer open to cancel.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/requests/{id}/quotes:
    get:
      tags:
        - rfq
      summary: List the quotes on an RFQ.
      description: |-
        Returns the quotes on the taker's own RFQ in every lifecycle status, most
        recent first, cursor-paginated, and optionally filtered by `status`. While
        the auction is open, `?status=SUBMITTED` is exactly the competing set the
        taker may accept — available in either selection mode. After the auction,
        each quote stays listed with its outcome, so a `quoteUpdate` event missed
        on the WebSocket is recoverable here. The view names no maker and carries
        no Permit2 authorisation — those are the maker's side of the trade.
      operationId: listRfqRequestQuotes
      parameters:
        - name: id
          in: path
          description: Opaque RFQ identifier, always carrying the `rfq_` prefix.
          required: true
          schema:
            type: string
          example: rfq_0193b6f17c107d6c8000abc123456789
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
        - name: status
          in: query
          description: Filter by quote status. `SUBMITTED` is the still-acceptable competing set.
          required: false
          schema:
            $ref: "#/components/schemas/QuoteStatus"
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Quotes on the RFQ.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QuotesPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Not found.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/tokens:
    get:
      tags:
        - instruments
      summary: List supported tokens.
      description: |-
        Returns tokens supported through Silhouette, each with its on-chain address
        and decimal precision. This is not a mirror of every token known to
        Hyperliquid. Every token field on the API is a symbol, so this collection is
        the symbol-to-address join: it is how a maker turns the symbol on a quote's
        `makerPays` or `makerReceives` leg into the contract it relays a settlement
        authorisation against. Sorted by `symbol` ascending and cursor-paginated.
      operationId: listTokens
      parameters:
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Supported tokens.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokensPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/rfq/tokens/{symbol}:
    get:
      tags:
        - instruments
      summary: Get a supported token.
      description: Symbol input is case-insensitive; responses use canonical uppercase symbols.
      operationId: getToken
      parameters:
        - name: symbol
          in: path
          description: Token symbol, case-insensitive.
          required: true
          schema:
            type: string
          example: hype
      responses:
        "200":
          description: Supported token.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Token"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Not found.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
  /v1/rfq/withdrawals:
    get:
      tags:
        - funding
      summary: List withdrawals.
      description: Returns withdrawals for the signed account.
      operationId: listWithdrawals
      parameters:
        - name: limit
          in: query
          description: Page size. Defaults to 50, maximum 100.
          required: false
          schema:
            type: integer
            format: int32
            minimum: 0
        - name: cursor
          in: query
          description: |-
            Opaque pagination cursor from a previous page's `nextCursor`. Clients
            must not parse or construct it.
          required: false
          schema:
            type: string
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Withdrawals page.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WithdrawalsPage"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
    post:
      tags:
        - funding
      summary: Create a withdrawal.
      description: |-
        Reserves the funds and queues an asynchronous on-chain withdrawal.

        Idempotent on `idempotencyKey`: a request repeating a key the account has
        already used — a network replay or a client retry — reserves nothing and
        returns `202` with the original withdrawal's id and its current status.
        Send a fresh UUID per intended withdrawal.
      operationId: createWithdrawal
      parameters:
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWithdrawalRequest"
        required: true
      responses:
        "202":
          description: Withdrawal accepted for asynchronous processing.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreateWithdrawalResponse"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: Insufficient balance (`INSUFFICIENT_BALANCE`), or the request exceeds what one part of the balance can cover (`EXCEEDS_WITHDRAWABLE`).
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "413":
          description: Request body exceeds the 1 MiB limit.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "503":
          description: The withdrawal token is not fully onboarded yet — it has no core registry linkage (`SERVICE_UNAVAILABLE`).
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
  /v1/rfq/withdrawals/{withdrawalId}:
    get:
      tags:
        - funding
      summary: Get a withdrawal.
      description: |-
        Returns a single withdrawal owned by the signed account.

        A withdrawal belonging to another account answers `404`, exactly as an
        identifier naming nothing does, so the response never confirms that somebody
        else's withdrawal exists.
      operationId: getWithdrawal
      parameters:
        - name: withdrawalId
          in: path
          description: Opaque withdrawal identifier, always carrying the `wd_` prefix.
          required: true
          schema:
            type: string
          example: wd_0193b6f3c8827e5a8000bcd345678901
        - name: Silhouette-API-Timestamp
          in: header
          description: |-
            The signing time in unix milliseconds; a request whose timestamp is more
            than 30 seconds from now is rejected. The window bounds staleness,
            not replay: a funds-committing request is guarded against replay by the
            `idempotencyKey` in its signed body. Each request's timestamp must be
            unique per access key, and clients must assign timestamps in increasing
            order at issuance (arrival order is not constrained — the wire may
            reorder them).
          required: true
          schema:
            type: integer
            format: int64
          example: 1700000000000
        - name: Silhouette-API-Signature
          in: header
          description: |-
            Base64 HMAC-SHA256, under the session secret, of the canonical request
            string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty
            for a GET).
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Withdrawal detail.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Withdrawal"
        "400":
          description: "Malformed request (`INVALID_REQUEST`): a body, query, or path parameter that fails validation, or a request body that is not valid UTF-8."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: "Authentication failed: a missing `Authorization` header or an unknown/revoked/expired access key (`UNAUTHORIZED`); a missing or malformed `Silhouette-API-Timestamp` or `Silhouette-API-Signature` header (`SIGNATURE_HEADER_INVALID`); a timestamp older than the 30-second window (`SIGNATURE_EXPIRED`) or ahead of it (`SIGNATURE_NOT_YET_VALID`); or a signature that does not match the recomputed HMAC (`SIGNATURE_INVALID`)."
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: No such withdrawal for this account (`NOT_FOUND`) — either no withdrawal carries the identifier, or it belongs to another account.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: Rate limit exceeded (`RATE_LIMITED`) — the per-IP pre-authentication limit, or the per-account limit on a signed request. Carries a `Retry-After` header.
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying.
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "500":
          description: Silhouette failed to serve the request (`INTERNAL_SERVER_ERROR`). Nothing about the request was necessarily rejected; quote the `X-Request-Id` header when reporting it.
          headers:
            X-Request-Id:
              schema:
                type: string
              description: The request's correlation id, stamped on every response; quote it when reporting an issue.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
      security:
        - hmac: []
components:
  schemas:
    AcceptRfqQuoteRequest:
      type: object
      description: |-
        Body of `POST /v1/rfq/requests/{id}/accept`: the quote the taker is
        accepting, chosen from `GET /v1/rfq/requests/{id}/quotes`.
      required:
        - quoteId
        - expectedVersion
      properties:
        expectedVersion:
          type: integer
          format: int64
          description: |-
            The `version` the quote carried when you read it. The acceptance is
            refused with `QUOTE_VERSION_MISMATCH` if the maker re-priced in
            between, rather than executing at terms you never saw.
        perpTakerOrder:
          type: object
          description: |-
            For a HyperCore perp matched-execution quote only: the taker's
            pre-signed immediate-or-cancel order at the maker's quoted price and
            size, broadcast against the maker's resting post-only order. A signed
            HyperLiquid exchange action exactly as produced by
            HyperLiquid-compatible signing tooling, relayed to the venue verbatim —
            the venue owns the payload format, and it is validated fail-closed
            without being re-shaped, so its fields are deliberately not part of
            this contract. Omitted for every custodial adapter.
        quoteId:
          $ref: "#/components/schemas/QuoteId"
    ApiError:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: |-
            Stable machine-readable error code, e.g. `INVALID_REQUEST`,
            `INSUFFICIENT_BALANCE`, `NOT_FOUND`. Open set — new codes are additive.
          example: INVALID_REQUEST
        details:
          $ref: "#/components/schemas/ErrorDetails"
          description: Optional validation/diagnostic context.
        message:
          $ref: "#/components/schemas/PublicReason"
          description: |-
            Human-readable account of the failure, for a person reading it. Not a
            stable machine contract: branch on `code`, and expect the wording to
            change. It states why the request was refused and carries no identifier
            out of Silhouette's records, so it is safe to surface to an end user;
            where a refusal has field-level detail, that detail is in `details`.
    ApiKey:
      type: object
      description: |-
        One of the caller's live API keys, as returned by the listing. The secret is
        absent by construction — it is returned once at issuance and never again — so
        the listing exposes only the public access key and its lifetime.
      required:
        - accessKey
        - createdAt
        - expiresAt
      properties:
        accessKey:
          type: string
          description: "Public access key — the `Authorization: Bearer` token for this credential."
          example: sil_a1b2c3d4
        createdAt:
          $ref: "#/components/schemas/UnixMillis"
          description: When the key was minted.
        expiresAt:
          $ref: "#/components/schemas/UnixMillis"
          description: When the key expires.
    ApiKeysResponse:
      type: object
      description: |-
        The caller's live API keys. Not paginated: an account holds few credentials,
        and the listing is the audit view before revoking one.
      required:
        - keys
      properties:
        keys:
          type: array
          items:
            $ref: "#/components/schemas/ApiKey"
          description: The caller's live keys, newest first.
    Balance:
      type: object
      description: |-
        One token's balance on the account, split into the three sub-balances that
        together make up the account's claim on it. Amounts are canonical decimal
        strings in human token units, and the whole claim is their sum.

        Read it from `GET /v1/rfq/balances`, or subscribe to `balances` and receive
        the same object inside every snapshot.
      required:
        - token
        - available
        - locked
        - pending
        - withdrawable
      properties:
        available:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            The account's unreserved claim on this token. A withdrawal or RFQ that
            must complete in one request may accept less than this aggregate.
        locked:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            Held against a committed RFQ and unspendable until it resolves.
            Settlement consumes the hold; an RFQ that ends without settling releases
            it back to `available`.
        pending:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            Value credited for a committed trade the maker has not delivered yet
            (a promised-mode settlement inside its delivery window). Owed to the
            account but not withdrawable or tradeable until delivery makes it
            available; a failed delivery reverses it and the account is made
            whole. `"0"` outside a delivery window.
        token:
          $ref: "#/components/schemas/TokenSymbol"
          description: |-
            Canonical symbol of the token this line is for. Resolve it to an ERC-20
            contract, when one is needed on-chain, via `GET /v1/rfq/tokens`.
        withdrawable:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            The largest amount one request can withdraw. Size a request against this
            figure: one above it is refused however much `available` holds.

            A snapshot, not a reservation. It moves with the account's activity.
    ChallengeResponse:
      type: object
      description: |-
        The single-use nonce a SIWE login message must embed, issued by
        `POST /v1/auth/challenge`. It expires shortly and is consumed by the login
        that presents it, so each sign-in fetches its own.
      required:
        - nonce
      properties:
        nonce:
          type: string
          format: uuid
          description: A single-use nonce to embed in the SIWE login message. Expires shortly.
          example: 0193b6f1-7c10-7d6c-8000-abc123456789
    CreateMakerQuoteRequest:
      type: object
      description: A maker's firm quote for an open RFQ.
      required:
        - rfqId
        - instrumentId
        - side
        - settlementMode
        - makerPays
        - makerReceives
      properties:
        acceptableForMs:
          $ref: "#/components/schemas/DurationMillis"
          description: |-
            How long the quote stays acceptable, milliseconds from `receivedAt`.
            Optional: omitted, no maker cutoff applies and the quote stands
            until the request's auction closes. Rejected below the venue's
            floor (1000 ms by default); no upper bound — a window
            outlasting the auction simply never bites. A duration rather than an
            instant, so the maker's clock never shortens its own window. The
            settlement deadline is not submitted: the request publishes it, and it
            bounds when a win may settle.
        instrumentId:
          $ref: "#/components/schemas/InstrumentId"
        makerPays:
          $ref: "#/components/schemas/MakerQuoteLeg"
          description: What the maker pays out — the leg the taker receives.
        makerReceives:
          $ref: "#/components/schemas/MakerQuoteLeg"
          description: What the maker receives — the leg the taker pays.
        promised:
          type: boolean
          description: |-
            Submit this quote as a HyperCore promised delivery (`HYPERCORE_SPOT`
            only): instead of settling from held inventory, the maker promises an
            on-venue spot-send to the omnibus and joins the RFQ's delivery cascade,
            ranked best-price-first, with its obligation bounded by its registered
            delivery window rather than the quote's expiry. Defaults to `false` —
            an ordinary inventory quote.
        rfqId:
          $ref: "#/components/schemas/RfqId"
        settlement:
          type: object
          description: |-
            The settlement payload the declared adapter needs, routed by
            `settlementMode`: an object whose shape that adapter's own integration
            guide defines. `XSTOCKS_XCHANGE` carries the Backed-signed swap message
            and its signature; `HYPERCORE_PERP` carries the maker's two pre-signed
            HyperLiquid exchange actions, a post-only order at the quoted price and
            size and the `cancelByCloid` that clears it. Each is relayed to its
            third party verbatim: that party owns the format, and it is validated
            fail-closed at intake without being re-shaped, so its fields are
            deliberately not part of this contract. Mandatory on the modes that
            relay such a payload, refused on the modes that settle from the maker's
            own inventory.
        settlementMode:
          $ref: "#/components/schemas/SettlementMode"
          description: |-
            The settlement adapter this quote settles through. Mandatory: the
            maker must operate the declared adapter — a quote naming one it does
            not operate is rejected. A maker may hold one live quote per adapter
            on an RFQ; re-submitting the same adapter replaces that quote's terms.
        side:
          $ref: "#/components/schemas/Side"
    CreateRfqRequestInput:
      type: object
      description: |-
        Body of `POST /v1/rfq/requests`: the instrument, side, size, and price bound
        the taker wants quoted, the auction window, and the selection mode.
      required:
        - instrumentId
        - side
        - baseQty
        - idempotencyKey
      properties:
        autoAccept:
          type: boolean
          description: |-
            Quote-selection mode. When `true` the best conforming quote is
            auto-accepted at the auction end — unless the taker accepts one
            sooner with `POST /v1/rfq/requests/{id}/accept` — and the taker's
            funds are locked at submission. When `false` (the default) only an
            explicit acceptance before the auction ends fills the request; funds
            are locked at acceptance, not at submission. A perp request cannot
            auto-accept: matched execution needs the taker's signed order at
            acceptance, so `true` on a perp instrument is rejected.
          default: false
        baseQty:
          $ref: "#/components/schemas/PositiveDecimalString"
        idempotencyKey:
          $ref: "#/components/schemas/IdempotencyKey"
          description: |-
            Client-generated idempotency key (UUID), unique per submission. Because
            it rides in the HMAC-signed body, a replayed request necessarily carries
            the same key and is answered with the original RFQ instead of executing
            again; a retry that wants a new RFQ generates a fresh key.
        instrumentId:
          $ref: "#/components/schemas/InstrumentId"
          description: |-
            Canonical instrument identifier (e.g. `XTSLA-USDC-SPOT`) — the same
            value the WebSocket surfaces carry as `instrumentId`.
        quoteLimit:
          oneOf:
            - type: "null"
            - $ref: "#/components/schemas/PositiveDecimalString"
              description: |-
                The taker's price bound, in the quote token: on `BUY` the most the
                taker will pay for `baseQty`, on `SELL` the least it will accept. A
                quote outside it cannot win, and the bound is never shown to makers.
                Required when `autoAccept` is `true`, because selection then accepts on
                the taker's behalf and has no other bound to work to; omitting it there
                is rejected with `QUOTE_LIMIT_REQUIRED`. A taker-driven request may omit
                it and accept a quote explicitly, in which case no bound is applied.
        side:
          $ref: "#/components/schemas/Side"
        windowMs:
          $ref: "#/components/schemas/DurationMillis"
          description: |-
            How long the RFQ stays open for quotes — the taker's lever on the
            speed/price-discovery tradeoff: a short window settles a time-sensitive
            order quickly; a longer one gathers more competitive quotes. Clamped
            server-side to the operator's minimum and maximum, so a request outside
            that range is honoured at the nearer bound rather than rejected. Omit it
            to take the operator's default window, which always lands inside it.
    CreateRfqRequestResponse:
      type: object
      description: |-
        The RFQ a submission created, by id and lifecycle status. The auction runs
        asynchronously; read the outcome from `GET /v1/rfq/requests/{id}` or the
        `rfqStatus` WebSocket topic.
      required:
        - rfqId
        - status
        - auctionEndsAt
      properties:
        auctionEndsAt:
          $ref: "#/components/schemas/UnixMillis"
          description: |-
            The server-selected auction deadline, derived from the requested
            (clamped) `windowMs` or from the operator default when it was omitted.
            Quotes are gathered until this time; with `autoAccept` the winner is
            selected here. Returned so the client knows the effective expiry
            without re-deriving it.
        rfqId:
          $ref: "#/components/schemas/RfqId"
        status:
          $ref: "#/components/schemas/RfqStatus"
          description: |-
            `PENDING` on a fresh submission. On an idempotent replay this is the
            original RFQ's current lifecycle status, which may have progressed past
            `PENDING` (e.g. `QUOTED`, `SETTLED`, `CANCELLED`).
    CreateWithdrawalRequest:
      type: object
      description: |-
        Body of `POST /v1/rfq/withdrawals`: the token and amount to withdraw. The
        destination is not a field — funds always return to the account's registered
        address.
      required:
        - token
        - amount
        - idempotencyKey
      properties:
        amount:
          $ref: "#/components/schemas/PositiveDecimalString"
        idempotencyKey:
          $ref: "#/components/schemas/IdempotencyKey"
          description: |-
            Client-generated idempotency key (UUID), unique per request. Because it
            rides in the HMAC-signed body, a replayed request necessarily carries
            the same key and is answered with the original withdrawal instead of
            reserving again; a retry that wants a second withdrawal generates a
            fresh key.
        token:
          $ref: "#/components/schemas/TokenSymbol"
          description: |-
            Canonical symbol of the token to withdraw, as `GET /v1/rfq/tokens`
            lists it and as the withdrawal reads it back. A symbol the registry
            does not know is rejected.
    CreateWithdrawalResponse:
      type: object
      description: |-
        The withdrawal a create request reserved, by id and lifecycle status. The
        on-chain send happens asynchronously; read the outcome from
        `GET /v1/rfq/withdrawals/{withdrawalId}`.
      required:
        - withdrawalId
        - status
      properties:
        status:
          $ref: "#/components/schemas/WithdrawalStatus"
          description: |-
            `PENDING` on a fresh request. On an idempotent replay this is the
            original withdrawal's current lifecycle status, which may have
            progressed past `PENDING` (e.g. `PROCESSING`, `COMPLETED`, `FAILED`).
        withdrawalId:
          $ref: "#/components/schemas/WithdrawalId"
    DecimalString:
      type: string
      description: Exact non-negative canonical decimal string in human-readable units. Responses use canonical formatting with no scientific notation, separators, leading plus sign, unnecessary leading zeros, trailing fractional zeros, or decimal point for integer values. Request parsing accepts and normalises semantically equivalent trailing fractional zeros such as `1.0` or `0.000`; leading plus signs, unnecessary leading zeros, whitespace, scientific notation, missing integer digits, and missing fractional digits are rejected. Once the trailing zeros are stripped, a request value carrying more fractional digits than the field's token supports is rejected as `AMOUNT_OVER_PRECISE`, whose `message` names the maximum — never silently truncated.
      examples:
        - "10.5"
      maxLength: 64
      pattern: ^(0|[1-9][0-9]*)(\.[0-9]+)?$
    Deposit:
      type: object
      description: |-
        A taker-facing view of a deposit. A deposit appears here once observed
        on-chain; `status` is `PENDING` until credited and `COMPLETED` once
        credited.
      required:
        - depositId
        - token
        - amount
        - txHash
        - status
        - createdAt
        - updatedAt
      properties:
        amount:
          $ref: "#/components/schemas/DecimalString"
        createdAt:
          $ref: "#/components/schemas/UnixMillis"
        depositId:
          $ref: "#/components/schemas/DepositId"
        status:
          $ref: "#/components/schemas/DepositStatus"
        token:
          $ref: "#/components/schemas/TokenSymbol"
        txHash:
          $ref: "#/components/schemas/TxHash"
        updatedAt:
          $ref: "#/components/schemas/UnixMillis"
    DepositId:
      type: string
      description: "Opaque resource identifier, always carrying the `dep_` prefix. The whole string is the identity: store it and send it back verbatim, and never parse, split, or construct one — everything after the prefix is an internal encoding that may change."
      examples:
        - dep_0193b6f2a4417b118000cde456789012
      maxLength: 128
      minLength: 1
    DepositStatus:
      type: string
      description: |-
        Public deposit lifecycle. A deposit reads `PENDING` from the moment
        Silhouette has observed the on-chain transfer, and `COMPLETED` once the
        funds are credited to the account and spendable.

        This is an open set: values may be added, so a client must tolerate one it
        does not recognise rather than failing to parse the response.
      enum:
        - PENDING
        - COMPLETED
    DepositsPage:
      type: object
      description: A cursor-paginated page of deposits.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/Deposit"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
    DurationMillis:
      type: integer
      format: int64
      description: A duration in milliseconds.
      examples:
        - 60000
      minimum: 1
    ErrorDetails:
      type: object
      properties:
        errors:
          type: array
          items:
            $ref: "#/components/schemas/ValidationErrorDetail"
          description: Field-level validation errors; present for `INVALID_REQUEST`.
        withdrawable:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            The largest amount one request could have drawn when this one was
            evaluated, in the units the balance line uses. Present for
            `EXCEEDS_WITHDRAWABLE`, so the next request is sized from the refusal
            rather than guessed. A snapshot, not a reservation.
    ErrorResponse:
      type: object
      description: |-
        Nested error envelope: `{ "error": { "code", "message", "details"? } }`.
        `code` is a stable, machine-branchable identifier (an open, additive set);
        `message` is human-readable and not a stable contract.
      required:
        - error
      properties:
        error:
          $ref: "#/components/schemas/ApiError"
    EvmAddress:
      type: string
      description: EVM address, 0x-hex (EIP-55 checksummed on output).
      examples:
        - "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
      pattern: ^0x[0-9a-fA-F]{40}$
    FundingInfo:
      type: object
      description: |-
        What a client needs to move money in and out without asking a human: the
        chain to send on, the one address to send to, and how long a deposit takes
        to credit. Which tokens it can send is `GET /v1/rfq/tokens`, which is
        paged and so keeps this response a fixed size.

        Two behaviours are fixed rather than parameterised. A deposit credits the
        account that *sent* it, so one sent from an exchange credits that sender.
        A withdrawal always pays to the account's own address, so a compromised
        key cannot redirect funds.
      required:
        - chainId
        - depositAddress
      properties:
        chainId:
          type: integer
          format: int64
          description: |-
            EVM chain id every deposit and withdrawal moves on. Send on any other
            chain and the funds are not recoverable.
          minimum: 0
        depositAddress:
          $ref: "#/components/schemas/EvmAddress"
          description: The single address every deposit is sent to, whoever is depositing.
        depositConfirmations:
          type: integer
          format: int32
          description: |-
            Block confirmations a deposit waits out before it credits. Absent when
            the deployment has not stated one.
          minimum: 0
    GetBalancesResponse:
      type: object
      description: "`GET /v1/rfq/balances` response: the account's balances, one entry per token."
      required:
        - balances
      properties:
        balances:
          type: array
          items:
            $ref: "#/components/schemas/Balance"
    IdempotencyKey:
      type: string
      format: uuid
      description: "Client-generated idempotency key (UUID), unique per account. A request Silhouette has already seen under this key is answered with the originally created RFQ or withdrawal instead of executing again: the key rides inside the HMAC-signed body, so a replay necessarily repeats it. A retry that wants a new resource generates a fresh key."
      examples:
        - 0193b6f1-7c10-7d6c-8000-abc123456789
    Instrument:
      type: object
      description: An instrument tradable through Silhouette.
      required:
        - instrumentId
        - baseToken
        - quoteToken
        - instrumentType
        - baseQtyIncrement
        - quotePrecision
        - takerFeeBps
        - settlementGasPayer
      properties:
        baseQtyIncrement:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            The step a base quantity is expected to land on, in human token units.
            Round a size down to a multiple of this before submitting. Defaults to
            the base token's own smallest unit, down to twenty-eight decimal places.
            Guidance for sizing rather than a limit on submission: a request off the
            step is accepted, and a maker may then decline it.
        baseToken:
          $ref: "#/components/schemas/TokenSymbol"
          description: Canonical base-token symbol.
        instrumentId:
          $ref: "#/components/schemas/InstrumentId"
          description: Canonical instrument identifier (`BASE-QUOTE-TYPE`, uppercase).
        instrumentType:
          $ref: "#/components/schemas/InstrumentType"
        minBaseQty:
          $ref: "#/components/schemas/PositiveDecimalString"
          description: |-
            Smallest base quantity this instrument is quoted in, in human token
            units. Absent when it sets no minimum. Guidance for sizing rather than
            a limit on submission: a smaller request is accepted, and a maker may
            then decline it.
        minQuoteNotional:
          $ref: "#/components/schemas/PositiveDecimalString"
          description: |-
            Smallest order value this instrument is quoted in, in the quote token.
            Absent when it sets no minimum. Guidance for sizing, as with
            `minBaseQty`.
        quotePrecision:
          type: integer
          format: int32
          description: |-
            Decimal places a quote-token amount may carry — the precision a price
            or `quoteLimit` is read at. Defaults to the quote token's own decimals,
            capped at twenty-eight.
          example: 6
          maximum: 28
          minimum: 0
        quoteToken:
          $ref: "#/components/schemas/TokenSymbol"
          description: Canonical quote-token symbol.
        settlementGasPayer:
          $ref: "#/components/schemas/SettlementGasPayer"
          description: |-
            Who pays the chain gas that settles a trade of this instrument. Read it
            before budgeting for a fill: on `MAKER` a taker signs and spends nothing
            on chain.
        takerFeeBps:
          type: integer
          format: int32
          description: |-
            The taker fee, in basis points of the taker's pay-side leg. It is baked
            into the `takerTotal` a quote publishes rather than billed separately,
            so this is the disclosure, not a second charge.
          example: 5
          minimum: 0
        topOfBook:
          $ref: "#/components/schemas/TopOfBook"
          description: |-
            Latest top of book the exchange holds for the instrument, so a client
            has a value before it opens the live feed. Present while a maker is
            quoting it. It is a snapshot rather than proof that quoting is live at
            the moment of the read, so consult `ts` wherever that difference
            decides something; the live feed is the `prices` topic on the
            WebSocket.
    InstrumentId:
      type: string
      description: "Canonical instrument identifier: uppercase `BASE-QUOTE-TYPE` (e.g. `XTSLA-USDC-SPOT`). Every response carries the canonical form. A request may send a lenient variant such as `xTSLA/USDC/SPOT`, which is normalised on the way in — which is why the pattern here admits either separator and either case, and surrounding whitespace, rather than the canonical form alone. The length cap applies to the normalised value. Anything that is not a well-formed `BASE-QUOTE-TYPE` naming a known instrument type is rejected."
      examples:
        - XTSLA-USDC-SPOT
      maxLength: 128
      pattern: ^\s*[A-Za-z0-9]+[-/][A-Za-z0-9]+[-/](?:[Ss][Pp][Oo][Tt]|[Pp][Ee][Rr][Pp])\s*$
    InstrumentType:
      type: string
      description: |-
        The kind of an instrument — the discriminator that lets
        `/v1/rfq/instruments` carry instruments of different settlement models
        under one shape, and the trailing component of an [`InstrumentId`].
      enum:
        - SPOT
        - PERP
    InstrumentsPage:
      type: object
      description: A cursor-paginated page of supported instruments.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/Instrument"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
    LedgerEntry:
      type: object
      description: |-
        One ledger entry: the audit row behind a balance change. `delta` is a
        signed decimal string (positive credit, negative debit).
      required:
        - ledgerId
        - token
        - delta
        - source
        - createdAt
      properties:
        createdAt:
          $ref: "#/components/schemas/UnixMillis"
          description: When the entry was recorded, in unix milliseconds.
        delta:
          type: string
          description: |-
            Signed balance change in human-readable token units: positive for a
            credit, negative for a debit.
          example: "-100"
          pattern: ^-?(0|[1-9][0-9]*)(\.[0-9]+)?$
        ledgerId:
          type: string
          format: uuid
          description: Stable identifier of the entry.
          example: 0193b6f1-7c10-7d6c-8000-abc123456789
        source:
          $ref: "#/components/schemas/LedgerSource"
          description: The class of event that produced the entry.
        token:
          $ref: "#/components/schemas/TokenSymbol"
          description: The token whose balance changed.
    LedgerPage:
      type: object
      description: A cursor-paginated page of ledger entries.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/LedgerEntry"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
    LedgerSource:
      type: string
      description: |-
        The event class behind a ledger entry: which kind of economic event changed
        the account's balance. `DEPOSIT` and `WITHDRAWAL` are funding movements,
        `FILL` is a settled trade, and `ADJUSTMENT` is an operator correction.

        This is an open set: values may be added, so a client must tolerate one it
        does not recognise rather than failing to parse the response.
      enum:
        - DEPOSIT
        - FILL
        - WITHDRAWAL
        - ADJUSTMENT
    LoginRequest:
      type: object
      description: |-
        Body of `POST /v1/auth/api-keys`: the SIWE (EIP-4361) message the account's
        wallet signed, its signature, and an optional lifetime for the credential
        the login mints.
      required:
        - message
        - signature
      properties:
        message:
          type: string
          description: |-
            The SIWE (EIP-4361) message, embedding the challenge nonce and the
            Silhouette domain and chain id.
          example: |-
            app.silhouette.exchange wants you to sign in with your Ethereum account:
            0xbc851C042A01624435BcB885f7A2398d18dEbF3b

            URI: https://app.silhouette.exchange
            Version: 1
            Chain ID: 999
            Nonce: 0193b6f1-7c10-7d6c-8000-abc123456789
            Issued At: 2025-01-01T00:00:00Z
        signature:
          type: string
          description: The wallet's signature over `message`, as a 0x-prefixed hex string.
          example: "0x3c1ce03a187418f3e435b899521b3076a1a2e3ed0a3c900b1078794c99f04e79135b5ba0ee3042c479c3918168a472b4bae00c8684dbb59b0e3111fddc79cbee1b"
          pattern: ^0x[0-9a-fA-F]+$
        ttlMs:
          oneOf:
            - type: "null"
            - $ref: "#/components/schemas/DurationMillis"
              description: |-
                Requested credential lifetime. Optional: omit for the two-week default.
                Capped at three months (90 days); a longer request is rejected rather
                than silently shortened.
    LoginResponse:
      type: object
      description: |-
        The credential pair a successful SIWE login mints, with the account it acts
        for. The `secret` is returned here and never again, so a client that loses
        it logs in for a fresh pair.
      required:
        - userId
        - account
        - accessKey
        - secret
        - expiresAt
      properties:
        accessKey:
          type: string
          description: "Public access key — sent on every request as `Authorization: Bearer`."
          example: sil_a1b2c3d4
        account:
          $ref: "#/components/schemas/EvmAddress"
        expiresAt:
          $ref: "#/components/schemas/UnixMillis"
          description: When the credential expires.
        makerId:
          type: string
          description: |-
            Present when the account is a registered active market maker, so the
            client knows to expose the maker surface.
        secret:
          type: string
          description: Base64 secret — returned once. Sign requests with it; never send it.
          example: c2lsaG91ZXR0ZS1leGFtcGxlLXNlY3JldC1uZXZlci1zZW50
        userId:
          $ref: "#/components/schemas/UserId"
    MakerQuote:
      type: object
      description: |-
        One of the maker's quotes. The Permit2 `spender`, `permitSignature`, and
        `settlementDeadline` appear once the quote is `SELECTED` — that is the
        authorisation the maker relays to settle on its wrapper — as does
        `deliveryDeadline` when the win is a promised delivery, and all four stay on
        the quote through the statuses that follow. A quote that owes such a
        delivery also carries the window bounds it must deliver within, so a maker
        reads its obligations as `?status=PENDING_DELIVERY` on this collection. The
        `quoteStatus` WebSocket topic streams the same view, with one addition: a
        quote held in reserve behind the winner of a HyperCore delivery cascade
        carries its rank in that cascade there.
      required:
        - rfqId
        - quoteId
        - instrumentId
        - side
        - status
        - makerPays
        - makerReceives
        - receivedAt
      properties:
        acceptableForMs:
          $ref: "#/components/schemas/DurationMillis"
          description: |-
            The maker's acceptance window as submitted: milliseconds from
            `receivedAt` during which the quote stays acceptable. Absent when
            the submission named none — no maker cutoff.
        deliveryDeadline:
          $ref: "#/components/schemas/UnixSeconds"
          description: |-
            Deadline for a promised delivery, in unix **seconds** — the exact value
            the Silhouette-signed authorisation binds, to pass unchanged into the
            settlement call. Present from `SELECTED` onward on a quote whose
            settlement is a promised delivery rather than an atomic fill, which is
            what lets a maker recover the deadline over REST while it is
            `PENDING_DELIVERY`.
        instrumentId:
          $ref: "#/components/schemas/InstrumentId"
        makerPays:
          $ref: "#/components/schemas/QuoteLeg"
          description: What the maker pays out — the leg the taker receives.
        makerReceives:
          $ref: "#/components/schemas/QuoteLeg"
          description: What the maker receives — the leg the taker pays.
        permitSignature:
          type: string
          description: |-
            Silhouette-signed Permit2 signature (0x-hex), to relay on-chain.
            Present only when `SELECTED`.
        perpFillAvgPx:
          $ref: "#/components/schemas/PositiveDecimalString"
          description: |-
            Perp matched execution only: the realised average fill price for the
            settled size, in the quote token, as a decimal string. Present
            alongside `perpFilledSize`.
        perpFilledSize:
          $ref: "#/components/schemas/PositiveDecimalString"
          description: |-
            Perp matched execution only: the taker's realised fill size, as a
            decimal string. A partially filled IOC settles for this size, not the
            quoted size — present on a `SETTLED` perp quote, absent otherwise.
        quoteId:
          $ref: "#/components/schemas/QuoteId"
        receivedAt:
          $ref: "#/components/schemas/UnixMillis"
        rfqId:
          $ref: "#/components/schemas/RfqId"
        settledAt:
          $ref: "#/components/schemas/UnixMillis"
          description: |-
            The instant this quote's RFQ settled. Present once that RFQ has
            settled against this quote, absent otherwise — with `txHash`, this is
            what makes a listing filtered to `SETTLED` a reconcilable record of
            fills rather than a status feed.
        settlementDeadline:
          $ref: "#/components/schemas/UnixSeconds"
          description: |-
            The settlement deadline: the latest instant this win may settle. Unix
            seconds — an on-chain-native value (see `UnixSeconds`), the same figure
            the quote request published. An inventory win's Permit2 is signed with
            exactly this; an xchange win's is signed with the expiry Backed stamped
            on its own swap, at or before it. Present from `SELECTED` onward; absent
            on a quote that never won.
        side:
          $ref: "#/components/schemas/Side"
        spender:
          $ref: "#/components/schemas/EvmAddress"
          description: Permit2 spender (the maker's wrapper). Present only when `SELECTED`.
        status:
          $ref: "#/components/schemas/QuoteStatus"
        txHash:
          $ref: "#/components/schemas/TxHash"
          description: |-
            The transaction that settled this quote's RFQ, where the adapter
            stamped one. Absent until that settlement.
        windowEndsAt:
          $ref: "#/components/schemas/UnixMillis"
          description: |-
            The instant the window closes: the deadline by which the delivery must
            land for the trade to settle. A delivery observed later is refunded and
            the quote reads `DEFAULTED`. Present alongside `windowStartsAt`.
        windowStartsAt:
          $ref: "#/components/schemas/UnixMillis"
          description: |-
            The instant the maker's promised delivery window opens. Present while
            the quote is `PENDING_DELIVERY` and absent on every quote that owes no
            delivery.
    MakerQuoteLeg:
      type: object
      description: "One leg of a maker's submitted quote: a token and a positive amount."
      required:
        - token
        - amount
      properties:
        amount:
          $ref: "#/components/schemas/PositiveDecimalString"
          description: Leg amount in human token units, as a positive decimal string.
        token:
          $ref: "#/components/schemas/TokenSymbol"
          description: |-
            Canonical symbol of this leg's token, which must be the instrument's
            base or quote token in the role the RFQ's side gives it: on a `BUY` the
            maker pays the base token and receives the quote token, and on a `SELL`
            the reverse. A symbol the registry does not know, or one that is not
            this leg's token, is rejected on submission. A maker that needs the
            ERC-20 contract itself, to relay the settlement authorisation, reads it
            from the registry at `GET /v1/rfq/tokens`.
    MakerQuotesPage:
      type: object
      description: A page of the maker's quotes, cursor-paginated.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/MakerQuote"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
    OpenRfq:
      type: object
      description: |-
        One open RFQ a maker may quote, as delivered on the `openRfqs` topic and
        listed by `GET /v1/rfq/maker/requests`. It carries what a maker needs to
        price the trade and nothing more: the taker's quote limit stays private to
        the taker.
      required:
        - id
        - instrumentId
        - side
        - baseQty
        - autoAccept
        - auctionEndsAt
        - settlementDeadline
        - createdAt
      properties:
        auctionEndsAt:
          $ref: "#/components/schemas/UnixMillis"
          description: |-
            The instant the auction ends — the RFQ stops accepting quotes and the
            winner is selected.
        autoAccept:
          type: boolean
          description: |-
            `true` if the winning quote auto-fills at the deadline unless the
            taker accepts sooner; `false` if the taker must accept a quote
            themselves.
        baseQty:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            The quantity to quote on, in base-token units. On a `SELL` the taker fee
            is carved out of the base the taker delivers, so this is what the maker
            actually buys and a quote priced on any other quantity cannot win; on a
            `BUY` it is the full size the taker asked for.
        createdAt:
          $ref: "#/components/schemas/UnixMillis"
          description: The instant the taker submitted the RFQ.
        id:
          $ref: "#/components/schemas/RfqId"
          description: |-
            Opaque identifier of the RFQ to quote, carrying the `rfq_` prefix. Send
            it back verbatim as the quote's `rfqId`; never parse it.
        instrumentId:
          $ref: "#/components/schemas/InstrumentId"
        settlementDeadline:
          $ref: "#/components/schemas/UnixSeconds"
          description: |-
            The settlement deadline for every quote on this RFQ, in unix
            seconds: `auctionEndsAt` rounded up to the next whole second plus
            the operator's settlement headroom. The latest instant a winning
            quote can settle: an inventory win is signed with exactly this, and an
            xchange win is signed with the expiry Backed stamped on its own swap,
            which must fall at or before it. Never derive this value.
        side:
          $ref: "#/components/schemas/Side"
    OpenRfqsPage:
      type: object
      description: A page of open RFQs, cursor-paginated like every other v1 list.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/OpenRfq"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
    PositiveDecimalString:
      type: string
      description: Exact positive decimal string in human-readable units for action request fields. Zero and zero-equivalent values such as `0.0` are invalid for these fields.
      examples:
        - "10.5"
      maxLength: 64
      pattern: ^([1-9][0-9]*(\.[0-9]+)?|0\.[0-9]*[1-9][0-9]*)$
    PublicReason:
      type: string
      description: "Human-readable reason for a failure or refusal, written for the account reading it. A fixed phrase, not a stable machine contract: display it, and expect the wording to change."
      examples:
        - no quote arrived before the deadline
    Quote:
      type: object
      description: |-
        One competing quote on a taker's own RFQ, as the taker sees it when choosing
        which to accept. It carries the two legs, the all-in cost, and the quote's
        own lifecycle status; it carries no Permit2 authorisation, because that is
        the winning maker's settlement secret, and it names no maker, because the
        auction is anonymous to the taker.

        Read it from `GET /v1/rfq/requests/{id}/quotes`, or subscribe to `quotes`
        and receive the same object as each maker responds.
      required:
        - rfqId
        - quoteId
        - version
        - instrumentId
        - side
        - status
        - makerPays
        - makerReceives
        - takerTotal
        - receivedAt
      properties:
        acceptableForMs:
          $ref: "#/components/schemas/DurationMillis"
          description: |-
            The maker's acceptance window: how long the quote stays acceptable,
            counted from `receivedAt`. A re-quote restamps `receivedAt`, so the
            window restarts with each submission. Absent when the maker named
            none — the quote then stands until the auction closes. The auction
            close bounds acceptance either way.
        builderFeeTenthBp:
          type: integer
          format: int32
          description: |-
            The market's builder fee rate, in tenths of a basis point of the
            taker's notional (10 = 1 basis point). The venue charges it on the
            taker's order and credits Silhouette's builder address for the
            environment, which Silhouette publishes to integrators out of band and
            holds for every market on it. Sign the accept's pre-signed order with
            that address and this exact rate. Absent when the market has no builder
            fee, and the order must then carry no attribution. The venue caps a
            perp builder fee at 0.1%, so a rate never exceeds 100.
          maximum: 100
          minimum: 1
        instrumentId:
          $ref: "#/components/schemas/InstrumentId"
        makerPays:
          $ref: "#/components/schemas/QuoteLeg"
          description: What the maker pays out — the leg the taker receives.
        makerReceives:
          $ref: "#/components/schemas/QuoteLeg"
          description: What the maker receives — the leg the taker pays.
        perpFillAvgPx:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            Perp matched execution only: the realised average fill price for the
            settled size, in the quote token, as a decimal string. Present alongside
            `perpFilledSize`.
        perpFilledSize:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            Perp matched execution only: the taker's realised fill size, as a decimal
            string. A partially filled immediate-or-cancel order settles for this
            size, not the quoted size — present on a `SETTLED` perp quote, absent
            otherwise.
        quoteId:
          $ref: "#/components/schemas/QuoteId"
          description: |-
            Opaque identifier of this quote, carrying the `qt_` prefix. Send it back
            verbatim when accepting; never parse it.
        receivedAt:
          $ref: "#/components/schemas/UnixMillis"
          description: |-
            The time Silhouette received the quote, for ordering competing quotes
            and anchoring `acceptableForMs`.
        rfqId:
          $ref: "#/components/schemas/RfqId"
          description: |-
            The RFQ this quote answers. A quote is a resource in its own right, so
            it names its RFQ rather than relying on where it was read from.
        side:
          $ref: "#/components/schemas/Side"
        status:
          $ref: "#/components/schemas/QuoteStatus"
        takerTotal:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            The taker's all-in cost in the pay token: `makerReceives.amount` plus the
            taker fee, baked into one price. This is what the taker pays for
            `makerPays.amount` of the received asset; the fee is never a separate
            line.
        version:
          type: integer
          format: int64
          description: |-
            Which version of this quote's terms you are reading. A maker re-pricing
            its quote keeps the same `quoteId` and advances this, so the id alone
            does not tell one set of terms from the next.

            Send it back as `expectedVersion` when accepting and the acceptance is
            refused, rather than executed at terms you never saw, if the quote moved
            in between. Opaque and monotonic: compare for equality, never order or
            arithmetic.
    QuoteId:
      type: string
      description: "Opaque resource identifier, always carrying the `qt_` prefix. The whole string is the identity: store it and send it back verbatim, and never parse, split, or construct one — everything after the prefix is an internal encoding that may change."
      examples:
        - qt_0193b6f19a207e448000def987654321
      maxLength: 128
      minLength: 1
    QuoteLeg:
      type: object
      description: |-
        One leg of a two-leg quote: a token and the amount of it. A quote is
        described by the two legs the maker commits to, what it pays out and what it
        receives, so a leg's meaning never flips with the RFQ's side.
      required:
        - token
        - amount
      properties:
        amount:
          $ref: "#/components/schemas/DecimalString"
          description: Leg amount in human token units, as a decimal string.
        token:
          $ref: "#/components/schemas/TokenSymbol"
          description: |-
            Canonical symbol of this leg's token — the instrument's base or quote
            token. Resolve it to an ERC-20 contract, when one is needed on-chain,
            via `GET /v1/rfq/tokens`.
    QuoteStatus:
      type: string
      description: |-
        Lifecycle of an RFQ quote as clients see it. A quote arrives `SUBMITTED` and
        competes in the open auction. At the auction deadline one quote is
        `SELECTED` and the rest are `NOT_SELECTED`, or every quote is `EXPIRED` when
        the window closed with no conforming quote; a maker may instead retract a
        still-`SUBMITTED` quote, taking it to `CANCELLED`. A winner that settles
        atomically goes straight to `SETTLED`, and one whose settlement does not
        complete ends `FAILED`.

        A winning promised quote goes to `PENDING_DELIVERY` for as long as its maker
        owes the on-chain delivery, and from there to `SETTLED` on delivery or
        `DEFAULTED` when the maker misses its window. `DEFAULTED` is distinct from
        `FAILED` because it is attributable to the maker and carries consequences.

        This is an open set: values may be added, so a client must tolerate one it
        does not recognise rather than failing to parse the frame or response.
      enum:
        - SUBMITTED
        - SELECTED
        - NOT_SELECTED
        - EXPIRED
        - PENDING_DELIVERY
        - SETTLED
        - FAILED
        - DEFAULTED
        - CANCELLED
    QuotesPage:
      type: object
      description: A page of the competing quotes on a taker's RFQ, cursor-paginated.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/Quote"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
    Rfq:
      type: object
      description: |-
        One RFQ as its owning taker sees it, across the whole lifecycle. A trade is
        an RFQ in `SETTLED` carrying the settlement transaction hash, not a separate
        resource. Amounts are canonical decimal strings and every instant is unix
        milliseconds.

        Read it from `GET /v1/rfq/requests/{id}`, or subscribe to `rfqStatus` and
        receive the same object on each transition — the push carries the full view
        rather than an id and a status, so a subscriber never refetches to render
        it.
      required:
        - id
        - instrumentId
        - side
        - baseQty
        - status
        - autoAccept
        - createdAt
        - auctionEndsAt
      properties:
        auctionEndsAt:
          $ref: "#/components/schemas/UnixMillis"
          description: |-
            The instant the auction ends: quoting closes and the winner is
            selected. The RFQ itself lives on through settlement — the pair with
            the request's `windowMs` ("send a duration, read back the instant").
        autoAccept:
          type: boolean
          description: |-
            `true` when the request auto-fills at the deadline, though the taker
            may still accept a quote before then; `false` when only an explicit
            accept fills it. A client uses this to decide whether to offer an
            accept action.
        baseQty:
          $ref: "#/components/schemas/DecimalString"
        createdAt:
          $ref: "#/components/schemas/UnixMillis"
          description: The instant the taker submitted the RFQ.
        executedPayAmount:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            What you paid all in, in the token you paid with: the winning quote's
            taker-side leg plus `takerFee`. Present once the RFQ settles, absent on
            every other status and on a perp cross, whose realised fill lives on
            the quote as `perpFilledSize` and `perpFillAvgPx`. This is the executed amount, not the amount
            requested — a `quoteLimit` bounds it, it does not name it.
        executedRecvAmount:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            What you received, in the token you received. Present once the RFQ
            settles, on the same terms as `executedPayAmount`. Divide it by `executedPayAmount` for the all-in rate; both
            amounts are exact, so no rounding is imposed on that ratio here.
        failureCode:
          $ref: "#/components/schemas/RfqFailureCode"
          description: |-
            The same failure as a stable code. Branch on this; the reason's
            wording is not a contract. Absent on a request that ended before the
            field existed, so treat it as optional even beside a `failureReason`.
        failureReason:
          $ref: "#/components/schemas/PublicReason"
          description: |-
            Why an RFQ ended without settling, worded for a person. Present on a
            `FAILED` request, and on a `CANCELLED` one the taker called off;
            absent on every other status.
        id:
          $ref: "#/components/schemas/RfqId"
          description: |-
            Opaque identifier of this RFQ, carrying the `rfq_` prefix. Echo it
            verbatim on the endpoints that take an RFQ id; never parse it.
        instrumentId:
          $ref: "#/components/schemas/InstrumentId"
        quoteLimit:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            The taker's own price bound, in the quote token. Never shown to makers.
            Absent on a taker-driven RFQ submitted without one, where the taker
            accepts a quote explicitly and no bound is applied.
        quotedAt:
          $ref: "#/components/schemas/UnixMillis"
          description: The instant a winning quote was committed. Absent until one is.
        settledAt:
          $ref: "#/components/schemas/UnixMillis"
          description: The instant the trade settled. Absent until it does.
        side:
          $ref: "#/components/schemas/Side"
        status:
          $ref: "#/components/schemas/RfqStatus"
        takerFee:
          $ref: "#/components/schemas/DecimalString"
          description: |-
            The taker fee on this RFQ, in the pay token, frozen when the pay side
            was committed. `executedPayAmount` already includes it, so this says
            what that price was made of rather than naming a second charge. Absent
            until the RFQ settles: an RFQ that ends without settling refunds it
            with the rest of the lock, and a perp cross charges none.
        txHash:
          $ref: "#/components/schemas/TxHash"
          description: The settlement transaction hash. Present once the RFQ is `SETTLED`.
    RfqFailureCode:
      type: string
      description: |-
        Why an RFQ ended without a settlement.

        Present exactly on a `FAILED` or `CANCELLED` request, and absent on one that
        ended before this field existed. `CANCELLED_BY_TAKER` is the only code a
        `CANCELLED` request carries; every other value rides a `FAILED` one.

        Branch on this rather than `failureReason`, whose wording may change. An
        open set: tolerate a value you do not recognise rather than failing.

        Every value says no trade happened except `EXECUTION_UNCONFIRMED`, which
        says the outcome is unknown — check your venue position before re-trading.

        **No quote won the auction.**
        - `NO_QUOTES` — no maker quoted before the auction deadline. Market hours
          may affect quoting: an instrument tied to an underlying market session
          draws the most quotes while that market is open, whatever the session
          was at the deadline.
        - `NO_CONFORMING_QUOTE` — quotes arrived, but none matched the request's
          terms.
        - `PRICE_LIMIT_NOT_MET` — quotes arrived and were well-formed, but none came
          within the `quoteLimit` the request set.
        - `NOT_ACCEPTED` — a taker-driven request reached its deadline with no quote
          accepted. Nothing was locked and nothing is owed.

        **A quote won, and then the trade did not complete.**
        - `QUOTE_EXPIRED` — the winning quote's deadline passed before its
          settlement was observed.
        - `DELIVERY_WINDOW_EXHAUSTED` — a winning quote's delivery window closed with
          no delivery. The taker is made whole.
        - `SETTLEMENT_FAILED` — a maker, its adapter, or the venue refused, at
          selection or after. The funds are released.
        - `EXECUTION_NOT_FILLED` — a matched execution produced no fill for the
          taker: the venue said so, or the cross never started. Safe to try again.
        - `EXECUTION_UNCONFIRMED` — a matched execution's outcome is **not known**.
          Check your position at the venue before opening new exposure; retrying
          blind can double it against a fill that did land.

        **The request could not proceed.**
        - `INSUFFICIENT_BALANCE` — the account could not cover the side the
          acceptance committed it to.
        - `EXCEEDS_WITHDRAWABLE` — the claim covers the request but no single
          part of the balance does. Resize from the bound the refusal reported.
        - `TRADING_PAUSED` — trading is paused, so the request could not proceed.
        - `INSTRUMENT_UNAVAILABLE` — the instrument is unknown or not currently
          tradable.
        - `INTERNAL_ERROR` — something on our side failed while handling the
          request. No trade happened and anything held against it is released.

        **The taker ended it.**
        - `CANCELLED_BY_TAKER` — the taker called its own still-open request off.
      enum:
        - NO_QUOTES
        - NO_CONFORMING_QUOTE
        - PRICE_LIMIT_NOT_MET
        - NOT_ACCEPTED
        - QUOTE_EXPIRED
        - DELIVERY_WINDOW_EXHAUSTED
        - SETTLEMENT_FAILED
        - EXECUTION_NOT_FILLED
        - EXECUTION_UNCONFIRMED
        - INSUFFICIENT_BALANCE
        - EXCEEDS_WITHDRAWABLE
        - TRADING_PAUSED
        - INSTRUMENT_UNAVAILABLE
        - INTERNAL_ERROR
        - CANCELLED_BY_TAKER
    RfqId:
      type: string
      description: "Opaque resource identifier, always carrying the `rfq_` prefix. The whole string is the identity: store it and send it back verbatim, and never parse, split, or construct one — everything after the prefix is an internal encoding that may change."
      examples:
        - rfq_0193b6f17c107d6c8000abc123456789
      maxLength: 128
      minLength: 1
    RfqStatus:
      type: string
      description: |-
        Lifecycle of an RFQ. `PENDING` is the request while it collects quotes.
        Selection at the deadline (or an explicit taker accept) moves it to
        `QUOTED` when the winning quote carries a settlement artefact to observe —
        a signed Permit2 permit or an on-chain escrow — or straight to the terminal
        `SETTLED` when the winning quote's settlement mode settles atomically in
        the selection transaction. A winning promised quote with no artefact to
        watch instead moves to `PENDING_DELIVERY`.

        Both in-flight states are non-terminal, and they wait on different things.
        A `QUOTED` request waits for its settlement to be observed — the permit
        spent, or the escrow funded — and reaches `SETTLED` when it is, `FAILED`
        when the deadline passes without it. A `PENDING_DELIVERY` request waits for
        the maker to deliver within its window, and reaches `SETTLED` on the
        observed delivery, `FAILED` once the window is exhausted. A trade is an RFQ
        in `SETTLED`.

        The two unsettled terminals divide on whether the trade was called off.
        `CANCELLED` means a party ended it deliberately before settlement: in this
        version, the taker cancelling its own still-`PENDING` request. `FAILED`
        covers every other ending without a settlement — no quotes arrived, none
        conformed, no quote was accepted before the deadline, a committed permit
        deadline passed with no fill, or a delivery window was exhausted. Which
        one it was is carried by `failureCode`, not by the status; locked funds
        are released either way.

        This is an open set: values may be added, so a client must tolerate one it
        does not recognise rather than failing to parse the frame or response.
      enum:
        - PENDING
        - QUOTED
        - PENDING_DELIVERY
        - SETTLED
        - FAILED
        - CANCELLED
    RfqsPage:
      type: object
      description: A cursor-paginated page of RFQs.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/Rfq"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
    SettlementGasPayer:
      type: string
      description: |-
        Who pays the chain gas that puts a trade on chain. `MAKER`: the winning
        maker broadcasts the settlement call and pays its gas, so the taker signs
        and spends nothing on chain. `TAKER`: the taker broadcasts and pays.
        `EXCHANGE`: Silhouette broadcasts and pays.
      enum:
        - MAKER
        - TAKER
        - EXCHANGE
    SettlementMode:
      type: string
      description: |-
        The settlement adapter a quote settles through. Every quote names one,
        mandatory, and the declaration is validated against the adapters the maker
        operates before the quote is accepted. The binding is a property of the
        quote rather than of the maker or the instrument: a maker may operate
        several adapters and hold one live quote per adapter on the same RFQ, and
        the value carried on a quote is the one that settles it.

        The mode also determines whether the quote carries a `settlement` payload —
        the modes that relay a third party's signed artefact require one, and the
        modes that settle from the maker's own holdings refuse one — and whether a
        selected quote carries a Silhouette-signed Permit2 authorisation.

        This is an open set: adapters are added as venues are integrated, so a
        client must tolerate a value it does not recognise.
      enum:
        - XSTOCKS_INVENTORY
        - XSTOCKS_XCHANGE
        - DINARI_INVENTORY
        - DINARI_FULFILMENT_ESCROW
        - ERC20
        - HYPERCORE_SPOT
        - HYPERCORE_PERP
    Side:
      type: string
      enum:
        - BUY
        - SELL
    Token:
      type: object
      description: |-
        A token supported through Silhouette. Not a mirror of every token known to
        Hyperliquid.
      required:
        - symbol
        - weiDecimals
      properties:
        address:
          oneOf:
            - type: "null"
            - $ref: "#/components/schemas/EvmAddress"
              description: |-
                On-chain ERC-20 contract. Absent when the token has no ERC-20.
                Every token field on the API is a symbol, so this is the documented
                route for a client that needs the contract itself — relaying a
                Permit2 authorisation, calling a wrapper, or reading an on-chain
                balance.
        symbol:
          $ref: "#/components/schemas/TokenSymbol"
          description: Canonical uppercase token symbol.
        weiDecimals:
          type: integer
          format: int32
          description: |-
            Wei decimal precision used to convert raw chain/accounting units into
            human-readable token units.
          example: 8
          minimum: 0
    TokenSymbol:
      type: string
      description: Canonical uppercase token symbol, e.g. `USDC`.
      examples:
        - USDC
      maxLength: 32
      pattern: ^[A-Z0-9]+$
    TokensPage:
      type: object
      description: A cursor-paginated page of supported tokens.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/Token"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
    TopOfBook:
      type: object
      description: |-
        The top of one instrument's book at a moment in time: the best bid and the
        best ask, aggregated across every maker streaming prices for the instrument
        and attributed to none of them. Either side is `null` when no maker is
        showing that side.

        Indicative rather than tradeable. A taker acts on it by raising an RFQ and
        choosing among the quotes it draws, not by hitting the price. The same
        object serves both surfaces: the latest one known rides on an instrument as
        `topOfBook`, giving a client a value before it opens the live feed, and the
        `price` frame on a `prices` subscription carries each change as it happens.
        The two surfaces diverge once the last maker stops quoting the instrument:
        `topOfBook` is omitted from then on, but the `price` frame still carries one
        final, both-sides-`null` frame clearing the instrument before subscribers
        hear nothing further.
      required:
        - instrumentId
        - ts
      properties:
        ask:
          type:
            - string
            - "null"
          description: Best ask as a decimal string; `null` if there is no ask.
          example: "100"
        bid:
          type:
            - string
            - "null"
          description: Best bid as a decimal string; `null` if there is no bid.
          example: "95"
        instrumentId:
          $ref: "#/components/schemas/InstrumentId"
          description: The instrument these prices are for.
        ts:
          $ref: "#/components/schemas/UnixMillis"
          description: The instant this top of book was published, in unix milliseconds.
    TxHash:
      type: string
      description: EVM transaction hash, lowercase 0x-hex.
      examples:
        - "0x2f1c9e4d7b3a8056c1d4e9f2a7b6c3d8e5f4a1b2c3d4e5f6a7b8c9d0e1f2a3b4"
      pattern: ^0x[0-9a-f]{64}$
    UnixMillis:
      type: integer
      format: int64
      description: Unix timestamp in milliseconds.
      examples:
        - 1700000000000
      minimum: 0
    UnixSeconds:
      type: integer
      format: int64
      description: Unix timestamp in seconds. Used only for on-chain-native values relayed verbatim (the Permit2 deadline and a promised delivery's deadline); every other instant in this API is `UnixMillis`.
      examples:
        - 1700000000
      minimum: 0
    UserId:
      type: string
      format: uuid
      description: UUIDv7 identifier (chronologically sortable).
      examples:
        - 0193b6f1-7c10-7d6c-8000-abc123456789
    ValidationErrorDetail:
      type: object
      required:
        - field
        - reason
      properties:
        field:
          type: string
          description: Field path using dot/bracket notation, e.g. `orderIds[3]`.
          example: instrumentId
        message:
          $ref: "#/components/schemas/PublicReason"
        reason:
          $ref: "#/components/schemas/PublicReason"
          description: Stable validation reason.
    Withdrawal:
      type: object
      description: A taker-facing view of a withdrawal across its lifecycle.
      required:
        - withdrawalId
        - token
        - amount
        - toAddress
        - status
        - rail
        - createdAt
        - updatedAt
      properties:
        amount:
          $ref: "#/components/schemas/DecimalString"
        createdAt:
          $ref: "#/components/schemas/UnixMillis"
        failureCode:
          $ref: "#/components/schemas/WithdrawalFailureCode"
          description: |-
            Why the withdrawal did not complete or has not been released.

            Present on `FAILED` and on `UNRESOLVED` — including
            `OUTCOME_UNKNOWN`, which is set precisely while the row may still
            reconcile to completed. Read `status` for the lifecycle; this says
            only what went wrong. A row that recorded no code omits the field
            even so, so read its absence as "no reason published", never as
            success.

            An open set — tolerate a value you do not recognise rather than
            failing to parse the response.
        failureReason:
          $ref: "#/components/schemas/PublicReason"
          description: |-
            A human-readable sentence for `failureCode`, safe to show a user.
            Present exactly when `failureCode` is. Its wording may change, so
            branch on the code rather than on this text.
        rail:
          $ref: "#/components/schemas/WithdrawalRail"
          description: |-
            The custody rail the payout leaves (or left) on. `HYPEREVM` pays an
            ERC-20 transfer to your wallet, recorded under `txHash`. `HYPERCORE`
            pays an HL spot-send: the funds arrive in your HyperCore spot balance
            at the same address, and no `txHash` exists.
        status:
          $ref: "#/components/schemas/WithdrawalStatus"
        toAddress:
          $ref: "#/components/schemas/EvmAddress"
        token:
          $ref: "#/components/schemas/TokenSymbol"
        txHash:
          $ref: "#/components/schemas/TxHash"
        updatedAt:
          $ref: "#/components/schemas/UnixMillis"
        withdrawalId:
          $ref: "#/components/schemas/WithdrawalId"
    WithdrawalFailureCode:
      type: string
      description: |-
        Why a withdrawal did not complete.

        Most values mean the transfer definitively did not happen and the reserve
        returns to your available balance shortly after the status changes. Two do
        not: `OUTCOME_UNKNOWN`, where the funds stay reserved while an ambiguous
        send is confirmed, and `HELD_FOR_REVIEW`, where nothing was sent but the
        request is held for an operator.

        One value says the exchange, not your request or the network, is the
        reason: `CUSTODY_UNFUNDED`. Nothing is wrong with the request, and
        re-sending it succeeds once the shortfall is cleared.

        Branch on this rather than `failureReason`, whose wording may change. The
        set is closed today and may gain values, so tolerate one you do not
        recognise rather than failing.
      enum:
        - PROVIDER_UNAVAILABLE
        - CUSTODY_UNFUNDED
        - REJECTED
        - REVERTED_ON_CHAIN
        - TRANSFER_NOT_OBSERVED
        - SUPERSEDED
        - RETRIES_EXHAUSTED
        - OUTCOME_UNKNOWN
        - INTERNAL_ERROR
        - HELD_FOR_REVIEW
    WithdrawalId:
      type: string
      description: "Opaque resource identifier, always carrying the `wd_` prefix. The whole string is the identity: store it and send it back verbatim, and never parse, split, or construct one — everything after the prefix is an internal encoding that may change."
      examples:
        - wd_0193b6f3c8827e5a8000bcd345678901
      maxLength: 128
      minLength: 1
    WithdrawalRail:
      type: string
      description: |-
        The custody rail a withdrawal paid out on. `HYPEREVM` is an ERC-20 transfer
        from the omnibus to the account's own address, recorded under `txHash`;
        `HYPERCORE` is an HL spot-send from the spot omnibus, which carries no
        `txHash`. The rail is chosen when the request reserves and is reported here,
        never requested: the payout leaves the custody its reservation is held at
        and never moves to the other rail. Tolerate a value you do not recognise
        rather than failing to parse the response.
      enum:
        - HYPEREVM
        - HYPERCORE
    WithdrawalStatus:
      type: string
      description: |-
        Lifecycle of a withdrawal. A request starts `PENDING` with the funds
        reserved, becomes `APPROVED` once it clears Silhouette's checks, and reads
        `PROCESSING` while the on-chain transfer is in flight. It ends `COMPLETED`
        when the transfer confirms and the funds have left custody, or `FAILED` when
        the transfer definitively did not happen and the reserved funds are released
        back to the account. `UNRESOLVED` means the on-chain outcome is not yet
        known: the funds stay reserved and an operator reconciles the request with
        the chain, so it is neither a success nor a release.

        This is an open set: values may be added, so a client must tolerate one it
        does not recognise rather than failing to parse the response.
      enum:
        - PENDING
        - APPROVED
        - PROCESSING
        - COMPLETED
        - FAILED
        - UNRESOLVED
    WithdrawalsPage:
      type: object
      description: A cursor-paginated page of withdrawals.
      required:
        - items
        - hasMore
        - nextCursor
      properties:
        hasMore:
          type: boolean
        items:
          type: array
          items:
            $ref: "#/components/schemas/Withdrawal"
        nextCursor:
          type:
            - string
            - "null"
          description: The `cursor` value for the next page; null when `hasMore` is false.
  securitySchemes:
    hmac:
      type: http
      scheme: bearer
      bearerFormat: access key
      description: 'HMAC-SHA256 per-request signing, required on every private endpoint. A SIWE login (`POST /v1/auth/api-keys`) mints a credential pair: a public access key and a secret. Each request carries the access key as the bearer credential (`Authorization: Bearer <access-key>`) plus two companion headers: the signing time in `Silhouette-API-Timestamp` (unix milliseconds) and a base64 `Silhouette-API-Signature`, the HMAC-SHA256, under the secret, of the canonical string `"{timestamp}\n{METHOD}\n{path}?{query}\n{body}"` (the body empty for a GET). The timestamp must fall within 30 seconds of receipt, the MAC is recomputed over the received bytes, and the access key resolves to the account it acts for; every authentication failure is a `401`. The 30-second window bounds how long a captured signature stays usable; it does not make a request single-use within it. Replay of a funds-committing request is guarded by the `idempotencyKey` carried in the signed body. The companion headers are documented on each operation.'
tags:
  - name: instruments
    description: Public token and instrument metadata.
  - name: balances
    description: Private account balance state.
  - name: rfq
    description: RFQ submission, lookup, quoting, and settlement history.
  - name: funding
    description: Deposits and withdrawals.
  - name: auth
    description: SIWE login and HMAC credential issuance.
servers:
  - url: https://rfq-api.silhouette.exchange
    description: Production
