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

# Receive Real-Time Event Notifications via Fliqr AI Webhooks

> Register an HTTPS endpoint to receive instant event notifications from Fliqr AI — new messages, contacts, flow completions, and more.

Webhooks let your server receive real-time notifications when events occur inside Fliqr AI. Instead of polling the API, you register an HTTPS endpoint and Fliqr AI delivers an HTTP POST to that URL within seconds of each subscribed event. This is the recommended way to react to inbound messages, new contacts, flow completions, and order activity without introducing polling latency.

## Available Events

| Event                  | Description                                         |
| ---------------------- | --------------------------------------------------- |
| `new_message`          | A contact sends a message on any connected channel  |
| `new_contact`          | A new contact is created                            |
| `tag_applied`          | A tag is applied to a contact                       |
| `flow_completed`       | A contact reaches the end of a flow                 |
| `order_placed`         | A contact places an order                           |
| `order_status_changed` | An order status is updated                          |
| `handover_requested`   | A contact requests or is escalated to a human agent |
| `conversation_closed`  | A conversation is marked as closed                  |

## Register a Webhook

Create a webhook endpoint by sending a `POST` to `/webhooks` with your URL, the events you want to receive, and a secret for signature verification:

```bash theme={null}
curl -X POST https://api.fliqr.ai/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/fliqr-webhook",
    "events": ["new_message", "new_contact"],
    "secret": "your_webhook_secret"
  }'
```

**Response — HTTP 201 Created**

```json theme={null}
{
  "id": "wh_abc123",
  "url": "https://your-server.com/fliqr-webhook",
  "events": ["new_message", "new_contact"],
  "status": "active",
  "created_at": "2025-01-15T10:00:00Z"
}
```

### Registration Parameters

<ParamField path="url" type="string" required>
  The HTTPS URL Fliqr AI will POST events to. Must be publicly reachable. HTTP (non-TLS) endpoints are not accepted.
</ParamField>

<ParamField path="events" type="array" required>
  An array of event names to subscribe to. At least one event is required. Use `["*"]` to subscribe to all events.
</ParamField>

<ParamField path="secret" type="string" required>
  A secret string you choose. Fliqr AI uses this to compute an HMAC-SHA256 signature for every delivery, which your server uses to verify authenticity. Store this as an environment variable.
</ParamField>

### Webhook Object Fields

<ResponseField name="id" type="string">
  Unique identifier for the webhook registration. Use this ID to update or delete the webhook.
</ResponseField>

<ResponseField name="url" type="string">
  The delivery URL.
</ResponseField>

<ResponseField name="events" type="array">
  The list of subscribed event names.
</ResponseField>

<ResponseField name="status" type="string">
  `active` or `disabled`. Webhooks are automatically disabled after repeated delivery failures.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp when the webhook was registered.
</ResponseField>

## Event Payload Structure

Every event delivery is an HTTP POST with a JSON body following this structure:

```json theme={null}
{
  "id": "evt_xyz789",
  "event": "new_message",
  "timestamp": "2025-01-15T10:30:00Z",
  "data": {
    "contact_id": "cid_abc123",
    "channel": "whatsapp",
    "message": {
      "id": "msg_def456",
      "text": "Hello!",
      "direction": "inbound",
      "sent_at": "2025-01-15T10:30:00Z"
    }
  }
}
```

<ResponseField name="id" type="string">
  A unique identifier for this specific event delivery. Use this for idempotency — if you receive the same `id` twice, discard the duplicate.
</ResponseField>

<ResponseField name="event" type="string">
  The event name, matching one of the subscribed event types.
</ResponseField>

<ResponseField name="timestamp" type="string">
  ISO 8601 timestamp when the event occurred on the Fliqr AI platform.
</ResponseField>

<ResponseField name="data" type="object">
  The event payload. Schema varies by event type. Always contains `contact_id` and `channel` at minimum.
</ResponseField>

## Verify Webhook Signatures

Fliqr AI signs every delivery with an HMAC-SHA256 digest of the raw request body, using your webhook secret. The signature is sent in the `X-Fliqr-Signature` header. Always verify this before processing:

```javascript webhook-server.js theme={null}
const crypto = require('crypto');
const express = require('express');
const app = express();

// Use express.raw() so req.body is a Buffer containing the unmodified request bytes.
// Never use express.json() before this route — parsing the body before hashing it
// changes whitespace and key ordering, which breaks the HMAC check.
app.post('/fliqr-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-fliqr-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  const digest = crypto
    .createHmac('sha256', secret)
    .update(req.body) // req.body is the raw Buffer — do not JSON.stringify
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(digest, 'hex'))) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body.toString());
  // Process event
  console.log('Received event:', event.event);
  res.status(200).send('OK');
});
```

<Warning>
  Always verify the `X-Fliqr-Signature` header before processing any webhook payload. Never trust the payload contents without first confirming the signature matches — an unverified endpoint can be exploited to inject arbitrary events.
</Warning>

<Note>
  Use `crypto.timingSafeEqual` (or your language's equivalent constant-time comparison) rather than a simple string equality check. Standard string comparison is vulnerable to timing attacks.
</Note>

## Retry Policy

If your endpoint returns anything other than HTTP `200`, or does not respond within **10 seconds**, Fliqr AI marks the delivery as failed and retries automatically:

| Attempt | Delay after previous failure |
| ------- | ---------------------------- |
| 1       | 30 seconds                   |
| 2       | 1 minute                     |
| 3       | 2 minutes                    |
| 4       | 4 minutes                    |
| 5       | 8 minutes                    |

After 5 consecutive failures, the webhook is automatically **disabled** and you receive a notification email. Re-enable it from **Settings → Webhooks** once your endpoint is healthy.

## Respond Quickly, Process Asynchronously

Your endpoint must return `200` within 10 seconds. For any processing that takes longer — database writes, downstream API calls, or sending follow-up messages — push the event body onto an internal queue and return `200` immediately:

```javascript theme={null}
app.post('/fliqr-webhook', (req, res) => {
  // Verify signature first
  verifySignature(req);

  // Enqueue for async processing
  eventQueue.push(req.body);

  // Respond immediately
  res.status(200).send('OK');
});
```

***

## What's Next

<CardGroup cols={2}>
  <Card title="Contacts" icon="address-book" href="/docs/api-reference/contacts">
    Use the Contacts API to look up and update contact records when you receive a webhook event.
  </Card>

  <Card title="Webhook Integrations Guide" icon="plug" href="/docs/integrations/webhooks">
    Step-by-step guide for connecting Fliqr AI webhooks to popular platforms and serverless functions.
  </Card>
</CardGroup>
