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

# cardholder.approved

> Fired when a cardholder's KYC is approved

## Event: `cardholder.approved`

Sent when a cardholder passes KYC verification and becomes eligible for card issuance. This event is emitted alongside [`cardholder.kyc.updated`](/partner/webhooks/cardholder-kyc-updated) with `kycStatus: "approved"` — subscribe to whichever fits your integration; both signal the same transition.

Approval is asynchronous: after `POST /partner/cardholders/{id}/kyc` the cardholder stays `pending` until verification completes. Sandbox replays the same sequence, approving the cardholder after a short delay.

## Payload

```json theme={null}
{
  "cardholderId": "ch_abc123",
  "externalUserId": "user_42"
}
```

| Field | Type | Description |
| - | - | - |
| `cardholderId` | string | Cardholder that was approved |
| `externalUserId` | string \| null | Your identifier for this user, when provided at creation |

The rejection counterpart (`cardholder.rejected`) carries the same fields plus an optional `reason` string with a sanitized rejection summary.

## Response

Your endpoint must return a **2xx** status code within **30 seconds** to acknowledge receipt. Any non-2xx response or timeout triggers the [retry policy](/partner/webhooks#retry-policy).

| Status code | Meaning |
| - | - |
| `200` | Event received and processed |
| `202` | Event received, will process asynchronously |
| Any non-2xx | Delivery failed — will retry |

## Example handler

```javascript theme={null}
app.post("/webhooks/contro", (req, res) => {
  const eventType = req.headers["x-contro-event"];

  if (eventType === "cardholder.approved") {
    const { cardholderId } = req.body;
    // Cardholder is verified — you can now issue cards
  }

  res.status(200).send("OK");
});
```
