# Transaction tracking

Source: https://docs.settlemint.com/docs/developer-guides/operations/transaction-tracking
Understand DALP transaction finality, indexer visibility, and safe retry behavior.



Use this guide when DALP accepts a write request but the lifecycle is not yet clear to your integration. DALP finality means the queued operation reached a terminal platform state. Explorer and indexed-resource visibility can arrive slightly later. Start with the transaction request ID, then use the transaction hash when you need the mined receipt.

<Callout type="warn" title="Do not retry before checking status">
  A `CONFIRMATION_TIMEOUT` only means DALP did not confirm the final receipt before the request timed out. Check the
  queue status or receipt first. If the original transaction later succeeds, sending the same operation again can create
  duplicate on-chain work.
</Callout>

## Supply-changing retries [#supply-changing-retries]

For minting and other supply-changing writes, send one idempotency key for the approved instruction and keep polling the transaction request. DALP uses the sender, EVM chain, and idempotency key to recognize the same queued operation.

If the first request is still active, DALP keeps the retry attached to that in-flight workflow instead of creating a second independent mint. If the first request already completed inside the 24-hour idempotency window, DALP returns the recorded transaction result. After that window, reconcile the earlier transaction before issuing a replacement instruction with a new idempotency key. If the request failed, reached dead letter, or was cancelled, submit a new request only after reconciling the previous result.

This is a platform transaction-control boundary. DALP token contracts still enforce configured mint authority, such as the Supply Management role. For the full mint retry model, see [Mint replay, idempotency, and supply controls](/docs/architecture/security/replay-idempotency-mint-controls).

## Prerequisites [#prerequisites]

* Platform URL, such as `https://your-platform.example.com`
* API key. See [Getting Started](/docs/developer-guides/api-integration/getting-started).
* A transaction request ID from an asynchronous DALP response, or a transaction hash from an error response or `X-Transaction-Hash` header

## Choose the lookup [#choose-the-lookup]

| What you have                       | Endpoint or command                                     | Use it for                                                                                            |
| ----------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| Transaction request ID              | `GET /api/v2/transaction-requests/{transactionId}`      | Poll DALP queue state, sub-status, operation kind, transaction hash, block number, and error message. |
| Transaction hash                    | `GET /api/v2/blockchain-transactions/{transactionHash}` | Read the EVM transaction receipt, including success or reverted execution.                            |
| Authenticated CLI session plus hash | `dalp blockchain-transactions read {transactionHash}`   | Run the same receipt lookup from a terminal.                                                          |

## Finality and visibility [#finality-and-visibility]

DALP uses the transaction request state as the source of truth for platform finality. A transaction request is final when it reaches a terminal state: `COMPLETED`, `FAILED`, `DEAD_LETTER`, or `CANCELLED`. For a successful write, `COMPLETED` means DALP has finished the operation and recorded the result for the caller-visible request.

Application-level finality supports T+0 settlement workflows such as DvP and XvP coordination. Do not compare DALP finality directly to a generic EVM confirmation rule such as waiting 12 blocks. The EVM receipt and block number still matter for chain inspection. DALP integrations should gate workflow progress on the queue status returned by DALP.

Indexer visibility is a second phase. After a completed on-chain mutation has a known block number, DALP waits for the indexer to process that block before returning the completed status from the status endpoint. The wait is event-driven: DALP waits for indexer block-progress events and resolves immediately when the indexer has already caught up. If the indexer does not catch up within 30 seconds, the status endpoint still returns the current transaction status.

This means there are two safe observations to separate in your integration:

| Observation                                  | What it means                                                               | What to do                                                                                    |
| -------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| Queue status is `COMPLETED`                  | DALP finished the requested operation.                                      | Continue the business workflow. Do not resubmit the original write.                           |
| Receipt has `status: "Success"`              | The EVM transaction was mined successfully.                                 | Use the receipt for chain inspection, reconciliation, and explorer links.                     |
| A resource or explorer has not caught up yet | Indexed views can lag the terminal transaction state.                       | Poll the affected resource or explorer for a short period instead of retrying the write.      |
| Queue status is terminal but not `COMPLETED` | DALP stopped the operation as failed, cancelled, or requiring intervention. | Follow the error message, sub-status, or operational runbook before submitting a new request. |

For developer-facing workflows, treat `COMPLETED` as the point where the DALP operation is final. Treat indexer and explorer lag as a read-your-writes visibility concern. Never use a missing explorer entry or a temporarily stale indexed view as evidence that the original write did not happen.

## Caller visibility [#caller-visibility]

DALP scopes stored queue records to the caller. A status lookup returns the same not-found result for a missing transaction request and for a request outside the caller's wallet or organization scope.

