# Subscribe to webhook events (https://docs.chargeflow.io/docs/merchants/automation/subscribe-to-events)



Webhooks let your application receive real-time notifications from Chargeflow without polling the API. When a subscribed event occurs, for example when a new dispute is ingested, Chargeflow sends an HTTP POST request to your configured endpoint with the event payload.

## Before you start [#before-you-start]

1. Navigate to your [Chargeflow Account Settings](https://app.chargeflow.io/settings).
2. Scroll to the **Developers** section.
3. Generate **API Access Keys**. The same key is used for API requests and to sign webhook payloads.

Once your API key is available, you can register webhook endpoints either from the Chargeflow dashboard (click **+ Add Webhook**) or via the API.

## Register a webhook endpoint [#register-a-webhook-endpoint]

```
POST https://api.chargeflow.io/public/2025-04-01/webhooks
```

Each registration subscribes one endpoint to **one** event (`event` is a single string). Register once per event you need, or pass `*` to subscribe one endpoint to all events.

```bash title="Terminal"
curl -X POST https://api.chargeflow.io/public/2025-04-01/webhooks \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event": "dispute.created", "url": "https://your-server.com/webhooks/dispute-created"}'
```

A successful registration returns `{"success": true, "id": "..."}`. See the [Create Webhook reference](https://docs.chargeflow.io/docs/api/2025-04-01/merchants/webhooks-management) for the full event enum.

### Available events [#available-events]

| Event                       | Description                                                    |
| --------------------------- | -------------------------------------------------------------- |
| `dispute.created`           | A new dispute has been ingested and initially enriched         |
| `alerts.created`            | A new pre-chargeback alert has been received                   |
| `alerts.updated`            | The outcome of an alert has been updated                       |
| `alerts.transaction.linked` | An alert has been matched to its corresponding PSP transaction |

## List your webhooks [#list-your-webhooks]

```bash title="Terminal"
curl -X GET https://api.chargeflow.io/public/2025-04-01/webhooks \
  -H "x-api-key: YOUR_API_KEY"
```

## Delete a webhook [#delete-a-webhook]

```bash title="Terminal"
curl -X DELETE https://api.chargeflow.io/public/2025-04-01/webhooks/WEBHOOK_ID \
  -H "x-api-key: YOUR_API_KEY"
```

## Create a handler [#create-a-handler]

Your endpoint must:

1. Accept `POST` requests with a JSON body.
2. Return a `2XX` status code promptly, before any complex logic that could cause a timeout. Chargeflow retries delivery until a `2XX` response is received or the retry limit is reached.
3. Process the event asynchronously after acknowledging receipt.

### Example handler (Node.js / Express) [#example-handler-nodejs--express]

<Callout type="warn" title="Payload shapes differ by event">
  `dispute.created` arrives wrapped in an envelope (`{type, data, creationDate, webhookId}`) where `type` is the event name. **Alert payloads are delivered flat, with no envelope** - their `type` field holds the **alert** type (for example `fraud_warning`), not the event name. Do not switch on `event.type` to route alert events; register a dedicated URL per event instead (one registration is one event anyway). Payload shapes: [webhook events overview](../webhook-events).
</Callout>

```javascript title="Node.js"
const express = require('express');
const app = express();
app.use(express.json({ type: 'application/json' }));

// One route per registered event - the URL tells you which event arrived.
app.post('/webhooks/dispute-created', (request, response) => {
  response.json({ received: true }); // acknowledge first

  const dispute = request.body.data; // enveloped: {type, data, creationDate, webhookId}
  // Trigger your enrichment flow asynchronously:
  // enrichDispute(dispute.id);
});

app.post('/webhooks/alerts-created', (request, response) => {
  response.json({ received: true });

  const alert = request.body; // flat payload - no envelope
  // alert.id, alert.type (e.g. 'fraud_warning'), alert.status, alert.outcome
  // Initiate refund or mitigation workflow:
  // handleAlert(alert.id);
});

app.listen(8000, () => console.log('Webhook handler running on port 8000'));
```

## Signature verification and delivery [#signature-verification-and-delivery]

For signature verification and delivery/retry behavior, see [Webhooks](https://docs.chargeflow.io/docs/reference/concepts/webhooks).

## Testing webhooks [#testing-webhooks]

After saving your webhook registration, you can send a mock payload at any time to validate your integration:

* Click the **Test** button next to your webhook endpoint in the Chargeflow dashboard, or
* Access the actions menu for the registered endpoint and select **Send Test Payload**.

Chargeflow will deliver a sample event payload to your endpoint. Check that your handler responds with `200 OK` and processes the payload correctly.

## Next steps [#next-steps]

<Cards>
  <Card title="Webhook events overview" href="../webhook-events">
    Full event payload reference.
  </Card>

  <Card title="Automate dispute management" href="./automate-dispute-management">
    How dispute.created fits into the automation flow.
  </Card>

  <Card title="Manage pre-chargeback alerts" href="/docs/merchants/alerts/manage-alerts">
    How alert webhooks work in each integration model.
  </Card>

  <Card title="Webhooks" href="/docs/reference/concepts/webhooks">
    Signature verification and delivery/retry behavior.
  </Card>
</Cards>
