# Idempotency and on-chain outcome

Source: https://docs.settlemint.com/docs/events/idempotency-and-on-chain-outcome
Reconcile cached DAPI mutation responses with webhook-only on-chain outcomes, including final, retracted, and recalled events.



A cached DAPI mutation response reflects the synchronous mutation outcome only; it does not track on-chain fate.
The originating transaction can still be dropped from the mempool, reorged out, or recalled for compliance reasons.
Use webhooks to observe that on-chain status.

This rule is the boundary between request idempotency and chain reconciliation.
Treat an HTTP `201` returned from an idempotency cache as "the mutation was accepted and returned this response", not "the transaction is final forever".

## Reconciliation pattern [#reconciliation-pattern]

Subscribe to the lifecycle events that can prove or revise an outcome:

* `*.final` for events that passed the configured reorg depth.
* `*.retracted` for events invalidated by a reorg.
* `*.recalled` for operator-authorized compliance recall.

Dedupe every delivery by the `webhook-id` header.
Correlate webhook events back to the originating API call with `event.request.idempotency_key` when that value is present.
If a later retracted or recalled event names an earlier event through `supersedes`, treat the later event as authoritative over the cached DAPI response.

## Retract and recall handling [#retract-and-recall-handling]

When a `*.retracted` event arrives, mark the superseded event as no longer valid and reverse any external side effects that assumed finality. When a `*.recalled` event arrives, apply the recall even if the original delivery failed or is unknown to your system; unknown `supersedes` references should be ignored after audit logging.

If both retraction and recall apply to the same original event, recall wins.
Your consumer should keep the recall as the final superseding signal.
Keep this [retract and recall handling](#retract-and-recall-handling) branch close to your idempotency correlation code so cached DAPI responses cannot mask later chain outcomes.

## Typed SDK supersedes accessor [#typed-sdk-supersedes-accessor]

The typed SDK exposes `supersedes` only on `retracted` and `recalled` lifecycle states. That lets TypeScript consumers branch on lifecycle state before applying authoritative-over-cached-response behavior.

```typescript
import { verifyWebhook, type Webhook } from "@settlemint/dalp-sdk";

const result = verifyWebhook({ rawBody, headers, secret });
if (!result.ok) throw new Error(result.code);

const event: Webhook.Event = result.event;

if (event.lifecycle_state === "retracted" || event.lifecycle_state === "recalled") {
  await markSuperseded({
    originalEventId: event.supersedes,
    supersedingEventId: event.evt_id,
    idempotencyKey: event.request?.idempotency_key,
  });
}
```

`verifyWebhook` returns the discriminated event union after signature verification, so reconciliation code can narrow on `event.type` or `event.lifecycle_state`.
