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

# Look up orders by transaction hash

> Look up confirmed on-chain payment associations for the merchant and store bound to your API key. Paid, underpaid and overpaid orders are supported. One hash can contain multiple payments, so `data` is a list grouped by order.

## Authentication and scope

Use a merchant API key bound to a store, with the existing Bearer API key and three signing headers. The API key alone determines the scope. Do not send merchant IDs, store IDs or identity headers to select another scope. Unbound keys cannot use this endpoint. See [Authentication and Signing](/en/api/getting-started#authentication-and-signing).

The GET body must be empty. Sign `{timestamp}.{nonce}.`, including the trailing dot, without appending query parameters. Use a fresh nonce and valid timestamp for every retry, and keep credentials on your server. Supply each query parameter exactly once; extra, array or duplicate parameters are rejected.

## Networks and hash normalization

| chain_type | Network | Input and normalization |
| --- | --- | --- |
| 1 | TRON | 64 hexadecimal characters, optionally prefixed once with 0x/0X; returned lowercase without 0x |
| 2 | Ethereum | 64 hexadecimal characters, optionally prefixed once with 0x/0X; returned lowercase with 0x |
| 3 | Solana | Base58 signature, 64 to 88 characters, decoding to exactly 64 bytes; case-sensitive and case-preserving |
| 4 | BSC | Same as Ethereum |
| 5 | Polygon | Same as Ethereum |
| 6 | Arbitrum | Same as Ethereum |
| 8 | Base | Same as Ethereum |

The network is required and cannot be inferred from the hash. Unlisted networks return 400. `tx_hash` accepts up to 128 characters and trims surrounding whitespace. Internal spaces, repeated prefixes, invalid characters or lengths and full explorer URLs return 400. Do not lowercase Solana signatures. Production and test endpoints query their own environments.

## Amounts and payment status

`amount_total/currency` is the original order amount. `target_amount_total/target_currency` is the required token amount. `amount_received_total/paid_currency` is the cumulative order receipt. `matched_payments` contains valid payments for this hash only. Do not treat cumulative receipts as this hash's payment amount.

Amounts are integers in business minor units. USDT/USDC use two decimal places: 1200 means 12.00, not a blockchain raw amount with 6/18 decimals. Handle int64 values precisely without floating-point precision loss. An underpaid order may require USDT but have a recognized USDC payment; required and received currencies are returned separately and must not be subtracted across currencies.

`payment_status` retains the actual order status. Underpaid or overpaid orders may be pending, processing, failed or expired. A match does not establish order payment success; only paid indicates a paid order. A shortfall can also have been accepted by an existing merchant tolerance policy. `transfer_status` classifies the amount; read both statuses separately.

## Empty results, errors and retries

No match, pending recognition, no publishable association and another merchant or store's transaction all return 200 with `data: []`. Candidate orders, anomalous receipt details and other tenants' information are not returned. Only completed payments with valid amounts, currencies and established order ownership are visible. An empty result does not prove no on-chain payment occurred, funds were lost or merchant settlement completed. Incomplete historical records may not be searchable.

Invalid parameters return 400, invalid authentication or store binding returns 401, rate limiting returns 429 and unavailable lookup returns 503. A query handles at most 100 matching payment records; exceeding this bound returns 503 without silently truncating results. Real query failures are never disguised as empty lists. Respect Retry-After on 429; use backoff and a fresh nonce on 503.

Responses must not be cached. Prefer [webhook notifications](/en/api/webhook-notifications) over frequent polling. Use this endpoint for reconciliation and occasional checks; it does not repair payments, post funds or issue refunds. Examples are fictional. The Node.js sample calculates the full signature; the curl sample requires signing headers prepared in advance.



## OpenAPI

````yaml /en/api/openapi.json get /api/v1/checkout/sessions/by-transaction
openapi: 3.1.0
info:
  title: StablePay Payment API
  version: '2026-03-31'
  summary: Payment session and payment method APIs.
  description: >-
    OpenAPI specification for StablePay Payment APIs.


    Notes:

    - Base URL: https://api.stablepay.co

    - Authentication: Bearer API Key + `X-StablePay-Timestamp` +
    `X-StablePay-Nonce` + `X-StablePay-Signature`

    - Signed payload: `{timestamp}.{nonce}.{requestBody}`

    - Signature algorithm: HMAC-SHA256, lowercase hex

    - For GET requests, `requestBody` is an empty string.
servers:
  - url: https://api.stablepay.co
    description: Production
security:
  - bearerAuth: []
    stablepayTimestamp: []
    stablepayNonce: []
    stablepaySignature: []
tags:
  - name: Payment Sessions
    description: Create, retrieve, and cancel payment sessions.
  - name: Payment Methods
    description: Retrieve payment methods.
paths:
  /api/v1/checkout/sessions/by-transaction:
    get:
      tags:
        - Payment Sessions
      summary: Look up orders by transaction hash
      description: >-
        Look up confirmed on-chain payment associations for the merchant and
        store bound to your API key. Paid, underpaid and overpaid orders are
        supported. One hash can contain multiple payments, so `data` is a list
        grouped by order.


        ## Authentication and scope


        Use a merchant API key bound to a store, with the existing Bearer API
        key and three signing headers. The API key alone determines the scope.
        Do not send merchant IDs, store IDs or identity headers to select
        another scope. Unbound keys cannot use this endpoint. See
        [Authentication and
        Signing](/en/api/getting-started#authentication-and-signing).


        The GET body must be empty. Sign `{timestamp}.{nonce}.`, including the
        trailing dot, without appending query parameters. Use a fresh nonce and
        valid timestamp for every retry, and keep credentials on your server.
        Supply each query parameter exactly once; extra, array or duplicate
        parameters are rejected.


        ## Networks and hash normalization


        | chain_type | Network | Input and normalization |

        | --- | --- | --- |

        | 1 | TRON | 64 hexadecimal characters, optionally prefixed once with
        0x/0X; returned lowercase without 0x |

        | 2 | Ethereum | 64 hexadecimal characters, optionally prefixed once
        with 0x/0X; returned lowercase with 0x |

        | 3 | Solana | Base58 signature, 64 to 88 characters, decoding to
        exactly 64 bytes; case-sensitive and case-preserving |

        | 4 | BSC | Same as Ethereum |

        | 5 | Polygon | Same as Ethereum |

        | 6 | Arbitrum | Same as Ethereum |

        | 8 | Base | Same as Ethereum |


        The network is required and cannot be inferred from the hash. Unlisted
        networks return 400. `tx_hash` accepts up to 128 characters and trims
        surrounding whitespace. Internal spaces, repeated prefixes, invalid
        characters or lengths and full explorer URLs return 400. Do not
        lowercase Solana signatures. Production and test endpoints query their
        own environments.


        ## Amounts and payment status


        `amount_total/currency` is the original order amount.
        `target_amount_total/target_currency` is the required token amount.
        `amount_received_total/paid_currency` is the cumulative order receipt.
        `matched_payments` contains valid payments for this hash only. Do not
        treat cumulative receipts as this hash's payment amount.


        Amounts are integers in business minor units. USDT/USDC use two decimal
        places: 1200 means 12.00, not a blockchain raw amount with 6/18
        decimals. Handle int64 values precisely without floating-point precision
        loss. An underpaid order may require USDT but have a recognized USDC
        payment; required and received currencies are returned separately and
        must not be subtracted across currencies.


        `payment_status` retains the actual order status. Underpaid or overpaid
        orders may be pending, processing, failed or expired. A match does not
        establish order payment success; only paid indicates a paid order. A
        shortfall can also have been accepted by an existing merchant tolerance
        policy. `transfer_status` classifies the amount; read both statuses
        separately.


        ## Empty results, errors and retries


        No match, pending recognition, no publishable association and another
        merchant or store's transaction all return 200 with `data: []`.
        Candidate orders, anomalous receipt details and other tenants'
        information are not returned. Only completed payments with valid
        amounts, currencies and established order ownership are visible. An
        empty result does not prove no on-chain payment occurred, funds were
        lost or merchant settlement completed. Incomplete historical records may
        not be searchable.


        Invalid parameters return 400, invalid authentication or store binding
        returns 401, rate limiting returns 429 and unavailable lookup returns
        503. A query handles at most 100 matching payment records; exceeding
        this bound returns 503 without silently truncating results. Real query
        failures are never disguised as empty lists. Respect Retry-After on 429;
        use backoff and a fresh nonce on 503.


        Responses must not be cached. Prefer [webhook
        notifications](/en/api/webhook-notifications) over frequent polling. Use
        this endpoint for reconciliation and occasional checks; it does not
        repair payments, post funds or issue refunds. Examples are fictional.
        The Node.js sample calculates the full signature; the curl sample
        requires signing headers prepared in advance.
      operationId: lookupPaymentSessionsByTransaction
      parameters:
        - name: chain_type
          in: query
          required: true
          description: Required network; supported values and formats are listed above.
          schema:
            type: integer
            enum:
              - 1
              - 2
              - 3
              - 4
              - 5
              - 6
              - 8
          example: 4
        - name: tx_hash
          in: query
          required: true
          description: >-
            Complete transaction hash with surrounding whitespace trimmed; not
            an address, order ID or explorer URL.
          schema:
            type: string
            minLength: 1
            maxLength: 128
          example: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
        - $ref: '#/components/parameters/XStablePayTimestamp'
        - $ref: '#/components/parameters/XStablePayNonce'
        - $ref: '#/components/parameters/XStablePaySignature'
      responses:
        '200':
          description: Query successful; an empty list is a normal result.
          headers:
            Cache-Control:
              description: Responses must not be cached.
              schema:
                type: string
                enum:
                  - no-store
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionLookupResponse'
              examples:
                paid:
                  summary: >-
                    Paid order, with different transaction and cumulative
                    receipts
                  value:
                    chain_type: 4
                    tx_hash: >-
                      0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                    data:
                      - id: example-session
                        order_id: example-merchant-order
                        payment_status: paid
                        transfer_status: paid
                        amount_total: 1200
                        currency: usd
                        target_amount_total: 1200
                        target_currency: USDT
                        paid_currency: USDT
                        amount_received_total: 1200
                        matched_payments:
                          - payment_id: example-payment
                            paid_currency: USDT
                            amount_received: 800
                partially_paid:
                  summary: Expired after partial payment; actual status retained
                  value:
                    chain_type: 4
                    tx_hash: >-
                      0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                    data:
                      - id: example-session
                        order_id: example-merchant-order
                        payment_status: expired
                        transfer_status: partially_paid
                        amount_total: 1200
                        currency: usd
                        target_amount_total: 1200
                        target_currency: USDT
                        paid_currency: USDT
                        amount_received_total: 800
                        matched_payments:
                          - payment_id: example-payment
                            paid_currency: USDT
                            amount_received: 800
                overpaid:
                  summary: Overpaid and completed
                  value:
                    chain_type: 4
                    tx_hash: >-
                      0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                    data:
                      - id: example-session
                        order_id: example-merchant-order
                        payment_status: paid
                        transfer_status: overpaid
                        amount_total: 1200
                        currency: usd
                        target_amount_total: 1200
                        target_currency: USDT
                        paid_currency: USDT
                        amount_received_total: 1500
                        matched_payments:
                          - payment_id: example-payment
                            paid_currency: USDT
                            amount_received: 1500
                alternative_currency:
                  summary: Recognized USDC partial payment against required USDT
                  value:
                    chain_type: 4
                    tx_hash: >-
                      0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                    data:
                      - id: example-session
                        order_id: example-merchant-order
                        payment_status: expired
                        transfer_status: partially_paid
                        amount_total: 1200
                        currency: usd
                        target_amount_total: 1200
                        target_currency: USDT
                        paid_currency: USDC
                        amount_received_total: 800
                        matched_payments:
                          - payment_id: example-payment
                            paid_currency: USDC
                            amount_received: 800
                empty:
                  summary: No match visible to this merchant and store
                  value:
                    chain_type: 4
                    tx_hash: >-
                      0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                    data: []
        '400':
          description: >-
            Invalid network, hash, duplicate/extra parameters or nonempty GET
            body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Authentication failed, no store binding or invalid identity.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate limited; retry after Retry-After.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          headers:
            Retry-After:
              description: Delay before retrying, in seconds.
              schema:
                type: integer
                minimum: 0
        '503':
          description: >-
            Lookup unavailable or the 100-payment-record bound exceeded; this
            does not mean unpaid.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      x-codeSamples:
        - lang: javascript
          label: Node.js
          source: >-
            import { createHmac, randomUUID } from 'node:crypto';


            const timestamp = Math.floor(Date.now() / 1000).toString();

            const nonce = randomUUID();

            const signature = createHmac('sha256',
            process.env.STABLEPAY_SECRET_KEY)
              .update(`${timestamp}.${nonce}.`)
              .digest('hex');
            const url = new
            URL('https://api.stablepay.co/api/v1/checkout/sessions/by-transaction');

            url.searchParams.set('chain_type', '4');

            url.searchParams.set('tx_hash', process.env.TRANSACTION_HASH);

            const response = await fetch(url, {
              method: 'GET',
              headers: {
                Authorization: `Bearer ${process.env.STABLEPAY_API_KEY}`,
                'X-StablePay-Timestamp': timestamp,
                'X-StablePay-Nonce': nonce,
                'X-StablePay-Signature': signature,
              },
            });

            console.log(response.status, await response.json());
        - lang: bash
          label: curl
          source: >-
            curl --get
            'https://api.stablepay.co/api/v1/checkout/sessions/by-transaction' \
              --data-urlencode 'chain_type=4' \
              --data-urlencode "tx_hash=$TRANSACTION_HASH" \
              --header "Authorization: Bearer $STABLEPAY_API_KEY" \
              --header "X-StablePay-Timestamp: $TIMESTAMP" \
              --header "X-StablePay-Nonce: $NONCE" \
              --header "X-StablePay-Signature: $SIGNATURE"
components:
  parameters:
    XStablePayTimestamp:
      name: X-StablePay-Timestamp
      in: header
      required: true
      description: Unix timestamp in seconds. Must be within 5 minutes of server time.
      schema:
        type: string
      example: '1735689600'
    XStablePayNonce:
      name: X-StablePay-Nonce
      in: header
      required: true
      description: Replay-protection nonce, 16-64 characters. Use a fresh UUID per request.
      schema:
        type: string
        minLength: 16
        maxLength: 64
      example: 550e8400-e29b-41d4-a716-446655440000
    XStablePaySignature:
      name: X-StablePay-Signature
      in: header
      required: true
      description: >-
        Lowercase hex HMAC-SHA256 of `{timestamp}.{nonce}.{requestBody}` signed
        with the merchant API Secret. For GET requests, `requestBody` is an
        empty string.
      schema:
        type: string
      example: a1b2c3d4e5f6...
  schemas:
    TransactionLookupResponse:
      type: object
      required:
        - chain_type
        - tx_hash
        - data
      properties:
        chain_type:
          type: integer
          description: Queried network.
          enum:
            - 1
            - 2
            - 3
            - 4
            - 5
            - 6
            - 8
        tx_hash:
          type: string
          description: Normalized query hash; Solana preserves original case.
        data:
          type: array
          description: Orders visible to this merchant and store; [] when none match.
          maxItems: 100
          items:
            $ref: '#/components/schemas/TransactionLookupOrder'
      additionalProperties: false
    ErrorResponse:
      type: object
      description: Error response.
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              description: Error type.
            code:
              type: string
              description: Error code.
            message:
              type: string
              description: Error message.
            param:
              type: string
              description: Related parameter.
            doc_url:
              type: string
              description: Error documentation URL.
            decline_code:
              type: string
              description: Decline code.
            request_log_url:
              type: string
              description: Request log URL.
            charge:
              type: string
              description: Related payment ID.
        request_id:
          type: string
          description: Request ID.
        timestamp:
          type: integer
          format: int64
          description: Error timestamp in Unix seconds.
    TransactionLookupOrder:
      type: object
      required:
        - id
        - order_id
        - payment_status
        - transfer_status
        - amount_total
        - currency
        - target_amount_total
        - target_currency
        - paid_currency
        - amount_received_total
        - matched_payments
      properties:
        id:
          type: string
          description: >-
            StablePay order/checkout session ID, usable with the existing
            session endpoint.
        order_id:
          type: string
          description: Merchant order ID.
        payment_status:
          type: string
          description: Actual order status; a lookup match does not change it.
          enum:
            - pending
            - processing
            - paid
            - failed
            - expired
        transfer_status:
          type: string
          description: >-
            Amount classification. pending/confirming means classification is
            not complete; this field alone does not determine recognition of the
            matched payment.
          enum:
            - pending
            - confirming
            - partially_paid
            - paid
            - overpaid
        amount_total:
          type: integer
          description: Original order amount in currency business minor units.
          format: int64
          minimum: 1
        currency:
          type: string
          description: >-
            Original order currency, lowercase as in the existing API, such as
            usd or eur.
          example: usd
        target_amount_total:
          type: integer
          description: Required token amount in target_currency business minor units.
          format: int64
          minimum: 1
        target_currency:
          type: string
          description: >-
            Required token currency; it can differ from the actual
            paid_currency.
          enum:
            - USDT
            - USDC
        paid_currency:
          type: string
          description: Actual received currency.
          enum:
            - USDT
            - USDC
        amount_received_total:
          type: integer
          description: >-
            Cumulative order receipts in the order paid_currency business minor
            units, not this hash's amount.
          format: int64
          minimum: 1
        matched_payments:
          type: array
          description: >-
            Valid payment records for this hash only; there may be multiple
            records.
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/TransactionLookupPayment'
      additionalProperties: false
    TransactionLookupPayment:
      type: object
      required:
        - payment_id
        - paid_currency
        - amount_received
      properties:
        payment_id:
          type: string
          description: Payment record ID, distinct from the order/session ID.
        paid_currency:
          type: string
          description: Actual received currency.
          enum:
            - USDT
            - USDC
        amount_received:
          type: integer
          description: >-
            This payment amount in paid_currency business minor units; USDT/USDC
            use two decimal places.
          format: int64
          minimum: 1
      additionalProperties: false
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: Bearer API Key, for example `Bearer sk_live_xxx`.
    stablepayTimestamp:
      type: apiKey
      in: header
      name: X-StablePay-Timestamp
      description: Unix timestamp in seconds. Must be within 5 minutes of server time.
    stablepayNonce:
      type: apiKey
      in: header
      name: X-StablePay-Nonce
      description: Replay-protection nonce, 16-64 characters. Use a fresh UUID per request.
    stablepaySignature:
      type: apiKey
      in: header
      name: X-StablePay-Signature
      description: >-
        Lowercase hex HMAC-SHA256 of `{timestamp}.{nonce}.{requestBody}` signed
        with the merchant API Secret. For GET requests, `requestBody` is an
        empty string.

````

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