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

> ## Agent Instructions
> Bitnob API requests authenticate with an `X-Auth-Client` API key header against https://api.bitnob.com.
> Always use the sandbox environment for testing. Never put live API keys in sample code.
> Money amounts are integers in the smallest unit of the currency (satoshis for BTC, cents for fiat). Never use floats.
> Every state-changing request takes a client-supplied `reference` and must be treated as idempotent on retry.
> Quotes expire. Re-quote rather than reusing a stale quote id.

# List transactions across wallets

> Returns a paginated list of transactions. Filterable by wallet_id, status, direction, asset, and created_at range.



## OpenAPI

````yaml /api-collections/swagger/bitnob-platform.openapi.json get /transactions
openapi: 3.0.3
info:
  description: >-
    # Bitnob Enterprise API


    A high-performance Wallet-as-a-Service API for partners and developers:

    non-custodial wallets, transactions, gas stations, on-chain subscriptions
    and

    platform webhooks across multiple chains.


    ## Authentication


    Authenticate every request with your secret API key in the **X-API-Key**

    request header. Keys are scoped to one organization and carry a fixed set of

    permissions; an endpoint returns **403** when the key lacks the required

    permission. Treat the key as a credential — never expose it in a browser or

    mobile client.


    ## Request tracing


    Every response carries an **X-Request-Id** header. Log it, and quote it when

    contacting support — it identifies the exact request in our systems.


    ## Rate limits


    Wallet/business API and node RPC traffic have **independent rate-limit
    budgets**,

    even when they use the same API key and organization:


    | Traffic | Limit |

    |---|---|

    | Wallet and other non-RPC API requests | A per-key requests-per-minute
    allowance plus a configured burst, and a shared organization per-minute
    allowance |

    | **POST /rpc/{chain_network}**, including the **/v1/rpc/{chain_network}**
    alias | A shared organization requests-per-second limit, counted per
    JSON-RPC call |


    Authenticated RPC requests do not consume your wallet API quota, and wallet

    requests do not consume your RPC limit. The RPC limit is shared across all
    keys

    and chains in your organization; creating another key does not increase it.


    A JSON-RPC batch counts each call in the batch, not as a single request —
    batching

    many calls into one HTTP request consumes the limit per call. On live there
    is

    currently no additional per-day volume cap; sandbox applies a low per-second
    and

    per-day default so it cannot be used as production infrastructure. An RPC
    429

    carries **Retry-After**.


    Rate-limited responses expose the current state:


    - **X-RateLimit-Limit** — requests permitted in the current window.

    - **X-RateLimit-Remaining** — requests still available in this window.

    - **X-RateLimit-Reset** — Unix time (seconds) when the window resets.

    - **X-RateLimit-Scope** — **key** for authenticated non-RPC traffic, or
    **ip**
      for unauthenticated/failed-auth traffic. (RPC uses a separate per-second limit
      and returns only Retry-After on 429.)

    For non-RPC traffic, **X-Org-RateLimit-Limit**,
    **X-Org-RateLimit-Remaining**,

    and **X-Org-RateLimit-Reset** describe your organization-wide allowance
    shared

    across keys; the unprefixed headers report your per-key allowance or the

    organization allowance, whichever has less remaining. RPC proxy traffic is
    limited

    separately and returns only **Retry-After** on 429 (no X-RateLimit headers).


    Read these proactively and slow down before you are throttled. When a limit
    is

    exceeded the API responds **429** with a **Retry-After** header (seconds to

    wait); retry only after that delay.


    These are request-quota boundaries, not dedicated infrastructure: CPU,
    database

    and upstream provider capacity may still be shared. Authentication,
    permissions,

    IP restrictions, signatures and nonce replay protection are unchanged.


    ## Idempotency


    Send an **Idempotency-Key** header (a unique value you generate, e.g. a
    UUID)

    on **POST**, **PATCH** and **DELETE** requests so a network retry never

    applies the same change twice.


    - It is **required** on value-moving endpoints (creating transactions,
      creating addresses, refilling and sponsor-broadcasting gas stations,
      creating wallets); those return **400 idempotency_key_required** if absent.
    - Keys are retained for **24 hours**.

    - Retrying with the same key replays the original response byte-for-byte and
      adds an **Idempotent-Replayed: true** header.
    - Reusing a key with a **different request body** is rejected with
      **409 idempotency_conflict** — generate a fresh key per distinct operation.
    - Only successful (2xx) responses are stored, so a request that failed can
    be
      safely retried with the same key.
    - An Idempotency-Key sent without authentication is rejected with **401**.


    ## Pagination


    List endpoints are cursor-paginated. Parameters:


    - **limit** — page size; bounded by the endpoint maximum.

    - **starting_after** — return the page after this opaque cursor.

    - **ending_before** — return the page before this opaque cursor.


    Supplying both **starting_after** and **ending_before** is rejected with

    **400 ambiguous_cursor**. Every list response is the same envelope:

        {
          "object": "list",
          "data": [ ... ],
          "has_more": true,
          "next_cursor": "b3JnOjE2ODk...",
          "url": "/wallets"
        }

    When **has_more** is true, pass **next_cursor** as **starting_after** to
    fetch

    the following page. Cursors are opaque — do not parse or construct them.


    ## Transaction lifecycle and async outcomes


    Creating a transaction (POST /v1/transactions) returns one of two outcomes:


    - **201 Created** — signed and submitted for broadcast.

    - **202 Accepted** — accepted but not yet executing. The body's **status**
    says why:
      - **pending_authorization** — organization policy requires approval; the
        transaction executes once approvers approve it.
      - **delayed** — a policy time-lock holds the transaction until **execute_at**
        (an ISO-8601 timestamp on the response), when it executes automatically.

      In both cases the transaction is durably persisted and its funds are locked.

    A transaction's **status** moves through these states:


    - **Non-terminal**: pending_signature, pending_authorization, delayed,
    signed,
      awaiting_sponsorship, broadcasting, confirming, pending_screening.
    - **Terminal**: completed, failed, cancelled, rejected.


    Poll **GET /v1/transactions/{id}** (or subscribe to a webhook) until the
    status

    is terminal. An empty **tx_hash** means the transaction has not been
    broadcast

    on-chain yet; once broadcast, **confirmations** climbs until the transaction
    is

    **completed**.


    ## Amounts and units


    All monetary values are JSON **strings**, never numbers — parse them with a

    decimal/big-number type, not a float, to avoid precision loss.


    Every monetary value appears in two forms so you never have to guess
    decimals:


    - **amount** / **fee_amount** — human-readable decimal in the asset's
    display
      unit (e.g. "25.5" USDC).
    - **amount_raw** / **fee_amount_raw** / wallet **balance** — the integer
    value
      in the asset's smallest base unit (e.g. "25500000" for 25.5 USDC at 6
      decimals). **balance** fields are ALWAYS base units.

    **asset_decimals** (and **fee_decimals**) give the conversion:

    display = raw / 10^decimals. The fee is denominated in **fee_asset**, which
    for

    most chains is the native coin (ETH, TRX, …) and differs from the
    transferred

    asset — always read **fee_asset**/**fee_decimals** rather than assuming.
    After a

    transaction confirms, **realized_fee_amount**(_raw) carries the actual fee
    paid.


    Two balance shapes:


    - **GET /v1/wallets/{id}/balances** — one row PER FUNDED ADDRESS (an HD
    wallet
      has many addresses).
    - **GET /v1/wallets/{id}/balances/{asset}** — a single AGGREGATED total for
    that
      asset across the wallet.

    ## Fees


    Control the fee in one of two ways on transaction create and estimate:


    - **fee_level** — "low", "medium", or "high"; the platform picks a rate.

    - **fee_rate** / **max_fee_rate** — Bitcoin only, in sat/vB, for explicit
      control with an optional cap (the transaction will not pay above
      **max_fee_rate**).

    Use **POST /v1/transactions/estimate-fee** to preview low/medium/high quotes
    and

    any gas-funding options before sending.


    ## Errors


    Every error response is the same envelope:

        {
          "error": {
            "type": "invalid_request_error",
            "code": "invalid_cursor",
            "message": "starting_after is not a valid cursor",
            "param": "starting_after",
            "request_id": "req_01HX..."
          }
        }

    **type** is the broad category; **code** is a stable, machine-readable

    identifier — branch on **code**, show **message** to humans, and **param**

    names the offending field when applicable. Some errors also carry a
    **details**

    object with structured, error-specific context (for example the list of

    conflicting fields); treat it as optional and error-specific.


    ### Error code catalogue


    **invalid_request_error** (HTTP 400) — the request was malformed or failed

    validation: invalid_request, invalid_json, invalid_body, empty_body,

    trailing_data, type_mismatch, unknown_field, missing_required_field,

    invalid_id, invalid_cursor, ambiguous_cursor, invalid_expand, invalid_chain,

    invalid_vault_id, wallet_chain_unknown, invalid_transaction_data,

    amount_precision_loss, gas_station_low_balance, body_too_large,

    idempotency_key_required, idempotency_key_invalid, invalid_webhook_id,

    method_not_allowed.


    **authentication_error** (HTTP 401) — the request was not authenticated:

    not_authenticated, auth_required, org_required.


    **permission_error** (HTTP 403) — the request is authenticated but not

    permitted: insufficient_permissions (the API key lacks the required

    permission), ip_not_allowed (the request's source IP is not in the key's IP

    allowlist).


    **not_found_error** (HTTP 404) — the addressed resource does not exist:

    not_found, route_not_found, asset_not_found, event_not_found,

    gas_station_not_found, subscription_not_found, transaction_not_found,

    vault_not_found, wallet_not_found, address_not_found, webhook_not_found.


    **conflict_error** (HTTP 409) — the request conflicts with current state:

    conflict, idempotency_conflict, wallet_archived.


    **precondition_error** (HTTP 412) — a precondition on the request was not
    met,

    for example an ETag mismatch on a conditional request.


    **unprocessable_entity_error** (HTTP 422) — the request was well-formed but
    a

    business rule prevented it from being processed.


    **rate_limit_error** (HTTP 429) — too many requests: rate_limit_exceeded.


    **api_error** (HTTP 500) — an unexpected server error: request_error,

    internal_error.


    **service_unavailable_error** (HTTP 503) — a dependency is unavailable;

    retry after the Retry-After delay: service_unavailable.


    ## Webhook signature verification


    The platform signs every webhook delivery with HMAC-SHA256 using the signing

    secret issued when you create the webhook or subscription (and rotated via
    the

    rotate-secret endpoints — the plaintext secret is shown only once).


    The signature covers the canonical JSON object below, where **data**,

    **event** and **timestamp** are the corresponding fields of the delivered

    payload and **timestamp** is Unix seconds:

        {"data": <data>, "event": "<event>", "timestamp": <unix_seconds>}

    The signed object contains ONLY those three keys — when verifying, build a

    fresh object from the delivery's data, event and timestamp and exclude the

    signature field (and any other body fields) from the HMAC input. Serialize
    the

    keys in lexical order (data, event, timestamp), matching Go's encoding/json.

    The signature value is:

        "sha256=" + hex( HMAC_SHA256(signing_secret, canonical_json) )

    **Platform webhooks** (created via /webhooks) carry this value in the

    **X-Webhook-Signature** response header. **On-chain subscription webhooks**

    (created via /subscriptions) carry it as a **signature** field inside the

    delivered JSON body — there is no signature header.


    To verify a delivery: recompute the signature from your stored secret and

    compare it to the received value with a constant-time comparison. Reject the

    delivery on mismatch, and reject deliveries whose **timestamp** is older
    than

    your tolerance window (a few minutes is typical) to defend against replay.


    ## Webhook event types


    **Platform notification webhooks** (/webhooks) deliver these event types:

    wallet.created, wallet.deleted, wallet.suspended, wallet_address.created,

    transaction.created, transaction.signed, transaction.broadcasting,

    transaction.confirmed, transaction.completed, transaction.received,

    transaction.failed, transaction.cancelled, gas_station.low_balance,

    gas_station.critical_balance, gas_station.auto_refilled, session.created,

    api_key.created, api_key.deleted, policy.violation,

    policy.created, policy.updated, policy.activated, policy.archived,

    policy.deleted, policy.rule.added, policy.rule.updated, policy.rule.deleted,

    kyt.deposit.held, kyt.deposit.flagged, kyt.deposit.approved,
    kyt.deposit.rejected, kyt.deposit.blocked,

    kyt.withdrawal.blocked, kyt.withdrawal.flagged, kyt.sanctions.match,
    kyt.alert.escalated,

    kyt.screening.timeout, kyt.screening.requeued, kyt.screening.completed.


    Use **GET /v1/webhooks/event-types** for the authoritative machine-readable
    list.


    **On-chain subscription webhooks** (/subscriptions) deliver these event

    types: incoming_native_tx, incoming_token_tx, incoming_nft_transfer,

    incoming_internal_tx, outgoing_native_tx, outgoing_token_tx,

    outgoing_nft_transfer, outgoing_internal_tx, tx_failed, tx_pending,

    confirmation_threshold_met, confirmation_finalized, contract_deployed,

    contract_executed, balance_changed.


    ## Versioning and deprecation


    Pin the API version with the **API-Version** request header; omit it to take

    the current default. Additive changes (new endpoints, new optional fields)
    are

    not versioned and may appear at any time — write tolerant clients.


    When an operation or version is scheduled for removal it is marked

    **deprecated** in this specification, and its responses carry a

    **Deprecation** header plus a **Sunset** header (RFC 8594) giving the date

    after which it stops functioning. Monitor those headers and migrate before
    the

    Sunset date.
  title: Bitnob Enterprise API
  version: v1
servers:
  - description: Production
    url: https://api.platform.bitnob.com
security:
  - ApiKeyAuth: []
paths:
  /transactions:
    get:
      tags:
        - Transactions
      summary: List transactions
      description: >-
        Returns a paginated list of transactions. Filterable by wallet_id,
        status, direction, asset, and created_at range.
      operationId: getTransactions
      parameters:
        - description: API version (defaults to the latest supported version if omitted).
          in: header
          name: API-Version
          required: false
          schema:
            default: v1
            enum:
              - v1
            type: string
        - description: Opaque cursor; returns the page after this item.
          in: query
          name: starting_after
          required: false
          schema:
            type: string
        - description: Opaque cursor; returns the page before this item.
          in: query
          name: ending_before
          required: false
          schema:
            type: string
        - description: Page size; bounded by the endpoint maximum.
          in: query
          name: limit
          required: false
          schema:
            type: integer
        - description: Filter by wallet UUID.
          in: query
          name: wallet_id
          required: false
          schema:
            format: uuid
            type: string
        - description: Filter by transaction status.
          in: query
          name: status
          required: false
          schema:
            type: string
        - description: Filter by direction.
          in: query
          name: direction
          required: false
          schema:
            enum:
              - inbound
              - outbound
            type: string
        - description: Filter by asset symbol (e.g. USDT).
          in: query
          name: asset
          required: false
          schema:
            type: string
        - description: Created at >= this RFC3339 timestamp.
          in: query
          name: created_at[gte]
          required: false
          schema:
            format: date-time
            type: string
        - description: Created at <= this RFC3339 timestamp.
          in: query
          name: created_at[lte]
          required: false
          schema:
            format: date-time
            type: string
      responses:
        '200':
          content:
            application/json:
              example:
                data:
                  - amount: '25.5'
                    amount_raw: '25500000'
                    asset: USDC
                    asset_decimals: 6
                    block_hash: >-
                      0x8f4c1b8e2a7d3f06c5b1a09e8d7c6f5e4d3c2b1a09f8e7d6c5b4a3928170615f
                    block_number: 21345678
                    broadcast_at: '2026-05-16T10:30:02Z'
                    chain: ethereum
                    completed_at: '2026-05-16T10:30:45Z'
                    confirmations: 12
                    created_at: '2026-05-16T10:30:00Z'
                    direction: outbound
                    execute_at: '2026-05-16T11:30:00Z'
                    fee_amount: '0.0012'
                    fee_amount_raw: '1200000000000000'
                    fee_asset: ETH
                    fee_decimals: 18
                    from_address: '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE'
                    gas_station_id: 9b2e7a1c-3d4f-4a8b-9c0d-1e2f3a4b5c6d
                    id: 7c9e6679-7425-40de-944b-e07fc1f90ae7
                    network: mainnet
                    note: Invoice 4471 payout
                    object: transaction
                    realized_fee_amount: '0.001'
                    realized_fee_amount_raw: '1000000000000000'
                    realized_fee_asset: ETH
                    realized_fee_decimals: 18
                    signed_at: '2026-05-16T10:30:01Z'
                    sponsored: false
                    status: completed
                    to_address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
                    tx_hash: >-
                      0x9f4c1b8e2a7d3f06c5b1a09e8d7c6f5e4d3c2b1a09f8e7d6c5b4a3928170615f
                    wallet_id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
                has_more: false
                next_cursor: ''
                object: list
                url: ''
              schema:
                properties:
                  data:
                    items:
                      $ref: '#/components/schemas/TransactionResponse'
                    type: array
                  has_more:
                    type: boolean
                  next_cursor:
                    description: >-
                      Opaque cursor for the next page; pass as starting_after.
                      Present only when has_more is true.
                    type: string
                  object:
                    example: list
                    type: string
                  url:
                    type: string
                type: object
          description: Success
          headers:
            X-RateLimit-Limit:
              description: Requests permitted in the current window.
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: Unix time (seconds) at which the rate-limit window resets.
              schema:
                type: integer
            X-RateLimit-Scope:
              description: >-
                Scope the rate limit was applied at: key for authenticated API
                traffic, or ip for unauthenticated/failed-auth traffic. RPC
                proxy traffic is limited separately (per second, per
                organization) and is not bucketed here.
              schema:
                type: string
            X-Request-Id:
              description: Unique id for this request; quote it in support requests.
              schema:
                type: string
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Invalid request
        '401':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Authentication required
        '403':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Insufficient permissions
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Rate limit exceeded
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
        '500':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          description: Internal server error
      security:
        - ApiKeyAuth: []
components:
  schemas:
    TransactionResponse:
      properties:
        amount:
          description: >-
            Transferred amount as a human-readable decimal in the asset's
            display unit
          example: '25.5'
          type: string
        amount_raw:
          description: Transferred amount as an integer in the asset's smallest base unit
          example: '25500000'
          type: string
        asset:
          description: Symbol of the transferred asset
          example: USDC
          type: string
        asset_decimals:
          description: >-
            Number of decimals used to convert the asset's base units to its
            display unit
          example: 6
          type: integer
        block_hash:
          description: Hash of the block the transaction was included in
          example: '0x8f4c1b8e2a7d3f06c5b1a09e8d7c6f5e4d3c2b1a09f8e7d6c5b4a3928170615f'
          type: string
        block_number:
          description: Number of the block the transaction was included in
          example: 21345678
          type: integer
        broadcast_at:
          description: ISO-8601 timestamp when the transaction was broadcast to the network
          example: '2026-05-16T10:30:02Z'
          format: date-time
          type: string
        chain:
          description: Blockchain the transaction was made on
          example: ethereum
          type: string
        completed_at:
          description: ISO-8601 timestamp when the transaction reached a completed state
          example: '2026-05-16T10:30:45Z'
          format: date-time
          type: string
        confirmations:
          description: Number of block confirmations for the transaction
          example: 12
          type: integer
        created_at:
          description: ISO-8601 timestamp when the transaction was created
          example: '2026-05-16T10:30:00Z'
          format: date-time
          type: string
        direction:
          description: >-
            Transaction direction relative to the wallet (e.g. inbound,
            outbound)
          example: outbound
          type: string
        error_message:
          description: Human-readable error message when the transaction failed
          type: string
        execute_at:
          description: >-
            When a policy-delayed transaction (status=delayed) is scheduled to
            execute. Present only while the transaction is time-locked.
          example: '2026-05-16T11:30:00Z'
          format: date-time
          type: string
        fee_amount:
          description: >-
            Estimated network fee as a human-readable decimal in the fee asset's
            display unit
          example: '0.0012'
          type: string
        fee_amount_raw:
          description: >-
            Estimated network fee as an integer in the fee asset's smallest base
            unit
          example: '1200000000000000'
          type: string
        fee_asset:
          description: >-
            Symbol of the asset fees are denominated in (usually the chain's
            native coin)
          example: ETH
          type: string
        fee_decimals:
          description: >-
            Number of decimals used to convert the fee asset's base units to its
            display unit
          example: 18
          type: integer
        from_address:
          description: Sender address
          example: '0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE'
          type: string
        gas_station_id:
          description: Identifier of the gas station used to fund the transaction's fees
          example: 9b2e7a1c-3d4f-4a8b-9c0d-1e2f3a4b5c6d
          type: string
        id:
          description: Unique identifier of the transaction
          example: 7c9e6679-7425-40de-944b-e07fc1f90ae7
          type: string
        network:
          description: Network the transaction was made on (e.g. mainnet)
          example: mainnet
          type: string
        note:
          description: Optional partner-supplied note attached to the transaction
          example: Invoice 4471 payout
          type: string
        object:
          description: Object type, always "transaction"
          example: transaction
          type: string
        realized_fee_amount:
          description: >-
            Actual network fee paid as a human-readable decimal in the fee
            asset's display unit
          example: '0.001'
          type: string
        realized_fee_amount_raw:
          description: >-
            Actual network fee paid as an integer in the fee asset's smallest
            base unit
          example: '1000000000000000'
          type: string
        realized_fee_asset:
          description: Symbol of the asset the realized fee is denominated in
          example: ETH
          type: string
        realized_fee_decimals:
          description: >-
            Number of decimals used to convert the realized fee asset's base
            units to its display unit
          example: 18
          type: integer
        signed_at:
          description: ISO-8601 timestamp when the transaction was signed
          example: '2026-05-16T10:30:01Z'
          format: date-time
          type: string
        sponsored:
          description: Whether the transaction's network fee was sponsored
          example: false
          type: boolean
        status:
          description: >-
            Lifecycle state. Non-terminal: pending_signature,
            pending_authorization, delayed, signed, awaiting_sponsorship,
            broadcasting, confirming, pending_screening. Terminal: completed,
            failed, cancelled, rejected. Poll this endpoint until a terminal
            state; an empty tx_hash means it has not been broadcast yet.
          enum:
            - pending_signature
            - pending_authorization
            - delayed
            - signed
            - awaiting_sponsorship
            - broadcasting
            - confirming
            - completed
            - failed
            - cancelled
            - rejected
            - pending_screening
          example: completed
          type: string
        to_address:
          description: Recipient address
          example: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
          type: string
        tx_hash:
          description: On-chain transaction hash; empty until the transaction is broadcast
          example: '0x9f4c1b8e2a7d3f06c5b1a09e8d7c6f5e4d3c2b1a09f8e7d6c5b4a3928170615f'
          type: string
        tx_type:
          description: Type of transaction
          type: string
        wallet_id:
          description: Identifier of the wallet the transaction belongs to
          example: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
          type: string
      type: object
    Error:
      description: Standard error envelope.
      properties:
        error:
          properties:
            code:
              type: string
            details:
              additionalProperties: true
              type: object
            message:
              type: string
            param:
              type: string
            request_id:
              type: string
            type:
              type: string
          type: object
      type: object
  securitySchemes:
    ApiKeyAuth:
      in: header
      name: X-API-Key
      type: apiKey

````

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