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. |
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.
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.
{
"signedTransaction": "0x02f8...",
"txHash": "0x1234abcd...",
"transactionId": "550e8400-e29b-41d4-a716-446655440000"
}
For sign-transaction, omitted broadcast is also sign-only — no record is written and no transactionId is returned.
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
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
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.
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 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:
{
"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.