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

# Webhooks

> Receive and handle real-time event notifications

## Overview

Webhooks push events to your server in real-time. No polling needed.

```
Event occurs in Waffo Pancake --> HTTP POST to your endpoint --> You process the event
```

***

## Setup

<Steps>
  <Step title="Create an HTTPS Endpoint">
    Build a publicly accessible endpoint that accepts POST requests and returns 200 OK.
  </Step>

  <Step title="Register in Dashboard">
    Go to Dashboard > API & Development > Webhooks. Add your endpoint URL.
  </Step>

  <Step title="Select Events">
    Choose which events to receive.
  </Step>

  <Step title="Save">
    Save your webhook configuration.
  </Step>
</Steps>

***

## Event Types

Events cover payment, subscription, and refund lifecycle notifications. When configuring webhooks in the Dashboard, you can select which event categories to subscribe to.

<Note>
  The exact event names and available categories are shown in the Dashboard when you configure your webhook endpoint. Refer to your Dashboard for the current list.
</Note>

***

## Payload Format

Each webhook delivery is an HTTP POST with a JSON body containing the event type and associated data:

```json theme={"system"}
{
  "event": "<event_type>",
  "data": {
    // Event-specific fields
  }
}
```

<Note>
  All IDs are UUID v4 format. Amounts are display format strings (e.g., "29.00" = \$29.00). Timestamps are ISO 8601 UTC.
</Note>

***

## Handling Webhooks

Return `200 OK` quickly. Process asynchronously if your handler needs to do heavy work.

<CodeGroup>
  ```javascript Node.js (Express) theme={"system"}
  app.post('/webhooks/waffo', (req, res) => {
    // Respond immediately
    res.status(200).send('OK');

    // Process async
    const { event, data } = req.body;

    switch (event) {
      case 'order.completed':
        handleOrderCompleted(data);
        break;
      case 'subscription.activated':
        handleSubscriptionActivated(data);
        break;
      case 'subscription.canceled':
        handleSubscriptionCanceled(data);
        break;
    }
  });
  ```

  ```python Python (Flask) theme={"system"}
  from flask import Flask, request, jsonify

  app = Flask(__name__)

  @app.route('/webhooks/waffo', methods=['POST'])
  def handle_webhook():
      payload = request.get_json()
      event = payload.get('event')
      data = payload.get('data')

      if event == 'order.completed':
          handle_order_completed(data)
      elif event == 'subscription.activated':
          handle_subscription_activated(data)

      return jsonify({'received': True}), 200
  ```
</CodeGroup>

***

## Idempotency

Events may be delivered more than once. Use the event data for deduplication:

```javascript theme={"system"}
const processedEvents = new Set();

app.post('/webhooks/waffo', (req, res) => {
  const { event, data } = req.body;
  const eventKey = `${event}:${data.id}`;

  if (processedEvents.has(eventKey)) {
    return res.status(200).send('Already processed');
  }

  processedEvents.add(eventKey);
  res.status(200).send('OK');
  processWebhook(event, data);
});
```

<Tip>
  For production, store processed event IDs in a database instead of in-memory to persist across server restarts.
</Tip>

***

## Retry Policy

Failed deliveries are retried automatically. Ensure your endpoint returns a `200` status code promptly to confirm receipt.

***

## Testing

Use the [Test Mode](/features/test-mode) to test webhooks:

1. Switch to Test Mode in the Dashboard
2. Go to API & Development > Webhooks
3. Perform actions that trigger events
4. Check your endpoint for incoming events

<Note>
  Webhooks fire in both Test and Live modes. Make sure your endpoint can distinguish between environments.
</Note>

***

## Best Practices

1. **Use HTTPS** -- Required for production webhook endpoints
2. **Respond fast** -- Return 200 within 30 seconds
3. **Handle duplicates** -- Use IDs for deduplication
4. **Process async** -- Don't block the response with heavy processing
5. **Log everything** -- Record incoming webhooks for debugging
