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

# Cards Webhooks

> Card events on the webhook stream and envelope you already consume

<Warning>
  **Cards is available in sandbox only while we finish development.** Dakota enables Cards per
  account. The Cards endpoints are in the [API reference](/api-reference/introduction), marked
  **Sandbox only**. Endpoints, fields, and flows can still change before release.
</Warning>

Card events arrive on your existing webhook stream, in the standard envelope, with the same signing and retries. See [Webhook Integration](/documentation/webhooks) for delivery. Delivery is at least once, so dedupe on the event `id`.

**If your webhook target filters by type, add the card events first.** A target with `global: false` receives only the types in its `event_types` list. A card event type missing from that list is never delivered and never retried. Targets with `global: true` receive card events automatically.

`data.object` is the resource that changed, in the same shape the API returns for it. On updates, `data.previous_attributes` holds the prior values of the fields that changed.

## Events

| Event | Fires when | `data.object` |
| - | - | - |
| `customer.capability_status.updated` | A customer's capability changes status. Filter on `capability: "cards"`. | The capability |
| `wallet.card_enablement.completed` | A wallet's enablement becomes `active`. Cards can now be issued against it. | The acceptance record: see [below](#enablement-completed) |
| `cardholder.created` | A cardholder is created. | [Cardholder](/api-reference/cards/get-a-cardholder) |
| `cardholder.updated` | Any change to a cardholder, including its status moving to `active` or `closed`. | [Cardholder](/api-reference/cards/get-a-cardholder) |
| `cardholder.information_requested` | A reviewer asks you for information. Not live yet. | The open requests |
| `card.created` | A card is issued, still `pending`. | [Card](/api-reference/cards/get-a-card) |
| `card.updated` | A card changes: activation, freeze, unfreeze, close, or a new spend limit. | [Card](/api-reference/cards/get-a-card) |
| `card_transaction.created` | A card transaction is first recorded: an authorization, a decline, a refund, or a force post. | [Card transaction](/api-reference/cards/get-a-card-transaction) |
| `card_transaction.updated` | Anything later happens to that transaction. | [Card transaction](/api-reference/cards/get-a-card-transaction) |

A new card fires `card.created` with `status: pending`, then `card.updated` with `status: active`. Build against the second one.

Events can arrive out of order (see [Event ordering](/documentation/webhooks#event-ordering)), so `card.created` can land after `card.updated`.

* **Cards** carry `version`, which goes up by one on every card event. Keep the highest `version` you have applied for each card, and drop any event or response whose `version` is not greater.
* **Cardholders and card transactions** carry `updated_at`, in seconds. Ignore an event whose object is older than the one you stored, and re-fetch the object when the two are equal.

### Enablement completed

`wallet.card_enablement.completed` records what the customer signed. Keep it as their acceptance record.

| Field | Meaning |
| - | - |
| `wallet_id`, `customer_id`, `client_id` | Who and what the enablement covers |
| `intent_kind` | Always `enable_card_settlement` |
| `intent_hash` | Hash of the exact intent the customer signed |
| `signatures` | Every signature submitted with the intent. `signature` repeats the first one. |
| `signer_public_key` | The key that signed. Empty when the signer could not be identified. |
| `settlement_destination` | The one address Dakota can recover settled card spend to |
| `completed_at` | When the enablement became `active`, in Unix seconds |

## Card transactions

A card transaction is one purchase, refund, or other network event on a card. Its `id` stays the same for its whole life. Its first state arrives on `card_transaction.created` and later ones on `card_transaction.updated`, but a status can arrive on either event: a transaction can start `pending` and become `authorized` on an update, and a decline can arrive on an update too. **Upsert by `data.object.id` and switch on `status`, not on the event type.** There are no separate hold, release, or settlement events.

| `status` | Meaning |
| - | - |
| `pending` | Recorded before its first authorization was applied. |
| `authorized` | Approved. The amount is held; nothing has settled. |
| `declined` | Refused. Nothing was held and no money moved. `decline_reason` says why. Final. |
| `partially_cleared` | Part of the amount has settled; more is expected. |
| `cleared` | Settled. |
| `auth_reversed` | The merchant cancelled before capturing. The hold was released. |
| `expired` | Never captured. The hold was released. |
| `force_posted` | Settled with no prior authorization, so nothing was held first. |
| `returned` | A merchant refund. See [Refunds](#refunds). |
| `disputed` | Reserved. Not sent yet. |

Amounts such as `auth_amount` and `cleared_amount` are decimal strings in major units (`"39.00"`). The card's `spend_limit.amount` is different: an integer in minor units.

`outstanding_amount` is the part of `cleared_amount` that no authorization covered, less any refunds. It is non-zero after a force post, or when a merchant clears more than it authorized.

### Refunds

A merchant refund is a **new card transaction** on the same card, with `status: returned`. The original purchase stays `cleared`, and the refund is not linked to it.

`status: returned` means the network sent the money back. `refund_state` says whether it has reached the wallet: `pending`, then `paid`, or it ends `reversed` or `failed`. Credit the cardholder only when `refund_state` is `paid`. It is `null` on every transaction that is not a refund.

<Note>
  **Refund payouts to the wallet are not live yet.** Until they are, `refund_state` stays `pending`.
</Note>

### Declines

| `decline_reason` | Meaning |
| - | - |
| `insufficient_funds` | The wallet's available balance was too low. |
| `card_inactive` | The card was not active, for example frozen. |
| `spend_limit_exceeded` | The purchase would exceed the card's spend limit. |
| `merchant_not_allowed` | A merchant or category restriction refused it. |
| `card_details_incorrect` | The card number, expiry, or CVV did not match. |
| `suspected_fraud` | Refused as suspected fraud. |
| `authorization_timeout` | No decision was reached in time. |
| `other` | Anything else. Treat any value you do not recognize as `other`. |

`decline_code` is the raw network code. Use it for support only; do not build logic on it.

## A handler

```text theme={null}
on any card.* event:
  skip it unless data.object.version is greater than the version you stored
  "active" → the card can be spent and revealed
  "frozen" → show it as frozen; the freeze may not have come from you (check freeze_sources)
  "closed" → stop offering the card

on card_transaction.created or card_transaction.updated:
  skip it if data.object.updated_at is older than the transaction you stored
  upsert the transaction by data.object.id, then switch on status:
    "pending", "authorized"       → show a pending charge for auth_amount
    "declined"                    → show a declined attempt, worded from decline_reason
    "partially_cleared", "cleared",
    "force_posted"                → show the charge at cleared_amount
    "auth_reversed", "expired"    → remove the pending charge; no money moved
    "returned"                    → show a pending refund; credit it when refund_state is "paid"
```

Card events never contain the full card number, expiry, or CVV. `last4` is the most they carry.
