> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.clossir.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.clossir.com/_mcp/server.

# Idempotency

GETTING STARTED · SDK · IDEMPOTENCY

When a command returns `202 Accepted`, it enters an async processing pipeline. If your
request times out or you lose the response, you need to know whether it's safe to retry.

## Safe-by-design commands

Many Clossir API commands are idempotent by design — repeating the same request produces
the same outcome without side effects:

| Operation           | Idempotent by                   | How it works                                               |
| ------------------- | ------------------------------- | ---------------------------------------------------------- |
| Create attestation  | `partyId` + `type` + `chainEid` | Duplicate request returns the existing attestation         |
| Deploy asset        | `assetId` + `chainEid`          | Re-deploy to the same chain is a no-op if already deployed |
| Create organization | `externalId`                    | Same external ID returns the existing org                  |

For these endpoints, retrying after a timeout is safe — the API detects the duplicate and
returns the existing resource.

## Commands with side effects

Some commands are **not** inherently idempotent:

| Operation           | Why                                 |
| ------------------- | ----------------------------------- |
| Create transfer     | Each call creates a new transfer    |
| Create distribution | Each call queues a new distribution |

For these, use the `requestId` from the 202 response to check whether the original command
was received before retrying:

```ts title="safe-retry.ts"
import { SignumProblemDetailsError } from "@signum-tech/sdk/models/errors";

async function createTransferSafely(payload: TransferPayload) {
  try {
    return await signum.transfers.postTransfers(payload);
  } catch (e) {
    if (e instanceof SignumProblemDetailsError && e.status === 409) {
      // 409 Conflict — transfer already exists
      // The original request was processed; fetch it instead
      console.log("Transfer already created");
      return;
    }
    throw e;
  }
}
```

## Best practices

1. **Store the response** — always persist the `202` response body (especially `requestId`
   and the resource ID) before moving on. If your process crashes after receiving the
   response, you can look up the resource.

2. **Use webhooks** — for critical commands, subscribe to webhooks rather than relying on
   polling. The webhook payload includes the `requestId` from your original command. See
   [Async Operations](/getting-started/sdk/async-operations) for the correlation pattern.

3. **Configure retries conservatively for commands** — the SDK's built-in retry handles
   transport errors (timeouts, 5xx). For 202 commands, the retry is safe because the
   server didn't process the command if it returned a 5xx or timed out:

```ts title="command-retries.ts"
const result = await signum.transfers.postTransfers(payload, {
  retries: {
    strategy: "backoff",
    backoff: {
      initialInterval: 1000,
      maxElapsedTime: 30_000,
    },
  },
});
```

## Next steps

* [Async Operations](/getting-started/sdk/async-operations) — the full 202 command lifecycle
* [Retries & Backoff](/getting-started/sdk/retries) — configure retry strategies
* [Error Handling](/getting-started/sdk/error-handling) — handle errors from failed commands