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

# Idempotent Transactions Using sequence_id — Finrock

> Use sequence_id to safely retry failed transaction requests without risking duplicate withdrawals or double-spending in the Finrock API.

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.

<Note>
  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.
</Note>

**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

<Steps>
  <Step title="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.
  </Step>

  <Step title="Submit the transaction">
    Include the `sequence_id` in the request body when calling `POST /dac/v1/transaction`.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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](/reference/transaction-statuses) for the full list.
  </Step>
</Steps>

## 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.

<CodeGroup>
  ```javascript Node.js theme={null}
  const { v4: uuidv4 } = require('uuid');

  // Derive sequence_id from your internal payout record.
  // Using a deterministic ID (e.g., your DB primary key) means you can
  // reconstruct the same sequence_id on retry without storing it separately.
  const payoutId = 'payout_8472'; // your internal identifier
  const sequenceId = `payout-${payoutId}`; // e.g. "payout-payout_8472"

  const response = await fetch('https://api.finrock.io/dac/v1/transaction', {
    method: 'POST',
    headers: {
      'Authorization': 'Token YOUR_JWT',
      'x-api-key': 'YOUR_API_KEY',
      'content-type': 'application/json',
      'accept': 'application/json',
    },
    body: JSON.stringify({
      asset: 'USDT_ETH',
      amount: 500.00,
      destination_address: '0xRecipientAddress',
      destination_type: 'EXTERNAL',
      source_type: 'INTERNAL',
      sequence_id: sequenceId,  // idempotency key
    }),
  });

  const transaction = await response.json();
  console.log(`Transaction ID: ${transaction.id}, Status: ${transaction.status}`);
  ```

  ```python Python theme={null}
  import requests

  # Derive sequence_id from your internal payout record
  payout_id = 'payout_8472'
  sequence_id = f'payout-{payout_id}'

  response = requests.post(
      'https://api.finrock.io/dac/v1/transaction',
      headers={
          'Authorization': 'Token YOUR_JWT',
          'x-api-key': 'YOUR_API_KEY',
          'accept': 'application/json',
          'content-type': 'application/json',
      },
      json={
          'asset': 'USDT_ETH',
          'amount': 500.00,
          'destination_address': '0xRecipientAddress',
          'destination_type': 'EXTERNAL',
          'source_type': 'INTERNAL',
          'sequence_id': sequence_id,  # idempotency key
      }
  )

  transaction = response.json()
  print(f"Transaction ID: {transaction['id']}, Status: {transaction['status']}")
  ```

  ```bash cURL theme={null}
  curl --request POST \
       --url https://api.finrock.io/dac/v1/transaction \
       --header 'Authorization: Token YOUR_JWT' \
       --header 'x-api-key: YOUR_API_KEY' \
       --header 'accept: application/json' \
       --header 'content-type: application/json' \
       --data '{
         "asset": "USDT_ETH",
         "amount": 500.00,
         "destination_address": "0xRecipientAddress",
         "destination_type": "EXTERNAL",
         "source_type": "INTERNAL",
         "sequence_id": "payout-payout_8472"
       }'
  ```
</CodeGroup>

## 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.

| Pattern              | Example                | Notes                                         |
| -------------------- | ---------------------- | --------------------------------------------- |
| Database primary key | `payout-10482`         | Simple and deterministic                      |
| Order + attempt      | `order-99214-withdraw` | Useful if one order can have multiple payouts |
| UUID from DB record  | `a3f2c1d4-...`         | Good if you already store a UUID per payout   |

<Warning>
  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`.
</Warning>

## Supported Endpoints

`sequence_id` is currently supported on the following endpoint:

<Card title="Create Transaction" icon="arrow-right-arrow-left" href="/api-reference/transactions/create-transaction">
  `POST /dac/v1/transaction` — Submit an outgoing transfer with an idempotency key.
</Card>
