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

# Transaction History

> Persisted transaction records, on-chain monitoring, and webhooks for REST API broadcasts

REST broadcasts create persisted transaction records that Para tracks to terminal status on-chain. Records are queryable, auditable, and emit webhooks when they confirm or fail — no partner-side indexer required.

Rows are written for `POST /v1/wallets/{walletId}/transfer` broadcasts and for `POST /v1/wallets/{walletId}/sign-transaction` when `broadcast: true`. Sign-only calls do not write rows.

## Status Lifecycle

```
pending → submitted → confirmed
                    → reverted
                    ↘ failed
```

| Status      | Meaning                                                                                           |
| ----------- | ------------------------------------------------------------------------------------------------- |
| `pending`   | Record inserted; broadcast not yet attempted.                                                     |
| `submitted` | RPC accepted the signed bytes. Monitor is running. Not terminal.                                  |
| `confirmed` | Included in a block, not reverted. `blockNumber` and `blockHash` are populated.                   |
| `reverted`  | Included on-chain but execution failed (EVM status=0 or Solana signature error). Terminal.        |
| `failed`    | Never broadcast, or broadcast was rejected by the RPC. `failureStage` identifies where. Terminal. |

<Note>
  Partner code must treat unknown status values as non-terminal. Para may add new statuses (`finalized`, `replaced`) in a later release without bumping the API version.
</Note>

## Response Shape

Calls that broadcast return a `transactionId` field and set an `x-transaction-id` response header. Use either to look up the record afterward.

```json theme={null}
{
  "signedTransaction": "0x02f8...",
  "txHash": "0x1234abcd...",
  "transactionId": "550e8400-e29b-41d4-a716-446655440000"
}
```

<Note>
  For `sign-transaction`, omitted `broadcast` is also sign-only — no record is written and no `transactionId` is returned.
</Note>

If a broadcast request fails after Para creates the transaction row, the error response still sets `x-transaction-id` and includes `transactionId` in the JSON body. Failures after signing also include `signedTransaction`. Use the transaction history record's `failureStage` and `failureCode` fields for persisted failure details.

## Reading Transaction History

### List transactions for a wallet

```bash theme={null}
curl -H "X-API-Key: $PARA_API_KEY" \
  "https://api.beta.getpara.com/v1/wallets/$WALLET_ID/transactions?status=confirmed&limit=20"
```

Filter by status via `?status=`. Filter by source operation via `?intentKind=transfer` or `?intentKind=sign_transaction`. Paginate via `?cursor=` using the opaque cursor returned in each response. Results are ordered by `createdAt` DESC.

### Look up a single transaction

```bash theme={null}
curl -H "X-API-Key: $PARA_API_KEY" \
  "https://api.beta.getpara.com/v1/wallets/$WALLET_ID/transactions/$TX_ID"
```

Records that belong to a different partner return `404` with an identical body to missing records.

## Record Fields

Each record includes `intentKind`, either `transfer` or `sign_transaction`.

For EVM `sign_transaction` records, Para stores `chainId`, `to`, and `value` when `value` was present in the request. `tokenAddress` is absent. Para does not decode calldata.

For Solana `sign_transaction` records, `to`, `value`, and `tokenAddress` are absent because Para does not decode arbitrary Solana instructions.

## Polling Recipe

When webhooks aren't practical, poll the record until it leaves the `submitted` state.

```typescript theme={null}
async function waitForConfirmation(walletId: string, transactionId: string) {
  const start = Date.now();
  let delayMs = 2000;
  const timeoutMs = 10 * 60 * 1000;

  while (Date.now() - start < timeoutMs) {
    const res = await fetch(
      `https://api.beta.getpara.com/v1/wallets/${walletId}/transactions/${transactionId}`,
      { headers: { 'X-API-Key': process.env.PARA_API_KEY! } },
    );
    const record = await res.json();

    if (record.status === 'confirmed' || record.status === 'reverted' || record.status === 'failed') {
      return record;
    }
    await new Promise((r) => setTimeout(r, delayMs));
    delayMs = Math.min(delayMs * 1.5, 15000);
  }
  throw new Error('timed out waiting for confirmation');
}
```

## Webhooks

Subscribe to two event types from the [Developer Portal](https://developer.getpara.com) to receive terminal-state notifications automatically:

| Event                        | When it fires                                                              |
| ---------------------------- | -------------------------------------------------------------------------- |
| `rest.transaction.confirmed` | Transaction included in a block without reverting.                         |
| `rest.transaction.failed`    | Transaction reverted on-chain, or monitor detected a terminal RPC failure. |

Payload example for `rest.transaction.confirmed`:

```json theme={null}
{
  "transactionId": "550e8400-e29b-41d4-a716-446655440000",
  "walletId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "partnerId": "11111111-2222-3333-4444-555555555555",
  "intentKind": "transfer",
  "type": "EVM",
  "hash": "0x1234abcd...",
  "blockNumber": "5127103",
  "blockHash": "0xdeadbeef...",
  "resolvedAt": "2026-04-23T12:00:00.000Z"
}
```

`rest.transaction.failed` mirrors the shape and adds `status` (`reverted` or `failed`), plus optional `failureStage`, `failureCode`, and `failureMessage` fields.

## Failure Fields

When `status = failed`, `failureStage` describes where the pipeline broke:

| Stage             | Meaning                                                                                                                                                                                               |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mpc_sign`        | The MPC signing ceremony errored before a signature was produced.                                                                                                                                     |
| `signature_apply` | The signature could not be attached to the transaction.                                                                                                                                               |
| `signer_verify`   | The recovered signer address did not match the wallet's public address (EVM only).                                                                                                                    |
| `broadcast`       | The RPC node rejected the signed bytes (e.g. `INSUFFICIENT_NATIVE_BALANCE`, `EXECUTION_FAILED`).                                                                                                      |
| `monitor_timeout` | The signed transaction was broadcast but didn't reach a terminal on-chain state within Para's 30-minute monitor window. The transaction may still confirm later — query the chain directly to verify. |

When `status = reverted`, the transaction reached the chain and executed with a revert — `blockNumber` and `blockHash` are populated, `failureStage` is absent, and `failureMessage` contains any error detail surfaced by the monitor.

## Broadcast Error Messages

REST `/transfer` now sources broadcast-failure messages from Para's shared broadcast helper rather than passing through raw ethers.js strings. If code parses the previous raw string format, update it to read `failureCode` (e.g. `INSUFFICIENT_NATIVE_BALANCE`, `EXECUTION_FAILED`) from the transaction record or failure webhook instead.

## Retention

Both `/transfer` broadcasts and `sign-transaction` calls with `broadcast: true` write rows. Retention remains deferred until the `rest_transfers` table reaches an agreed large-row or disk threshold.
