openapi: 3.1.0
jsonSchemaDialect: https://spec.openapis.org/oas/3.1/dialect/base
info:
  title: Kuru Relay REST API
  version: 1.0.0-testnet
  summary: Typed sponsored transactions for Kuru trading wallets and AccountCore.
  description: |
    Kuru Relay accepts a closed set of signed wallet and AccountCore methods,
    constructs canonical calldata, sponsors the outer transaction, and returns
    broadcast acceptance. It is not a generic transaction relay.

    All integer request fields are canonical unsigned decimal strings. JSON
    objects reject unknown and duplicate fields. `BROADCAST` is not receipt,
    execution, or finality confirmation.
servers:
  - url: https://api.relay.testnet.kuru.io
    description: Monad testnet relay API
tags:
  - name: Authentication
    description: Wallet proof and short-lived JWT issuance.
  - name: Relay
    description: Typed transaction submission.
paths:
  /auth/challenge:
    post:
      tags: [Authentication]
      operationId: createAuthenticationChallenge
      summary: Create a one-time wallet-signing challenge
      description: |
        Returns the exact UTF-8 message the requested secondary EOA must sign
        with Ethereum `personal_sign`. The challenge expires after five minutes
        on the current testnet deployment. Do not reconstruct the message.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/AuthenticationChallengeRequest'}
      responses:
        '200':
          description: A new one-time challenge.
          headers:
            Cache-Control:
              schema: {type: string, const: no-store}
            Pragma:
              schema: {type: string, const: no-cache}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/AuthenticationChallengeResponse'}
        '400': {$ref: '#/components/responses/AuthenticationBadRequest'}
        '403': {$ref: '#/components/responses/AuthenticationFailed'}
        '405': {$ref: '#/components/responses/AuthenticationMethodNotAllowed'}
        '413': {$ref: '#/components/responses/AuthenticationRequestTooLarge'}
        '415': {$ref: '#/components/responses/AuthenticationUnsupportedMediaType'}
        '429': {$ref: '#/components/responses/AuthenticationRateLimited'}
        '500': {$ref: '#/components/responses/AuthenticationInternalError'}
        '503': {$ref: '#/components/responses/AuthenticationUnavailable'}
  /auth/token:
    post:
      tags: [Authentication]
      operationId: exchangeAuthenticationChallenge
      summary: Exchange a signed challenge for a wallet-bound JWT
      description: |
        Recovers the EOA from its canonical ERC-191 signature, requires it to
        equal the challenged wallet, rechecks the deployment's wallet-admission
        mode, atomically
        consumes the challenge, and returns a short-lived bearer token. ERC-1271
        signatures are not supported by the initial testnet flow.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/AuthenticationTokenRequest'}
      responses:
        '200':
          description: A one-hour wallet-bound testnet JWT.
          headers:
            Cache-Control:
              schema: {type: string, const: no-store}
            Pragma:
              schema: {type: string, const: no-cache}
          content:
            application/json:
              schema: {$ref: '#/components/schemas/AuthenticationTokenResponse'}
        '400': {$ref: '#/components/responses/AuthenticationBadRequest'}
        '401': {$ref: '#/components/responses/AuthenticationFailed'}
        '403': {$ref: '#/components/responses/AuthenticationFailed'}
        '405': {$ref: '#/components/responses/AuthenticationMethodNotAllowed'}
        '413': {$ref: '#/components/responses/AuthenticationRequestTooLarge'}
        '415': {$ref: '#/components/responses/AuthenticationUnsupportedMediaType'}
        '429': {$ref: '#/components/responses/AuthenticationRateLimited'}
        '500': {$ref: '#/components/responses/AuthenticationInternalError'}
        '503': {$ref: '#/components/responses/AuthenticationUnavailable'}
  /relay:
    options:
      tags: [Relay]
      operationId: preflightRelayTransaction
      summary: Perform a CORS preflight for POST /relay
      security: []
      parameters:
        - name: Origin
          in: header
          required: true
          schema: {type: string}
        - name: Access-Control-Request-Method
          in: header
          required: true
          schema: {type: string, const: POST}
      responses:
        '204':
          description: The origin is allowlisted for the relay POST operation.
          headers:
            Access-Control-Allow-Methods:
              schema: {type: string, const: 'POST, OPTIONS'}
            Access-Control-Allow-Headers:
              schema: {type: string, const: 'Authorization, Content-Type, Traceparent'}
            Access-Control-Max-Age:
              schema: {type: string, const: '300'}
        '403':
          $ref: '#/components/responses/Forbidden'
    post:
      tags: [Relay]
      operationId: submitRelayTransaction
      summary: Validate, sponsor, and broadcast one typed transaction
      description: |
        The authenticated JWT wallet must equal `wallet`. The selected method
        determines the exact payload schema. An optional EIP-7702 authorization
        delegates the same wallet to the configured KuruTradingWallet
        implementation before the target call executes.

        The current testnet deployment enables packed execution, batch
        execution, trigger cancellation, and AccountCore signer authorization.
        Trigger-create variants are versioned protocol schemas but are disabled
        by the current deployment.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RelayRequest'
            examples:
              batchCancel:
                summary: Cancel slot 3 with an exact order-ID binding
                value:
                  requestId: 018f5ef2-88a1-7b41-a826-4b679010f87f
                  method: wallet.execute_batch
                  wallet: '0x1111111111111111111111111111111111111111'
                  payload:
                    header:
                      accountId: '123'
                      market: '0x2222222222222222222222222222222222222222'
                      authNonce: '9'
                      nonce: '1720000000123'
                      deadline: '1720000030'
                      clientOrderId: '0x0000000000000000000000000000000000000000000000000000000000000001'
                      builder: '0x0000000000000000000000000000000000000000'
                      builderFeePps: '0'
                    orders: []
                    cancelSlotIdxs: ['3']
                    expectedOrderIds: ['31']
                    signature: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b'
                  authorization7702: null
      responses:
        '200':
          description: A configured RPC definitely accepted the signed transaction.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BroadcastResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '405':
          $ref: '#/components/responses/MethodNotAllowed'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/RequestTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '502':
          $ref: '#/components/responses/BroadcastRejected'
        '503':
          $ref: '#/components/responses/UnavailableOrUnknown'
        '504':
          $ref: '#/components/responses/DeadlineExceeded'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Wallet-bound testnet JWT obtained through the authentication challenge flow.
  responses:
    AuthenticationBadRequest:
      description: The authentication JSON or one of its fields is malformed.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/AuthenticationErrorResponse'}
    AuthenticationFailed:
      description: Authentication failed without revealing whether the challenge, signature, wallet, or admission check failed.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/AuthenticationErrorResponse'}
    AuthenticationMethodNotAllowed:
      description: The authentication route accepts POST only.
      headers:
        Allow:
          schema: {type: string, const: 'POST, OPTIONS'}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/AuthenticationErrorResponse'}
    AuthenticationRequestTooLarge:
      description: The authentication request exceeds the configured body limit.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/AuthenticationErrorResponse'}
    AuthenticationUnsupportedMediaType:
      description: Content-Type is not application/json.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/AuthenticationErrorResponse'}
    AuthenticationRateLimited:
      description: An authentication IP, wallet, concurrency, or capacity limit was reached.
      headers:
        Retry-After:
          description: Optional whole-second retry hint.
          schema: {$ref: '#/components/schemas/CanonicalDecimal'}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/AuthenticationErrorResponse'}
    AuthenticationInternalError:
      description: An internal authentication invariant failed.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/AuthenticationErrorResponse'}
    AuthenticationUnavailable:
      description: Authentication, secure randomness, wallet admission, or JWT issuance is temporarily unavailable.
      headers:
        Retry-After:
          description: Optional whole-second retry hint.
          schema: {$ref: '#/components/schemas/CanonicalDecimal'}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/AuthenticationErrorResponse'}
    BadRequest:
      description: Malformed, unsupported, stale, or policy-declined input.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    Unauthorized:
      description: Authentication failed without exposing the failed check.
      headers:
        WWW-Authenticate:
          schema: {type: string, const: 'Bearer realm="kuru-relay"'}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    Forbidden:
      description: Wallet admission, delegation, origin, or policy declined the request.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    MethodNotAllowed:
      description: The route accepts POST only.
      headers:
        Allow:
          schema: {type: string}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    Conflict:
      description: Trigger registration conflicts with an existing immutable record.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    RequestTooLarge:
      description: The HTTP body, method payload, or canonical calldata exceeds policy.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    UnsupportedMediaType:
      description: Content-Type is not application/json.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    RateLimited:
      description: A bounded rate, concurrency, or owned-byte admission limit was reached.
      headers:
        Retry-After:
          description: Optional whole-second retry hint.
          schema: {$ref: '#/components/schemas/CanonicalDecimal'}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    InternalError:
      description: An internal invariant failed.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    BroadcastRejected:
      description: The authoritative RPC definitely rejected the signed candidate.
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    UnavailableOrUnknown:
      description: |
        A dependency is unavailable, or broadcast outcome is ambiguous. Inspect
        `code`, `status`, and `retryable`; `BROADCAST_RESULT_UNKNOWN` must not be
        automatically retried.
      headers:
        Retry-After:
          description: Optional whole-second retry hint for retryable outcomes.
          schema: {$ref: '#/components/schemas/CanonicalDecimal'}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
    DeadlineExceeded:
      description: The relay's bounded internal request deadline elapsed.
      headers:
        Retry-After:
          description: Optional whole-second retry hint.
          schema: {$ref: '#/components/schemas/CanonicalDecimal'}
      content:
        application/json:
          schema: {$ref: '#/components/schemas/ErrorResponse'}
  schemas:
    AuthenticationChallengeRequest:
      type: object
      additionalProperties: false
      required: [wallet]
      properties:
        wallet: {$ref: '#/components/schemas/Address'}
    AuthenticationChallengeResponse:
      type: object
      additionalProperties: false
      required: [challengeId, message, expiresAt]
      properties:
        challengeId: {$ref: '#/components/schemas/AuthenticationChallengeID'}
        message:
          type: string
          minLength: 1
          maxLength: 2048
          description: Exact UTF-8 SIWE-style message to sign with ERC-191 personal_sign.
        expiresAt:
          type: string
          format: date-time
    AuthenticationTokenRequest:
      type: object
      additionalProperties: false
      required: [challengeId, signature]
      properties:
        challengeId: {$ref: '#/components/schemas/AuthenticationChallengeID'}
        signature:
          type: string
          pattern: '^0x[0-9a-fA-F]{130}$'
          description: Canonical low-s, 65-byte EOA personal-sign signature; recovery byte may be 0/1 or 27/28.
    AuthenticationTokenResponse:
      type: object
      additionalProperties: false
      required: [accessToken, tokenType, expiresAt, wallet]
      properties:
        accessToken:
          type: string
          minLength: 1
          maxLength: 16384
          description: Short-lived bearer JWT. Treat as a secret and never log it.
        tokenType:
          type: string
          const: Bearer
        expiresAt:
          type: string
          format: date-time
        wallet: {$ref: '#/components/schemas/Address'}
    AuthenticationErrorResponse:
      type: object
      additionalProperties: false
      required: [code, message, retryable]
      properties:
        code:
          type: string
          enum: [MALFORMED_REQUEST, AUTHENTICATION_FAILED, AUTHENTICATION_UNAVAILABLE, RATE_LIMITED, METHOD_NOT_ALLOWED, UNSUPPORTED_MEDIA_TYPE]
        message: {type: string}
        retryable: {type: boolean}
    AuthenticationChallengeID:
      type: string
      pattern: '^[0-9a-f]{32}$'
      description: Server-generated 128-bit challenge identifier without a 0x prefix.
    RelayRequest:
      oneOf:
        - $ref: '#/components/schemas/ExecuteReplaceRequest'
        - $ref: '#/components/schemas/ExecuteBatchRequest'
        - $ref: '#/components/schemas/CreateReplaceTriggerRequest'
        - $ref: '#/components/schemas/CreateBatchTriggerRequest'
        - $ref: '#/components/schemas/CancelTriggerRequest'
        - $ref: '#/components/schemas/AuthorizeAccountSignerRequest'
      discriminator:
        propertyName: method
        mapping:
          wallet.execute_replace_by_slot_packed: '#/components/schemas/ExecuteReplaceRequest'
          wallet.execute_batch: '#/components/schemas/ExecuteBatchRequest'
          wallet.create_replace_trigger: '#/components/schemas/CreateReplaceTriggerRequest'
          wallet.create_batch_trigger: '#/components/schemas/CreateBatchTriggerRequest'
          wallet.cancel_trigger: '#/components/schemas/CancelTriggerRequest'
          account_core.authorize_account_signer_by_sig: '#/components/schemas/AuthorizeAccountSignerRequest'
    BaseHTTPRelayRequest:
      type: object
      required: [requestId, wallet]
      properties:
        requestId: {$ref: '#/components/schemas/UUIDv7'}
        wallet:
          $ref: '#/components/schemas/Address'
        authorization7702:
          oneOf:
            - $ref: '#/components/schemas/Authorization7702'
            - type: 'null'
          default: null
      description: '`authorization7702` may be absent or null.'
    ExecuteReplaceRequest:
      x-kuru-testnet-enabled: true
      allOf:
        - $ref: '#/components/schemas/BaseHTTPRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.execute_replace_by_slot_packed}
            payload: {$ref: '#/components/schemas/ExecuteReplacePayload'}
      unevaluatedProperties: false
    ExecuteBatchRequest:
      x-kuru-testnet-enabled: true
      allOf:
        - $ref: '#/components/schemas/BaseHTTPRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.execute_batch}
            payload: {$ref: '#/components/schemas/ExecuteBatchPayload'}
      unevaluatedProperties: false
    CreateReplaceTriggerRequest:
      x-kuru-testnet-enabled: false
      allOf:
        - $ref: '#/components/schemas/BaseHTTPRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.create_replace_trigger}
            payload: {$ref: '#/components/schemas/CreateReplaceTriggerPayload'}
      unevaluatedProperties: false
    CreateBatchTriggerRequest:
      x-kuru-testnet-enabled: false
      allOf:
        - $ref: '#/components/schemas/BaseHTTPRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.create_batch_trigger}
            payload: {$ref: '#/components/schemas/CreateBatchTriggerPayload'}
      unevaluatedProperties: false
    CancelTriggerRequest:
      x-kuru-testnet-enabled: true
      allOf:
        - $ref: '#/components/schemas/BaseHTTPRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.cancel_trigger}
            payload: {$ref: '#/components/schemas/CancelTriggerPayload'}
      unevaluatedProperties: false
    AuthorizeAccountSignerRequest:
      x-kuru-testnet-enabled: true
      allOf:
        - $ref: '#/components/schemas/BaseHTTPRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: account_core.authorize_account_signer_by_sig}
            payload: {$ref: '#/components/schemas/AuthorizeAccountSignerPayload'}
      unevaluatedProperties: false
    BaseWebSocketRelayRequest:
      type: object
      required: [id]
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
          description: Opaque correlation ID unique within the connection.
        authorization7702:
          oneOf:
            - $ref: '#/components/schemas/Authorization7702'
            - type: 'null'
          default: null
    WebSocketRelayRequest:
      oneOf:
        - $ref: '#/components/schemas/WSExecuteReplaceRequest'
        - $ref: '#/components/schemas/WSExecuteBatchRequest'
        - $ref: '#/components/schemas/WSCreateReplaceTriggerRequest'
        - $ref: '#/components/schemas/WSCreateBatchTriggerRequest'
        - $ref: '#/components/schemas/WSCancelTriggerRequest'
        - $ref: '#/components/schemas/WSAuthorizeAccountSignerRequest'
      discriminator:
        propertyName: method
        mapping:
          wallet.execute_replace_by_slot_packed: '#/components/schemas/WSExecuteReplaceRequest'
          wallet.execute_batch: '#/components/schemas/WSExecuteBatchRequest'
          wallet.create_replace_trigger: '#/components/schemas/WSCreateReplaceTriggerRequest'
          wallet.create_batch_trigger: '#/components/schemas/WSCreateBatchTriggerRequest'
          wallet.cancel_trigger: '#/components/schemas/WSCancelTriggerRequest'
          account_core.authorize_account_signer_by_sig: '#/components/schemas/WSAuthorizeAccountSignerRequest'
    WSExecuteReplaceRequest:
      x-kuru-testnet-enabled: true
      allOf:
        - $ref: '#/components/schemas/BaseWebSocketRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.execute_replace_by_slot_packed}
            payload: {$ref: '#/components/schemas/ExecuteReplacePayload'}
      unevaluatedProperties: false
    WSExecuteBatchRequest:
      x-kuru-testnet-enabled: true
      allOf:
        - $ref: '#/components/schemas/BaseWebSocketRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.execute_batch}
            payload: {$ref: '#/components/schemas/ExecuteBatchPayload'}
      unevaluatedProperties: false
    WSCreateReplaceTriggerRequest:
      x-kuru-testnet-enabled: false
      allOf:
        - $ref: '#/components/schemas/BaseWebSocketRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.create_replace_trigger}
            payload: {$ref: '#/components/schemas/CreateReplaceTriggerPayload'}
      unevaluatedProperties: false
    WSCreateBatchTriggerRequest:
      x-kuru-testnet-enabled: false
      allOf:
        - $ref: '#/components/schemas/BaseWebSocketRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.create_batch_trigger}
            payload: {$ref: '#/components/schemas/CreateBatchTriggerPayload'}
      unevaluatedProperties: false
    WSCancelTriggerRequest:
      x-kuru-testnet-enabled: true
      allOf:
        - $ref: '#/components/schemas/BaseWebSocketRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: wallet.cancel_trigger}
            payload: {$ref: '#/components/schemas/CancelTriggerPayload'}
      unevaluatedProperties: false
    WSAuthorizeAccountSignerRequest:
      x-kuru-testnet-enabled: true
      allOf:
        - $ref: '#/components/schemas/BaseWebSocketRelayRequest'
        - type: object
          required: [method, payload]
          properties:
            method: {type: string, const: account_core.authorize_account_signer_by_sig}
            payload: {$ref: '#/components/schemas/AuthorizeAccountSignerPayload'}
      unevaluatedProperties: false
    IntentHeader:
      type: object
      additionalProperties: false
      required: [accountId, market, authNonce, nonce, deadline, clientOrderId, builder, builderFeePps]
      properties:
        accountId: {$ref: '#/components/schemas/Uint40'}
        market: {$ref: '#/components/schemas/Address'}
        authNonce: {$ref: '#/components/schemas/Uint256'}
        nonce:
          allOf: [{$ref: '#/components/schemas/Uint64'}]
          description: Wallet intent nonce in Unix-millisecond-oriented policy space.
        deadline:
          allOf: [{$ref: '#/components/schemas/Uint64'}]
          description: Inclusive Unix timestamp in seconds.
        clientOrderId: {$ref: '#/components/schemas/Bytes32'}
        builder: {$ref: '#/components/schemas/Address'}
        builderFeePps: {$ref: '#/components/schemas/Uint32'}
    NativeOrder:
      type: object
      additionalProperties: false
      required: [side, quantity, price, tif, executionInstruction, minSizeAfterBlock]
      properties:
        side: {type: string, enum: [BUY, SELL]}
        quantity: {$ref: '#/components/schemas/Uint96'}
        price: {$ref: '#/components/schemas/Uint32'}
        tif: {type: string, enum: [GTC, IOC, FOK]}
        executionInstruction: {type: string, enum: [NONE, POST_ONLY]}
        minSizeAfterBlock: {$ref: '#/components/schemas/Uint32'}
    ExecuteReplacePayload:
      type: object
      additionalProperties: false
      required: [header, packedOps, expectedOrderIds, signature]
      properties:
        header: {$ref: '#/components/schemas/IntentHeader'}
        packedOps:
          type: string
          pattern: '^0x(?:[0-9a-fA-F]{64}){1,256}$'
          description: One to 256 canonical 32-byte packed operations.
        expectedOrderIds:
          type: array
          minItems: 1
          maxItems: 256
          items: {$ref: '#/components/schemas/Uint64'}
          description: Length must equal the number of packed operations.
        signature: {$ref: '#/components/schemas/WalletSignature'}
    ExecuteBatchPayload:
      type: object
      additionalProperties: false
      required: [header, orders, cancelSlotIdxs, expectedOrderIds, signature]
      properties:
        header: {$ref: '#/components/schemas/IntentHeader'}
        orders:
          type: array
          maxItems: 256
          items: {$ref: '#/components/schemas/NativeOrder'}
        cancelSlotIdxs:
          type: array
          maxItems: 62
          uniqueItems: true
          items: {$ref: '#/components/schemas/Uint8'}
        expectedOrderIds:
          type: array
          maxItems: 62
          items: {$ref: '#/components/schemas/Uint64'}
          description: Length must equal `cancelSlotIdxs` length.
        signature: {$ref: '#/components/schemas/WalletSignature'}
      description: Orders plus cancel slots must contain at least one item and remain within deployment bounds.
    TriggerConditionFields:
      type: object
      required: [triggerExpiry, conditionSchema, condition, conditionHash]
      properties:
        triggerExpiry:
          allOf: [{$ref: '#/components/schemas/Uint64'}]
          description: Unix timestamp in seconds; future and within deployment horizon.
        conditionSchema:
          allOf: [{$ref: '#/components/schemas/Uint32'}]
          description: Nonzero allowlisted condition schema.
        condition:
          type: string
          pattern: '^0x(?:[0-9a-fA-F]{2}){4,16384}$'
          description: Canonical condition envelope whose first four bytes encode `conditionSchema`.
        conditionHash: {$ref: '#/components/schemas/Bytes32'}
    CreateReplaceTriggerPayload:
      x-kuru-testnet-enabled: false
      allOf:
        - $ref: '#/components/schemas/TriggerConditionFields'
        - type: object
          required: [header, packedOps, expectedOrderIds, signature]
          properties:
            header: {$ref: '#/components/schemas/IntentHeader'}
            packedOps:
              type: string
              pattern: '^0x(?:[0-9a-fA-F]{64}){1,256}$'
            expectedOrderIds:
              type: array
              minItems: 1
              maxItems: 256
              items: {$ref: '#/components/schemas/Uint64'}
            signature: {$ref: '#/components/schemas/WalletSignature'}
      unevaluatedProperties: false
    CreateBatchTriggerPayload:
      x-kuru-testnet-enabled: false
      allOf:
        - $ref: '#/components/schemas/TriggerConditionFields'
        - type: object
          required: [header, orders, cancelSlotIdxs, expectedOrderIds, signature]
          properties:
            header: {$ref: '#/components/schemas/IntentHeader'}
            orders:
              type: array
              maxItems: 256
              items: {$ref: '#/components/schemas/NativeOrder'}
            cancelSlotIdxs:
              type: array
              maxItems: 62
              uniqueItems: true
              items: {$ref: '#/components/schemas/Uint8'}
            expectedOrderIds:
              type: array
              maxItems: 62
              items: {$ref: '#/components/schemas/Uint64'}
            signature: {$ref: '#/components/schemas/WalletSignature'}
      unevaluatedProperties: false
      description: Orders plus cancel slots must contain at least one item; binding length equals cancel length.
    CancelTriggerPayload:
      type: object
      additionalProperties: false
      required: [accountId, authNonce, nonce, deadline, triggerId, signature]
      properties:
        accountId: {$ref: '#/components/schemas/Uint40'}
        authNonce: {$ref: '#/components/schemas/Uint256'}
        nonce: {$ref: '#/components/schemas/Uint64'}
        deadline: {$ref: '#/components/schemas/Uint64'}
        triggerId: {$ref: '#/components/schemas/Bytes32'}
        signature: {$ref: '#/components/schemas/WalletSignature'}
    AuthorizeAccountSignerPayload:
      type: object
      additionalProperties: false
      required: [account, authorizer, signer, permissions, expiry, nonce, deadline, signature]
      properties:
        account: {$ref: '#/components/schemas/Address'}
        authorizer: {$ref: '#/components/schemas/Address'}
        signer:
          allOf: [{$ref: '#/components/schemas/Address'}]
          description: Must equal the outer request wallet or bound WebSocket wallet.
        permissions: {$ref: '#/components/schemas/Uint32'}
        expiry:
          allOf: [{$ref: '#/components/schemas/Uint64'}]
          description: Zero means no expiry when deployment policy permits it.
        nonce: {$ref: '#/components/schemas/Uint256'}
        deadline:
          allOf: [{$ref: '#/components/schemas/Uint256'}]
          description: Inclusive Unix timestamp in seconds.
        signature:
          type: string
          pattern: '^0x(?:[0-9a-fA-F]{2}){1,4096}$'
          description: Bounded EOA/ERC-2098/ERC-1271 signature bytes.
    Authorization7702:
      type: object
      additionalProperties: false
      required: [authority, chainId, delegate, nonce, yParity, r, s]
      properties:
        authority:
          allOf: [{$ref: '#/components/schemas/Address'}]
          description: Must equal the request or session wallet.
        chainId: {$ref: '#/components/schemas/Uint256'}
        delegate:
          allOf: [{$ref: '#/components/schemas/Address'}]
          description: Must equal the configured KuruTradingWallet implementation.
        nonce:
          allOf: [{$ref: '#/components/schemas/Uint64'}]
          description: Authority transaction nonce; must be strictly less than uint64 max.
        yParity:
          type: string
          enum: ['0', '1']
        r: {$ref: '#/components/schemas/Bytes32'}
        s: {$ref: '#/components/schemas/Bytes32'}
    BroadcastResponse:
      type: object
      additionalProperties: false
      required: [requestId, status, txHash, sponsorAddress, sponsorNonce, transactionType]
      properties:
        requestId: {$ref: '#/components/schemas/UUIDv7'}
        status: {type: string, const: BROADCAST}
        txHash: {$ref: '#/components/schemas/Bytes32'}
        sponsorAddress: {$ref: '#/components/schemas/Address'}
        sponsorNonce: {$ref: '#/components/schemas/Uint64'}
        transactionType: {type: string, enum: [DYNAMIC_FEE, SET_CODE]}
    ErrorResponse:
      type: object
      additionalProperties: false
      required: [requestId, status, code, message, retryable, retryAfterMs, candidateTxHash, sponsorAddress, sponsorNonce]
      properties:
        requestId:
          oneOf:
            - $ref: '#/components/schemas/UUIDv7'
            - type: 'null'
        status: {type: string, enum: [REJECTED, UNKNOWN]}
        code: {$ref: '#/components/schemas/ErrorCode'}
        message: {type: string, minLength: 1, maxLength: 256}
        retryable: {type: boolean}
        retryAfterMs:
          type: [integer, 'null']
          minimum: 1
          maximum: 3600000
        candidateTxHash:
          oneOf:
            - $ref: '#/components/schemas/Bytes32'
            - type: 'null'
        sponsorAddress:
          oneOf:
            - $ref: '#/components/schemas/Address'
            - type: 'null'
        sponsorNonce:
          oneOf:
            - $ref: '#/components/schemas/Uint64'
            - type: 'null'
    WebSocketRelayResponse:
      oneOf:
        - $ref: '#/components/schemas/WebSocketBroadcastResponse'
        - $ref: '#/components/schemas/WebSocketErrorResponse'
    WebSocketBroadcastResponse:
      type: object
      additionalProperties: false
      required: [id, status, txHash, sponsorAddress, sponsorNonce, transactionType, retryable]
      properties:
        id: {type: string, minLength: 1, maxLength: 128}
        status: {type: string, const: BROADCAST}
        txHash: {$ref: '#/components/schemas/Bytes32'}
        sponsorAddress: {$ref: '#/components/schemas/Address'}
        sponsorNonce: {$ref: '#/components/schemas/Uint64'}
        transactionType: {type: string, enum: [DYNAMIC_FEE, SET_CODE]}
        retryable: {type: boolean, const: false}
    WebSocketErrorResponse:
      type: object
      additionalProperties: false
      required: [id, status, code, message, retryable]
      properties:
        id: {type: string, maxLength: 128}
        status: {type: string, enum: [REJECTED, UNKNOWN]}
        code: {$ref: '#/components/schemas/ErrorCode'}
        message: {type: string, minLength: 1, maxLength: 256}
        retryable: {type: boolean}
        receivedNonce: {type: integer, minimum: 0, maximum: 18446744073709551615}
        lastAcceptedNonce: {type: integer, minimum: 0, maximum: 18446744073709551615}
      description: Candidate transaction identity and retry-after hints are not present in the current WebSocket failure envelope.
    ErrorCode:
      type: string
      enum:
        - MALFORMED_REQUEST
        - METHOD_NOT_ALLOWED
        - UNSUPPORTED_MEDIA_TYPE
        - REQUEST_TOO_LARGE
        - UNKNOWN_METHOD
        - AUTHENTICATION_FAILED
        - AUTHENTICATION_UNAVAILABLE
        - UNSUPPORTED_CHAIN
        - UNSUPPORTED_WALLET
        - UNSUPPORTED_MARKET
        - WALLET_NOT_ALLOWLISTED
        - WALLET_NOT_DELEGATED
        - WALLET_REGISTRY_UNAVAILABLE
        - DEADLINE_EXPIRED
        - DEADLINE_TOO_FAR
        - NONCE_OUTSIDE_WINDOW
        - NON_INCREASING_SESSION_NONCE
        - INVALID_SIGNATURE
        - INVALID_BINDING
        - INVALID_TRIGGER_CONDITION
        - TRIGGER_REGISTRATION_CONFLICT
        - TRIGGER_REGISTRATION_DECLINED
        - TRIGGER_REGISTRATION_UNAVAILABLE
        - POLICY_DECLINED
        - RATE_LIMITED
        - INVALID_7702_AUTHORIZATION
        - 7702_CHAIN_MISMATCH
        - 7702_DELEGATE_MISMATCH
        - 7702_AUTHORITY_MISMATCH
        - 7702_NONCE_MISMATCH
        - 7702_UNSUPPORTED_CODE_STATE
        - 7702_PREFLIGHT_UNAVAILABLE
        - INVALID_ACCOUNT_AUTHORIZATION
        - ACCOUNT_AUTH_NONCE_MISMATCH
        - ACCOUNT_PERMISSION_DECLINED
        - ACCOUNT_AUTHORIZATION_UNAVAILABLE
        - GAS_POLICY_UNAVAILABLE
        - ACCESS_LIST_POLICY_UNAVAILABLE
        - SIGNER_QUEUE_FULL
        - SIGNER_LANE_UNAVAILABLE
        - SIGNER_POLICY_DECLINED
        - BROADCAST_REJECTED
        - BROADCAST_RESULT_UNKNOWN
        - INTERNAL_DEADLINE_EXCEEDED
        - INTERNAL_ERROR
    UUIDv7:
      type: string
      pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$'
    Address:
      type: string
      pattern: '^0x[0-9a-fA-F]{40}$'
    Bytes32:
      type: string
      pattern: '^0x[0-9a-fA-F]{64}$'
    WalletSignature:
      type: string
      pattern: '^0x[0-9a-fA-F]{130}$'
      description: Canonical 65-byte low-s ECDSA signature with v 27 or 28.
    CanonicalDecimal:
      type: string
      pattern: '^(0|[1-9][0-9]*)$'
    Uint8:
      allOf: [{$ref: '#/components/schemas/CanonicalDecimal'}]
      maxLength: 3
      description: Canonical decimal string in uint8 range; cancel slots must be below 62.
    Uint32:
      allOf: [{$ref: '#/components/schemas/CanonicalDecimal'}]
      maxLength: 10
      description: Canonical decimal string in uint32 range.
    Uint40:
      allOf: [{$ref: '#/components/schemas/CanonicalDecimal'}]
      maxLength: 13
      description: Canonical decimal string in uint40 range; account IDs must be nonzero.
    Uint64:
      allOf: [{$ref: '#/components/schemas/CanonicalDecimal'}]
      maxLength: 20
      description: Canonical decimal string in uint64 range.
    Uint96:
      allOf: [{$ref: '#/components/schemas/CanonicalDecimal'}]
      maxLength: 29
      description: Canonical decimal string in uint96 range.
    Uint256:
      allOf: [{$ref: '#/components/schemas/CanonicalDecimal'}]
      maxLength: 78
      description: Canonical decimal string in uint256 range.
