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

# Outbound webhooks

> Subscribe to message, conversation, opt-out, and template events. Verify HMAC signatures, handle retries, and avoid duplicate processing.

Outbound webhooks let your systems react to events in Flowella in near real-time, without polling the API. You configure a URL and a list of event types, and Flowella POSTs a signed JSON payload to that URL whenever one of those events fires.

## Configuring a webhook

You need the **Owner** or **Admin** role.

<Steps>
  <Step title="Open Settings → Webhooks">
    In the Flowella app, go to **Settings → Webhooks**.
  </Step>

  <Step title="Add a webhook">
    Click **Add webhook**, paste the public HTTPS URL of your endpoint, and pick the event types you want to receive.
  </Step>

  <Step title="Save the signing secret">
    Flowella generates a unique signing secret for the webhook. Copy it — you will use it to verify incoming requests.
  </Step>

  <Step title="Send a test">
    Click **Send test** to dispatch a `webhook.test` payload immediately. Check your endpoint received it and responded with a 2xx.
  </Step>
</Steps>

## Event types

You can subscribe to any of these product events:

| Event                     | When it fires                                                           |
| ------------------------- | ----------------------------------------------------------------------- |
| `message.received`        | An inbound WhatsApp message has arrived from a contact                  |
| `message.sent`            | Flowella has accepted your outbound message and submitted it to Meta    |
| `message.delivered`       | Meta confirmed the message was delivered to the recipient's device      |
| `message.read`            | The recipient opened the message                                        |
| `message.failed`          | Meta returned a failure for the message                                 |
| `conversation.opened`     | A conversation has moved into the open state                            |
| `conversation.closed`     | A conversation has been closed                                          |
| `optout.created`          | A contact opted out on a specific channel                               |
| `template.status_updated` | Meta changed a template's status (approved, rejected, paused, disabled) |

`webhook.test` is sent only when you click **Send test** — you do not subscribe to it explicitly.

## Request format

Flowella POSTs a JSON body to your URL with these headers:

```http theme={null}
POST https://your-endpoint.example.com/flowella
Content-Type: application/json
User-Agent: Flowella-Webhooks/1
X-Flowella-Signature: <hex digest>
```

Payloads are capped at **256 KB**. Larger objects are summarised — use the API to fetch the full record by ID if you need it.

The body shape varies by event but always includes:

```json theme={null}
{
  "event": "message.delivered",
  "timestamp": "2025-01-15T14:30:00.000Z",
  "organizationId": "clxxxxxxxxxxxxxxxxxxxxxxxx",
  "data": { "...event-specific fields..." }
}
```

## Verifying signatures

Every request includes an `X-Flowella-Signature` header. The value is the **HMAC-SHA256 hex digest** of the **raw UTF-8 request body** keyed with your webhook's signing secret.

Verify before processing:

```js theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(signatureHeader, "hex");
  return a.length === b.length && timingSafeEqual(a, b);
}
```

```python theme={null}
import hmac, hashlib

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature_header)
```

<Warning>
  Always sign over the **raw body bytes** before any JSON parsing or middleware reformats the payload. If your framework reserialises JSON, the signature will not match.
</Warning>

## Responses, retries, and timeouts

* **Timeout**: Flowella waits up to **10 seconds** for your endpoint to respond.
* **Success**: any `2xx` response is treated as successful delivery.
* **Retries**: failed deliveries are retried up to **3 attempts** with backoff.
* **Auto-disable**: if a webhook accumulates **10 consecutive failures**, Flowella deactivates it. You will need to re-enable it from **Settings → Webhooks** after fixing your endpoint.

Your endpoint should return as fast as possible — defer any heavy processing to a background queue on your side.

## Idempotency

Webhooks may be retried, so the same logical event can arrive more than once. To process safely:

* Use the `data.id` of the event (or a derived key like `event + data.messageId`) as an idempotency key.
* Keep a short-lived cache of processed keys (a few hours is enough for retries).
* Treat duplicates as no-ops.

## Listing and managing deliveries

**Settings → Webhooks** shows recent delivery attempts per webhook, including:

* Timestamp
* Event type
* Response status
* A truncated response body (up to \~1 KB) for debugging

If a webhook is deactivated due to repeated failures, the same page lets you re-enable it.

## Local development

The simplest way to develop against webhooks locally is to forward a public tunnel (for example, ngrok or Cloudflare Tunnel) to your dev server and point the webhook URL at the tunnel host. Use **Send test** to fire payloads on demand without waiting for real WhatsApp activity.

<Tip>
  Need to react to events that aren't on the list? Tell us at [support](https://flowella.io/support) — we add events as customers ask for them.
</Tip>

## Related

<CardGroup cols={2}>
  <Card title="Settings → Webhooks" icon="webhook" href="/settings/webhooks">
    Configure endpoints, signing secrets, and retries in the app.
  </Card>

  <Card title="API introduction" icon="code" href="/api-reference/introduction">
    Auth, errors, pagination, and rate limits for the REST API.
  </Card>

  <Card title="API keys" icon="key" href="/settings/api-keys">
    Create and rotate the bearer tokens your endpoint may need.
  </Card>

  <Card title="Notification events" icon="bell" href="/app/notification-events">
    The same events delivered into the in-app feed and email.
  </Card>
</CardGroup>
