# Transaction queue lifecycle and recovery

Source: https://docs.settlemint.com/docs/api-reference/observability/transaction-queue-lifecycle
Reference for the DALP transaction queue API: list and filter queued requests, read lifecycle status, cancel pending transactions with replace-by-fee, and run operator recovery for stuck or dead-lettered work.



Every write that DALP accepts becomes a queued transaction request with its own identifier and lifecycle. A bank operating regulated assets needs to see that queue the way it sees a payment rail. List what is in flight, read where any request sits in its lifecycle, stop a request that should not proceed, and recover a request that is stuck. The transaction queue endpoints expose that surface as published REST.

Use this reference when your integration monitors queued work, builds an operator console over the queue, or implements recovery runbooks. DALP returns a `transactionId` whenever it accepts an asynchronous write, and every endpoint below works from that identifier. To poll a single request as a task while you wait on an asynchronous response, start with [Transaction tracking](/docs/developers/operations/transaction-tracking). To read the mined EVM receipt for a known hash, use the [blockchain transaction receipt](/docs/developers/operations/transaction-tracking#read-the-receipt-when-you-have-a-transaction-hash) lookup.

All endpoints are versioned under `/api/v2`. Reads and cancellation are scoped to the caller's wallet set; the recovery operations require organization administrator permission.

## The lifecycle [#the-lifecycle]

A transaction request moves through an eleven-state machine from acceptance to a terminal outcome. DALP treats the queue state as the source of truth for platform finality, not a raw chain confirmation count.

<Callout type="info" title="Why the queue state is the finality signal">
  A request is final when it reaches a terminal state, not when a fixed number of blocks pass. Gate your workflow on the
  queue status DALP returns. Use the EVM receipt for chain inspection and reconciliation, not as the primary finality
  test.
</Callout>

| State              | Meaning                                                 | Terminal |
| ------------------ | ------------------------------------------------------- | -------- |
| `RECEIVED`         | Request accepted, awaiting queue placement              | No       |
| `QUEUED`           | Waiting for processing capacity                         | No       |
| `PREPARING`        | Validating the call, estimating gas, allocating a nonce | No       |
| `PENDING_APPROVAL` | Awaiting an external custody-provider approval          | No       |
| `SIGNING`          | Being signed by the signer provider                     | No       |
| `BROADCASTING`     | Signed transaction submitted to the network             | No       |
| `CONFIRMING`       | Included in a block, awaiting confirmation              | No       |
| `COMPLETED`        | Confirmed and finalized                                 | Yes      |
| `FAILED`           | Failed after retries were exhausted                     | Yes      |
| `DEAD_LETTER`      | Stopped, manual intervention required                   | Yes      |
| `CANCELLED`        | Cancelled by the caller or the system                   | Yes      |

Two terminal states offer operator rescue. `FAILED` is terminal: retries are exhausted and the request will not advance on its own. An operator can still force a `FAILED` request back to `QUEUED` through the recovery endpoint. `DEAD_LETTER` is a separate terminal state for requests that were stopped and need manual intervention; an operator can also rescue `DEAD_LETTER` back to `QUEUED`. Every other terminal state is final.

When a request fails, the status read carries a `subStatus` with finer detail, such as `NONCE_CONFLICT`, `INSUFFICIENT_FUNDS`, `TIMEOUT`, `REPLACED`, or `DROPPED`. Treat `subStatus` as diagnostic context for the current `status`, not as a separate lifecycle.

## List the queue [#list-the-queue]

```http
GET /api/v2/transaction-requests
```

Return a paginated list of queue entries for monitoring and operator views. Each row carries the queue state, sender, target chain, primary hash once broadcast, and a decoded action label.

Filter with JSON:API query parameters. Combine filters to narrow a dashboard or a recovery sweep.

| Parameter               | Filters on                      | Example                             |
| ----------------------- | ------------------------------- | ----------------------------------- |
| `filter[status]`        | Lifecycle state                 | `filter[status]=PENDING_APPROVAL`   |
| `filter[operationType]` | Mutation kind                   | `filter[operationType]=token.mint`  |
| `filter[fromAddress]`   | Sender wallet                   | `filter[fromAddress]=0x71C7…`       |
| `filter[chainId]`       | Target chain                    | `filter[chainId]=1`                 |
| `filter[createdAt]`     | Acceptance time                 | `filter[createdAt][gte]=2026-03-01` |
| `filter[updatedAt]`     | Last status change              | `filter[updatedAt][gte]=2026-03-09` |
| `filter[q]`             | Free-text search across the row | `filter[q]=mint`                    |

Results sort by `createdAt` ascending by default. Override with `sort` and page with `page[limit]` (default 50, maximum 200) and `page[offset]`. Set `ownership=caller` to restrict the list to the caller's own transactions even for an administrator; the default `ownership=scoped` lets an administrator see the organization-wide wallet set.

```bash
curl --globoff -X GET \
  "$DALP_API_URL/api/v2/transaction-requests?filter[status]=PENDING_APPROVAL&page[limit]=20" \
  --header "X-Api-Key: $DALP_API_TOKEN"
```

```json
{
  "data": [
    {
      "transactionId": "01934567-89ab-7def-8123-456789abcdef",
      "kind": "token.mint",
      "status": "PENDING_APPROVAL",
      "subStatus": null,
      "fromAddress": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F",
      "chainId": 1,
      "transactionHash": null,
      "description": "mint(0x71C7656EC7ab88b098defB751B7401B5f6d8976F, 1000000)",
      "createdAt": "2026-03-09T10:00:00.000Z",
      "updatedAt": "2026-03-09T10:00:00.000Z"
    }
  ],
  "meta": { "total": 1 },
  "links": {
    "self": "/v2/transaction-requests?filter[status]=PENDING_APPROVAL&page[limit]=20&page[offset]=0",
    "first": "/v2/transaction-requests?filter[status]=PENDING_APPROVAL&page[limit]=20&page[offset]=0",
    "prev": null,
    "next": null,
    "last": "/v2/transaction-requests?filter[status]=PENDING_APPROVAL&page[limit]=20&page[offset]=0"
  }
}
```

The `description` field is the decoded call, such as `mint(0x71C7…, 1000000)`. The field is `null` when DALP cannot decode a readable label for that row; fall back to `kind`.

## Read a request [#read-a-request]

```http
GET /api/v2/transaction-requests/{transactionId}
```

Read the full lifecycle status for one request: the operation kind, queue state, optional sub-status, primary hash, confirmed block number when available, and any recorded error. When a failed write maps to a known DALP error, the response also carries a structured `contractError` next to the flat `errorMessage`.

```bash
curl -X GET \
  "$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef" \
  --header "X-Api-Key: $DALP_API_TOKEN"
```

```json
{
  "data": {
    "transactionId": "01934567-89ab-7def-8123-456789abcdef",
    "kind": "token.mint",
    "status": "FAILED",
    "subStatus": "NONCE_CONFLICT",
    "transactionHash": null,
    "blockNumber": null,
    "errorMessage": "Nonce conflict on sender wallet",
    "contractError": null,
    "createdAt": "2026-03-09T10:00:00.000Z",
    "updatedAt": "2026-03-09T10:00:15.000Z"
  }
}
```

A status read returns the same not-found result for a missing request and for one outside the caller's wallet or organization scope. For a continuous feed from a browser context, open the server-sent events stream at `GET /api/v2/transaction-requests/{transactionId}/stream`. The stream requires a same-origin request or matching `Origin`/`Host` headers; programmatic API-key clients that do not send browser `Sec-Fetch-Site` headers should keep polling the status read instead. The stream sends an initial snapshot, then one event per transition, until a terminal state closes it.

## Cancel a request [#cancel-a-request]

```http
DELETE /api/v2/transaction-requests/{transactionId}
```

Cancel a request that should not proceed. DALP cancels a pre-broadcast request immediately. A request already broadcast to the network is cancelled with a replace-by-fee, a zero-value self-transfer that supersedes the original at the same nonce.

```bash
curl -X DELETE \
  "$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef" \
  --header "X-Api-Key: $DALP_API_TOKEN"
```

```json
{
  "data": {
    "status": "cancellation_pending",
    "cancelTransactionId": "0198aa11-2233-7cee-8def-445566778899"
  }
}
```

The `status` field reports `cancelled` for an immediate pre-broadcast cancel, `cancellation_pending` when a replace-by-fee transaction is racing the original on-chain, or `error` when the cancel could not start. A `cancellation_pending` result includes `cancelTransactionId`, which is the replacement transaction hash returned by the EVM node, not a DALP queue request identifier. Track the replacement through the [blockchain transaction receipt](/docs/developers/operations/transaction-tracking#read-the-receipt-when-you-have-a-transaction-hash) lookup, not through the queue status read. Cancellation is scoped to the caller's wallet set: a request the caller does not own returns the same not-found result as a missing one.

<Callout type="warn" title="Cancel is not a duplicate-safety guarantee">
  A replace-by-fee can lose the race if the original confirms first. Read the request status after cancelling, and for
  supply-changing writes keep using the idempotency key model in [Mint replay, idempotency, and supply
  controls](/docs/compliance-security/security/replay-idempotency-mint-controls) rather than relying on cancel to undo a
  mint.
</Callout>

## Operator recovery [#operator-recovery]

Three operations let an administrator recover stuck or dead-lettered work. Each requires organization administrator permission and acts only on a request whose sender wallet belongs to the administrator's organization. A request outside that scope returns not found.

### Requeue a failed request [#requeue-a-failed-request]

```http
POST /api/v2/transaction-requests/{transactionId}/retries
```

Requeue a `FAILED` or `DEAD_LETTER` request for another attempt. DALP resets the retry counter, clears the error state, and returns the request to `QUEUED`. Send an optional body to override the gas price or nonce for the new attempt.

```bash
curl -X POST \
  "$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef/retries" \
  --header "X-Api-Key: $DALP_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{ "gasPrice": "20000000000" }'
```

```json
{
  "data": {
    "transactionId": "01934567-89ab-7def-8123-456789abcdef",
    "previousStatus": "DEAD_LETTER",
    "status": "QUEUED"
  }
}
```

### Force a request to failed [#force-a-request-to-failed]

```http
POST /api/v2/transaction-requests/{transactionId}/failures
```

Force a non-terminal request to `FAILED` with a required reason. Use this to close out a request that cannot complete. DALP records the reason in the request history, so the operation leaves an audit trail rather than a silent state change.

```bash
curl -X POST \
  "$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef/failures" \
  --header "X-Api-Key: $DALP_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{ "reason": "Manual intervention: stuck request after nonce conflict" }'
```

```json
{
  "data": {
    "transactionId": "01934567-89ab-7def-8123-456789abcdef",
    "previousStatus": "BROADCASTING",
    "status": "FAILED",
    "reason": "Manual intervention: stuck request after nonce conflict"
  }
}
```

### Reset the nonce tracker [#reset-the-nonce-tracker]

```http
PATCH /api/v2/transaction-requests/{transactionId}/nonce-tracker
```

Force-set the nonce tracker for the wallet that submitted a request. Use this to clear a stuck-nonce condition, where DALP's tracked nonce has drifted from the wallet's on-chain nonce and new transactions cannot proceed. DALP applies the nonce to the sender's tracker and returns the previous and new values.

```bash
curl -X PATCH \
  "$DALP_API_URL/api/v2/transaction-requests/01934567-89ab-7def-8123-456789abcdef/nonce-tracker" \
  --header "X-Api-Key: $DALP_API_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{ "nonce": 42 }'
```

```json
{
  "data": {
    "previous": 40,
    "new": 42
  }
}
```

A `previous` value of `null` means the tracker was not yet initialized for that wallet.

## Errors [#errors]

A request for a row that is not in the caller's wallet set returns `DALP-0383` (`TRANSACTION_NOT_FOUND`) with HTTP 404, indistinguishable from a genuinely missing identifier. The recovery routes require administrator permission; a caller without that permission receives `DALP-0147` (`OFFCHAIN_ORGANIZATION_PERMISSION_REQUIRED`) or `DALP-0148` (`OFFCHAIN_USER_PERMISSION_REQUIRED`) with HTTP 403, depending on whether the organization or the user lacks the required role. Failed writes surface their detail through `errorMessage` and, when the revert maps to a catalog entry, a structured `contractError`. See [Error handling](/docs/api-reference/errors/error-handling) for the full model and the [error code reference](/docs/api-reference/errors/error-code-reference) for individual codes.

## Related [#related]

* [Transaction tracking](/docs/developers/operations/transaction-tracking): poll a single request and read its receipt as a task.
* [Mint replay, idempotency, and supply controls](/docs/compliance-security/security/replay-idempotency-mint-controls): the duplicate-safety model for supply-changing writes.
* [Reporting and audit access](/docs/api-reference/observability/reporting-audit-access): pull audit trails and operational reporting.
* [API monitoring](/docs/api-reference/observability/api-monitoring): request metrics, logs, and endpoint health.
