Skip to main content
Network failures and server-side errors are a reality in any distributed system. When you submit a transaction and receive a timeout or an HTTP 500 response, you cannot know with certainty whether the server processed the request before the error occurred. Retrying without a safeguard risks submitting the same withdrawal twice — a scenario no custody platform should tolerate. The sequence_id field solves this problem by making transaction requests idempotent.

What Idempotency Means for Transactions

An idempotent request is one that produces the same outcome no matter how many times you send it. In the context of Finrock transactions, idempotency means: if you submit a withdrawal with a given sequence_id, and then submit the exact same request again (with the same sequence_id), the platform will not execute a second transfer. Instead, it returns the original response from the first attempt. The funds leave your wallet exactly once.

The sequence_id Field

sequence_id is an optional string field available on the Create Transaction endpoint (POST /dac/v1/transaction). You supply the value yourself — it can be any arbitrary string that is unique within your wallet.
While sequence_id is optional, Finrock strongly recommends using it for every outgoing transfer. The risk of accidental double-spending without it is not worth the small implementation overhead.
Key properties of sequence_id:
  • You define it. Use a value that ties back to a unique record in your own system — an order ID, a payout ID, or a UUID you generate at payout creation time.
  • Scoped to your wallet. The sequence_id is only visible to users with access to your wallet. It is never broadcast on-chain or shared with recipients.
  • One send per ID. The platform confirms at most one outgoing transfer per sequence_id. All subsequent requests carrying the same ID return the original response without triggering a new transfer.

How It Works

1

Generate a unique sequence_id

Before submitting a withdrawal, create a sequence_id derived from a unique identifier in your system — such as your database’s primary key for the payout record.
2

Submit the transaction

Include the sequence_id in the request body when calling POST /dac/v1/transaction.
3

Handle the response

If you receive a successful 200 or 201 response, record the returned transaction ID alongside the sequence_id in your database. The transfer is now in progress.
4

Retry safely on failure

If you receive a timeout, connection error, or HTTP 500, retry the request with the same sequence_id and same payload. The platform will deduplicate the request and return the outcome of the first attempt — whether it succeeded or is still processing.
5

Poll for final status

Use the returned transaction ID to poll GET /dac/v1/transaction/{id} until the status reaches a terminal state. See the Transaction Statuses reference for the full list.

Code Example

The following example shows how to generate a robust sequence_id from an existing database record and use it when submitting a withdrawal.

Choosing a Good sequence_id

A good sequence_id is:
  • Unique per intended transfer. It should correspond to exactly one payout event in your system.
  • Stable across retries. You must be able to reproduce the exact same value when retrying, so derive it from a persistent record rather than generating a random UUID at request time.
  • Human-readable. Including a prefix (e.g., payout-, withdrawal-) makes it easier to trace in logs and customer support queries.
Uniqueness is your responsibility. If you reuse a sequence_id for a different transaction — for example, because you recycled an ID after a failed payout — the platform will return the original (old) response and will not execute the new transfer. Always ensure each distinct payout event uses a distinct sequence_id.

Supported Endpoints

sequence_id is currently supported on the following endpoint:

Create Transaction

POST /dac/v1/transaction — Submit an outgoing transfer with an idempotency key.