Hash lookup has a slightly different boundary. DALP can still return public on-chain receipt data for a known hash, but a pending stored record or stored receipt must be visible to the API key, wallet, and organization used for the lookup. A not-found response from the hash endpoint means DALP did not find the transaction on-chain and did not find a caller-visible stored record.

## Steps [#steps]

<Steps>
  <Step>
    ### Poll the queue status when you have a request ID [#poll-the-queue-status-when-you-have-a-request-id]

    Use the status endpoint from asynchronous responses, retry workflows, and runbooks that return a transaction request ID:

    ```bash
    curl -X GET "https://your-platform.example.com/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef" \
      -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
    ```

    A status response includes the operation kind, queue state, optional sub-status, primary transaction hash, confirmed block number when available, and any error message DALP recorded:

    ```json
    {
      "data": {
        "transactionId": "01934567-89ab-7def-8123-456789abcdef",
        "kind": "token.mint",
        "status": "CONFIRMING",
        "subStatus": null,
        "transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
        "blockNumber": null,
        "errorMessage": null,
        "createdAt": "2026-03-09T10:00:00.000Z",
        "updatedAt": "2026-03-09T10:00:15.000Z"
      }
    }
    ```
  </Step>

  <Step>
    ### Read the receipt when you have a transaction hash [#read-the-receipt-when-you-have-a-transaction-hash]

    Use the hash endpoint after a timeout error, an `X-Transaction-Hash` header, or a status response that contains `transactionHash`:

    ```bash
    curl -X GET "https://your-platform.example.com/api/v2/blockchain-transactions/0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" \
      -H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"
    ```

    From an authenticated CLI session, run the same lookup with:

    ```bash
    dalp blockchain-transactions read 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
    ```

    A mined transaction returns a receipt:

    ```json
    {
      "data": {
        "transactionHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
        "from": "0xABCD1234567890abcdef1234567890abcdef1234",
        "receipt": {
          "status": "Success",
          "revertReason": null,
          "revertReasonDecoded": null,
          "blockNumber": "12345678",
          "blockHash": "0xabcd...",
          "gasUsed": "21000",
          "effectiveGasPrice": "1000000000",
          "from": "0xABCD1234567890abcdef1234567890abcdef1234",
          "to": "0x567890abcdef1234567890abcdef1234567890ab"
        }
      }
    }
    ```

    If DALP has a caller-visible stored record but the receipt is not available yet, `receipt` is `null`.
  </Step>

  <Step>
    ### Decide what to do next [#decide-what-to-do-next]

    Use the queue response and receipt together. Do not use one stale response as the only retry signal.

    | Result                                                          | Meaning                                                       | Next step                                                                                                 |
    | --------------------------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
    | Queue status is non-terminal                                    | DALP still has work in progress.                              | Wait a few seconds and poll the status endpoint again.                                                    |
    | Queue status is `COMPLETED` and the receipt status is `Success` | The operation finished.                                       | Continue your workflow. Do not resubmit the original request.                                             |
    | Receipt status is `Reverted`                                    | The EVM transaction was mined but failed.                     | Fix the cause shown by `revertReasonDecoded` or the related application error, then submit a new request. |
    | Status response includes `errorMessage`                         | DALP recorded a queue failure or timeout detail.              | Investigate the message before sending a replacement request.                                             |
    | Either lookup returns not found                                 | DALP cannot show a caller-visible record for that identifier. | Confirm the API key, wallet, organization, chain, and identifier before retrying.                         |
  </Step>
</Steps>

## Transaction state flow [#transaction-state-flow]

<Mermaid
  chart="`
