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

# Onboarding Webhooks

Onboarding webhooks tell your server about each sub-merchant's onboarding
progress, whether or not the sub-merchant is in your page at the time. They're
sent to **your** webhook URL, the parent account's, for every sub-merchant
under it.

## Subscribe

In the merchant dashboard, go to **Developers → Webhooks**, choose webhook
**Version 2** or later, and subscribe to the five events below. Webhook URLs
are configured in the dashboard only. See
[Partner Setup](/guides/embedded-onboarding/partner-setup#3-set-up-webhooks).

## Events

| Event                                | When it fires                                                                                                                                                  |
| :----------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Sub-merchant Application Submitted` | The sub-merchant submits its onboarding application                                                                                                            |
| `Sub-merchant Application Approved`  | Coinflow approves the sub-merchant and its account goes live                                                                                                   |
| `Sub-merchant Application Declined`  | Underwriting declines the application. Sent once                                                                                                               |
| `Sub-merchant RFI Sent`              | An information request is sent to the sub-merchant, during onboarding or after it's live. Sent again, with the same `rfiId`, each time the request is reopened |
| `Sub-merchant RFI Submitted`         | The sub-merchant submits its answers to an information request. Sent again each time it answers a reopened request                                             |

## Payload

```typescript
{
  eventType:
    | 'Sub-merchant Application Submitted'
    | 'Sub-merchant Application Approved'
    | 'Sub-merchant Application Declined'
    | 'Sub-merchant RFI Sent'
    | 'Sub-merchant RFI Submitted';
  category: 'KYC';
  created: string;
  data: {
    merchantId: string;  // the sub-merchant's merchantId
    status: 'not_started' | 'in_progress' | 'submitted' | 'action_required' | 'approved' | 'declined';
    timestamp: string;   // ISO 8601
    rfiId?: string;      // only on the two RFI events
  };
}
```

**`Example`**

```json Example
{
  "eventType": "Sub-merchant RFI Sent",
  "category": "KYC",
  "created": "2026-10-28T15:04:05.000Z",
  "data": {
    "merchantId": "acme-store",
    "status": "action_required",
    "timestamp": "2026-10-28T15:04:05.000Z",
    "rfiId": "6720f1c2a4b5c6d7e8f90123"
  }
}
```

`data.status` is the onboarding status at the time of the event, the same value
[Get Onboarding](/api-reference/api-reference/onboarding/get-onboarding)
returns. For example:

* `Sub-merchant Application Submitted` reports `action_required` when the sub-merchant still has a step to finish, such as choosing where its funds settle, and `submitted` otherwise.
* After the last open RFI is submitted, `Sub-merchant RFI Submitted` reports `submitted` again for a sub-merchant in review, or `approved` for a live one.

Treat a `status` you don't recognize as still in progress, and ignore fields
you don't recognize.

## Handling deliveries

* **Verify the sender.** Check that the `Authorization` header matches your Webhook Validation Key before trusting a delivery. See [Verifying Webhook Signatures](/guides/developer-resources/webhooks/verifying-webhook-signatures).
* **Respond quickly.** Return a `200` within 5 seconds and do the work in the background. Failed deliveries are retried; see [Configuring Webhooks](/guides/developer-resources/webhooks/configuring-webhooks#automatic-retries).
* **Expect duplicates and out-of-order delivery.** Deliveries can repeat and arrive out of order. When in doubt, call [Get Onboarding](/api-reference/api-reference/onboarding/get-onboarding) for the current state.

**`Server`**

```typescript Server
app.post('/coinflow/webhooks', express.json(), (req, res) => {
  if (req.get('Authorization') !== process.env.COINFLOW_WEBHOOK_VALIDATION_KEY)
    return res.sendStatus(401);

  const {eventType, data} = req.body;
  if (eventType?.startsWith('Sub-merchant '))
    onboardingQueue.add({eventType, ...data});

  res.sendStatus(200);
});
```

## Turning events off for a sub-merchant

These events aren't sent for a sub-merchant whose webhook settings turn off
forwarding webhooks to its parent.