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

# Webhooks Overview

> Receive real-time notifications when events occur across FlexyPe products.

Webhooks let FlexyPe notify your systems the moment something happens across any FlexyPe product — a customer abandoning a session, viewing a product, or completing a purchase for the first time. Instead of polling for updates, your endpoint receives a POST request with the event payload as soon as it fires.

## How it works

1. You configure a webhook endpoint URL in your FlexyPe dashboard
2. FlexyPe sends a `POST` request to that URL whenever a subscribed event fires
3. Your endpoint processes the payload and returns `HTTP 200`
4. If delivery fails, FlexyPe retries automatically

## Setting up a webhook

Go to **Settings → Webhooks** in your FlexyPe dashboard. When creating an endpoint, you'll provide:

* **Endpoint URL** — the HTTPS URL where FlexyPe should deliver events
* **Secret** — a string you create, used to verify that requests come from FlexyPe
* **Events** — the specific events you want to subscribe to

<Note>
  Only the events you select will trigger deliveries to your endpoint. Subscribe only to what your integration needs to avoid unnecessary traffic.
</Note>

## Request headers

Every webhook request includes these headers:

| Header                    | Description                                                                |
| ------------------------- | -------------------------------------------------------------------------- |
| `X-Flexype-Authorization` | Your webhook secret — validate this to confirm the request is from FlexyPe |
| `X-Webhook-Version`       | The webhook payload version (e.g. `2026-05`)                               |
| `X-Event-Id`              | Unique identifier for this specific event occurrence                       |

## Verifying webhooks

Before processing any payload, validate the `X-Flexype-Authorization` header against the secret you set when configuring the webhook.

```js theme={null}
const secret = process.env.FLEXYPE_WEBHOOK_SECRET;
const incoming = req.headers['x-flexype-authorization'];

if (incoming !== secret) {
  return res.status(401).send('Unauthorized');
}

// Safe to process the payload
```

<Warning>
  Always verify `X-Flexype-Authorization` before processing a webhook payload. Skip this check and anyone can send arbitrary data to your endpoint.
</Warning>

<Tip>
  Store your webhook secret in an environment variable — never hardcode it in your source code or commit it to version control.
</Tip>

## Responding to webhooks

Your endpoint must return **HTTP 200** within **10 seconds** to acknowledge receipt.

<Tip>
  Return `200` immediately, then process the payload asynchronously — add it to a queue or background job rather than doing the work inline. This keeps you well within the timeout window.
</Tip>

<Note>
  Any non-200 response, or no response within 10 seconds, is treated as a delivery failure and triggers a retry.
</Note>

## Retry logic

When a delivery fails, FlexyPe retries three times before marking the webhook as failed:

| Attempt           | Delay from previous failure |
| ----------------- | --------------------------- |
| 1st retry         | 1 minute                    |
| 2nd retry         | 5 minutes                   |
| 3rd retry (final) | 15 minutes                  |

You can check delivery status and see failure reasons in the Webhooks section of your dashboard.

<Info>
  If all retry attempts fail, the webhook is marked as failed. You can manually retry individual failed deliveries from the dashboard at any time.
</Info>

## Idempotency

Each event has a unique `X-Event-Id` header. Use this value to deduplicate events — retried deliveries carry the same `X-Event-Id` as the original attempt, so you can safely ignore duplicates.

<Warning>
  Always implement idempotency checks. Due to retries, your endpoint may receive the same event more than once.
</Warning>

```js theme={null}
const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const eventId = req.headers['x-event-id'];

  if (processedEvents.has(eventId)) {
    return res.status(200).json({ received: true });
  }

  processedEvents.add(eventId);
  // Process the webhook payload...
});
```

<Info>
  In production, persist processed event IDs in a database rather than an in-memory Set, so deduplication survives server restarts.
</Info>

## Testing webhooks

Use the **Test** button on your configured webhook in the FlexyPe dashboard to send a sample payload to your endpoint. Do this before going live to confirm your verification logic and response handling are working correctly.

<Tip>
  Test your endpoint thoroughly before processing production events — this helps catch issues with verification, response timing, and payload parsing early.
</Tip>

## Implementation example

A complete webhook handler that verifies the request, deduplicates retries, and processes the payload asynchronously:

<CodeGroup>
  ```js Node.js (Express) theme={null}
  import express from 'express';

  const app = express();
  app.use(express.json());

  const WEBHOOK_SECRET = process.env.FLEXYPE_WEBHOOK_SECRET;
  const processedEvents = new Set(); // Use a database in production

  app.post('/webhook', (req, res) => {
    // 1. Verify the request is from FlexyPe
    const incoming = req.headers['x-flexype-authorization'];
    if (incoming !== WEBHOOK_SECRET) {
      return res.status(401).send('Unauthorized');
    }

    // 2. Acknowledge receipt immediately
    const eventId = req.headers['x-event-id'];
    res.status(200).json({ received: true });

    // 3. Deduplicate retried deliveries
    if (processedEvents.has(eventId)) {
      return;
    }
    processedEvents.add(eventId);

    // 4. Process asynchronously
    setImmediate(() => {
      const { event_type, event_time } = req.body;
      console.log(`Processing event: ${event_type} at ${event_time}`);
      // Your business logic here...
    });
  });

  app.listen(3000);
  ```

  ```python Python (Flask) theme={null}
  import os
  from flask import Flask, request, jsonify
  from threading import Thread

  app = Flask(__name__)

  WEBHOOK_SECRET = os.environ.get('FLEXYPE_WEBHOOK_SECRET')
  processed_events = set()  # Use a database in production

  def process_event(payload):
      event_type = payload.get('event_type')
      event_time = payload.get('event_time')
      print(f'Processing event: {event_type} at {event_time}')
      # Your business logic here...

  @app.route('/webhook', methods=['POST'])
  def webhook():
      # 1. Verify the request is from FlexyPe
      incoming = request.headers.get('X-Flexype-Authorization')
      if incoming != WEBHOOK_SECRET:
          return 'Unauthorized', 401

      event_id = request.headers.get('X-Event-Id')

      # 2. Acknowledge receipt immediately
      response = jsonify({'received': True})

      # 3. Deduplicate retried deliveries
      if event_id in processed_events:
          return response
      processed_events.add(event_id)

      # 4. Process asynchronously
      payload = request.get_json()
      Thread(target=process_event, args=(payload,)).start()

      return response

  if __name__ == '__main__':
      app.run(port=3000)
  ```
</CodeGroup>

## Available events

| Event                                                      | Trigger                                                                         |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------- |
| [Abandoned Checkout Recovery (ACR)](/webhooks/2026-05/acr) | Fires 15 minutes after a checkout session opens if the order is not completed   |
| [Abandoned Session Recovery (ASR)](/webhooks/2026-05/asr)  | Visitor browses without starting checkout; fires after 15 minutes of inactivity |
| [Collection Viewed](/webhooks/2026-05/collection-viewed)   | Customer views a collection page                                                |
| [Product Viewed](/webhooks/2026-05/product-viewed)         | Customer views a product page                                                   |
| [New Customer](/webhooks/2026-05/new-customer)             | First-time user logs in via FlexyPass and a new store account is created        |

## Best practices

* **Use HTTPS** — only HTTPS endpoints are accepted
* **Return 200 quickly** — hand off processing to a background job to avoid timeouts
* **Check idempotency** — use `X-Event-Id` to handle retried deliveries without double-processing
* **Store your secret securely** — use environment variables; never hardcode it in your codebase
* **Monitor delivery** — review webhook logs in your dashboard and act on failures before the retry window closes
