# Webhooks (https://docs.chargeflow.io/docs/reference/concepts/webhooks)



Configure and listen to webhook events produced by Chargeflow to automatically notify your system of changes and trigger reactions.

## Why webhooks [#why-webhooks]

When integrating with Chargeflow, your application may need to receive real-time notifications for specific events, such as when a new dispute is created. Webhooks provide this capability by allowing you to register an HTTP endpoint that Chargeflow calls whenever a subscribed event occurs.

By using webhooks, your application can respond immediately to new events without relying on periodic API polling. This improves efficiency, reduces unnecessary API calls, and ensures your system remains up to date in real time.

## Enabling webhooks [#enabling-webhooks]

<Callout title="The webhook URL is yours, not ours">
  Chargeflow does not issue a URL for you to call. You provide an HTTPS endpoint on your own server
  that accepts `POST` requests and answers `2XX`, and Chargeflow delivers events to it. The handler
  that endpoint needs is in [Configuring webhooks](#configuring-webhooks); if you would rather not
  run a server at all, use the [Zapier integration](https://docs.chargeflow.io/docs/merchants/automation/zapier-no-code)
  instead.
</Callout>

<Steps>
  <Step>
    Navigate to your [Chargeflow Account Settings](https://app.chargeflow.io/settings).
  </Step>

  <Step>
    Scroll to the **Developers** section.
  </Step>

  <Step>
    Generate **API Access Keys** to obtain your API key and enable webhook configuration.
  </Step>

  <Step>
    Click **+ Add Webhook** to register your first webhook endpoint and subscribe to the events relevant to your application.
  </Step>
</Steps>

After registration, Chargeflow sends webhook events as HTTP POST requests to your specified endpoint. A successful delivery is confirmed when your endpoint responds with a `2XX` status code (preferably `200`). If any other response is received, Chargeflow will retry delivery until a `2XX` response is returned or the retry limit is reached.

Payload shapes differ by event. `dispute.created` (and the platform `integration.access.error`) arrive wrapped in an envelope - `{type, data, creationDate, webhookId}` - where `type` is the event name and `data` carries the resource. **The three alert events are delivered flat, with no envelope**: their `type` field holds the **alert** type (for example `fraud_warning`), not the event name. See the [event payload reference](https://docs.chargeflow.io/docs/merchants/webhook-events) before writing your handler.

## Event authentication [#event-authentication]

Each webhook POST request from Chargeflow includes an HMAC signature in the `X-Chargeflow-Hmac-Sha256` header, derived from the body, path, and method of the request.

You can use this signature to verify the authenticity and integrity of the request on your side, ensuring you only react to genuine Chargeflow calls and not unauthorized third-party requests to your public endpoint.

<Callout type="warn" title="Security Recommendation">
  We strongly recommend implementing HMAC signature verification in all production systems.
</Callout>

The Secret Key used to sign (and verify) webhook requests is available on the [Chargeflow Settings](https://app.chargeflow.io/settings) page alongside other webhook configurations. Note: the API Secret Key is currently the same as the primary API Access Key secret.

This page is the canonical home for **verifying inbound webhook deliveries** (below). Signing your own **outbound** requests to the Chargeflow API uses the same HMAC construction and is documented in [Authentication](https://docs.chargeflow.io/docs/reference/api-fundamentals/authentication).

### Verifying a webhook signature [#verifying-a-webhook-signature]

```javascript title="Node.js"
const crypto = require('crypto');

function generateHmacSignature(data, secretKey) {
  const hmac = crypto.createHmac('sha256', secretKey);
  hmac.update(data);
  return hmac.digest('hex');
}

function verifySignature(req) {
  const receivedSig = req.headers['x-chargeflow-hmac-sha256'];

  const method = req.method;
  const path = req.originalUrl;
  const body = JSON.stringify(req.body);

  const dataToSign = `${method}\n${path}\n${body}`;
  const secret = 'your_secret_key'; // Obtain from Chargeflow Settings
  const hash = generateHmacSignature(dataToSign, secret);

  return receivedSig === hash;
}
```

## Configuring webhooks [#configuring-webhooks]

<Steps>
  <Step>
    ### Register the endpoint [#register-the-endpoint]

    Register the webhook event in the Chargeflow dashboard (**Settings → Developers → + Add Webhook**).
  </Step>

  <Step>
    ### Create a handler [#create-a-handler]

    Create an HTTP endpoint in your server that:

    1. Handles `POST` requests with a JSON payload consisting of an event object.
    2. Returns a `2XX` status code quickly, before any complex processing that could cause a timeout.

    ```javascript title="Node.js"
    // This example uses Express to receive webhooks
    const express = require('express');
    const app = express();

    // Match the raw body to content type application/json
    // If you are using Express v4 - v4.16 you need to use body-parser, not express, to retrieve the request body
    app.post('/webhook', express.json({ type: 'application/json' }), (request, response) => {
      const event = request.body;

      // Optionally validate request signature against body from x-chargeflow-hmac-sha256 header

      // Handle the event. Only enveloped events (dispute.created) carry the
      // event name in `type` - alert payloads are flat and their `type` is the
      // alert type, so route alert events by registering a dedicated URL per event.
      if (event.type === 'dispute.created') {
        const disputeCreated = event.data;
        // Then define and call a method to handle the dispute created event data.
        // handleDisputeCreated(disputeCreated);
      }

      // Return a response to acknowledge receipt of the event
      response.json({ received: true });
    });

    app.listen(8000, () => console.log('Running on port 8000'));
    ```
  </Step>

  <Step>
    ### Test your webhook [#test-your-webhook]

    Once your webhook is configured, you can trigger a test payload to validate your integration. After saving your webhook registration, click the **Test** button next to your endpoint, or access the actions menu for the registered endpoint at any time.
  </Step>
</Steps>

## Webhook event topics [#webhook-event-topics]

| Webhook Event               | Description                                                              |
| --------------------------- | ------------------------------------------------------------------------ |
| `dispute.created`           | Occurs whenever a new dispute is created.                                |
| `alerts.created`            | Occurs whenever a new alert is created.                                  |
| `alerts.updated`            | Occurs whenever an alert outcome is updated.                             |
| `alerts.transaction.linked` | Occurs whenever an alert is linked to its corresponding PSP transaction. |

<Callout title="Payload version">
  `dispute.created` deliveries wrap the current (`2025-04-01`) dispute resource shape under `data`.
  See the [`dispute.created` reference](https://docs.chargeflow.io/docs/merchants/webhook-events#dispute-created) for the full
  payload.
</Callout>

That table is the complete list. There is no dispute **update** event today: status changes,
evidence submission, and final outcomes are not pushed, so poll
`GET /public/2025-04-01/disputes/{disputeId}` (or watch the dashboard) for anything after
creation. Broader dispute-event coverage is planned.

## Next steps [#next-steps]

<Cards>
  <Card title="Subscribe to Webhook Events" href="/docs/merchants/automation/subscribe-to-events">
    Register an endpoint and build a handler.
  </Card>

  <Card title="Webhook Events Overview" href="/docs/merchants/webhook-events">
    The full payload reference for each event.
  </Card>

  <Card title="Authentication" href="/docs/reference/api-fundamentals/authentication">
    The HMAC code used to verify webhook signatures.
  </Card>
</Cards>