flowchart TD
START[&#x22;DALP request accepted&#x22;] --> STATUS[&#x22;Poll transaction request status&#x22;]
STATUS --> ACTIVE{&#x22;Queue still active?&#x22;}
ACTIVE -->|Yes| WAIT[&#x22;Wait, then poll again&#x22;]
WAIT --> STATUS
ACTIVE -->|COMPLETED| FINAL[&#x22;DALP operation is final&#x22;]
FINAL --> RECEIPT[&#x22;Read blockchain transaction receipt&#x22;]
RECEIPT --> INDEXED{&#x22;Indexed view visible?&#x22;}
INDEXED -->|Yes| DONE[&#x22;Continue without retrying&#x22;]
INDEXED -->|No| VISIBILITY[&#x22;Poll resource or explorer for visibility&#x22;]
VISIBILITY --> INDEXED
ACTIVE -->|FAILED, DEAD_LETTER, or CANCELLED| FIX[&#x22;Use error details before a new request&#x22;]
STATUS -->|Not found| SCOPE[&#x22;Check API key, wallet, organization, chain, and ID&#x22;]
`"
/>

## Response fields [#response-fields]

### Queue status response [#queue-status-response]

| Field               | Type             | Description                                                                                     |
| ------------------- | ---------------- | ----------------------------------------------------------------------------------------------- |
| `transactionId`     | `string`         | Transaction request identifier.                                                                 |
| `kind`              | `string`         | Mutation kind submitted to the queue, such as `token.create` or `token.mint`.                   |
| `status`            | `string`         | Current queue state. Terminal states are `COMPLETED`, `FAILED`, `DEAD_LETTER`, and `CANCELLED`. |
| `subStatus`         | `string \| null` | Optional progress or failure detail.                                                            |
| `transactionHash`   | `string \| null` | Primary EVM transaction hash after broadcast.                                                   |
| `transactionHashes` | `string[]`       | Present only when a batch operation produced multiple transactions.                             |
| `blockNumber`       | `string \| null` | Confirmed block number when DALP can resolve it.                                                |
| `errorMessage`      | `string \| null` | Queue error or timeout detail when available.                                                   |
| `createdAt`         | `string`         | When DALP accepted the transaction request.                                                     |
| `updatedAt`         | `string`         | When the request status last changed.                                                           |

### Receipt response [#receipt-response]

| Field                         | Type                      | Description                                                                |
| ----------------------------- | ------------------------- | -------------------------------------------------------------------------- |
| `transactionHash`             | `string`                  | EVM transaction hash.                                                      |
| `from`                        | `string`                  | Sender wallet address.                                                     |
| `receipt`                     | `object \| null`          | EVM receipt. `null` means the receipt is not available to this lookup yet. |
| `receipt.status`              | `"Success" \| "Reverted"` | EVM execution result.                                                      |
| `receipt.revertReason`        | `string \| null`          | Raw revert reason when available.                                          |
| `receipt.revertReasonDecoded` | `string \| null`          | Decoded revert reason when available.                                      |
| `receipt.blockNumber`         | `string`                  | Block containing the transaction.                                          |
| `receipt.gasUsed`             | `string`                  | Gas consumed by the transaction.                                           |
| `receipt.effectiveGasPrice`   | `string`                  | Effective gas price paid.                                                  |

## Polling pattern [#polling-pattern]

1. Save the transaction request ID or hash from the original response.
2. Wait two to three seconds before the first follow-up lookup.
3. Poll the status endpoint every three to five seconds while the queue remains active.
4. Treat `COMPLETED` as final for the DALP operation.
5. Read the receipt once a transaction hash is available.
6. If the receipt succeeds but an indexed resource or explorer view is stale, poll the read side for visibility instead of retrying the write.
7. Stop when the queue reaches a terminal state, the receipt is mined, or the lookup returns a scoped not-found result that you have verified.

## Queue recovery after missing receipts [#queue-recovery-after-missing-receipts]

DALP keeps polling for a receipt after broadcast. If the receipt stays unavailable, DALP marks the queued request as timed out and hands the request to reconciliation rather than changing the queue record on every poll. That timeout is not a finality signal for client retries. DALP needs more evidence before it can classify the request as completed, failed, or ready for rescue.

Reconciliation checks the chain again. If the receipt appears, DALP completes or fails the stored request from that receipt. If the receipt is still missing, DALP waits until the dropped-from-mempool window has elapsed before it requeues the request. That window is one hour from the stored transaction update time. After that point, DALP requeues the request when retry budget remains, or moves it to manual intervention when the retry budget is exhausted.

For API clients, the rule stays simple: check the transaction status before submitting a replacement request. DALP's recovery protects the queue, but it does not make blind external retries safe.

## Troubleshooting [#troubleshooting]

| Issue                                                      | What to check                                                                                        |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| 404 after a known submission                               | Confirm the same API key, wallet, organization, chain, and identifier were used.                     |
| Receipt remains `null`                                     | Keep polling for a short period, then inspect the configured EVM explorer or operational logs.       |
| Receipt is `Reverted` without a decoded reason             | The contract may not expose a revert string. Check logs and the operation inputs.                    |
| Receipt shows success but indexed state is not visible yet | Treat the write as successful. Wait a few seconds and query the affected resource or explorer again. |

## Related operations [#related-operations]

* [Error handling](/docs/developer-guides/api-integration/error-handling): Handle API errors and blockchain reverts.
* [CLI command reference](/docs/developer-guides/cli/command-reference#blockchain-transaction-commands): Look up a transaction hash from an authenticated terminal.
* [Reconciliate balances](/docs/developer-guides/operations/reconciliate-balances): Compare on-chain state with your records.
* [API reference](/docs/developer-guides/api-integration/api-reference): Complete endpoint documentation.
