---
updatedAt: 2026-07-09T11:14:05.000Z
---

Fetch the complete documentation index at: https://developers.flipdish.com/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Subscribe to Flipdish Events (v3)

Configure webhook subscriptions to receive real-time HTTP POST notifications for events in your Flipdish account.

Use Flipdish **Flipdish Webhooks** to receive HTTP POSTs when things happen in your account (e.g., `menu.published.v1`). You configure which **event types** to receive and the **callback URL** in the Portal.

These docs describe the delivery contract and how to verify requests. See the **Event schemas** in [API Reference v3.0](https://developers.flipdish.com/v3.0/reference/) → *Flipdish Webhooks* for exact payloads.

***

### Prerequisites

* Only users with the Owner or Managed Owner role in the Flipdish portal can create webhooks, as these roles include the Edit Webhooks permission.
* Access to the Flipdish Portal with permission to manage the Org’s settings
* An HTTPS endpoint you control (staging recommended for first tests)
* Your subscription’s shared secret for request verification

***

## 1. How it works

1. In the Portal, go to **Settings → Webhooks** and create a **Webhook Subscription**.
2. Select one or more **event types** (e.g., `menu.published.v1`).
3. Provide your **HTTPS** callback URL
4. When an event occurs, Flipdish sends an HTTP **POST** to your URL with the event payload.

### Org-scoped ownership

Webhook subscriptions now belong to the **OrgId**, not a user or OAuth app.

***

## 2. Request format

* **Method:** `POST`
* **Content-Type:** `application/json`
* **Body:** Event payload (see schemas in API Reference), e.g. [menu published v1](https://developers.flipdish.com/v3.0/reference/post_menu-published-v1).

### Headers

```http
X-Flipdish-Event-Type: <event-type>            # e.g., menu.published.v1
X-Flipdish-Event-Version: v1
X-Flipdish-Timestamp: 2025-08-14T12:34:56.789Z
X-Flipdish-Signature: t=2025-08-14T12:34:56.789Z,sha256=<hex>
```

See the Delivery contract section below for details on each header.

### Response contract

* Return **any 2xx** to acknowledge receipt.
* Non-2xx or timeouts are treated as failures and will be retried.

### Delivery contract

* Method: `POST`, Content-Type: `application/json`
* Acknowledge with **any 2xx** once you persist the event
* Retries are automatic (at-least-once semantics). Handlers must be idempotent

| Header                                 | Description                                       |
| -------------------------------------- | ------------------------------------------------- |
| `X-Flipdish-Event-Type`                | Event type identifier (e.g., `menu.published.v1`) |
| `X-Flipdish-Event-Version`             | Event version (e.g., `v1`, `v2`)                  |
| `X-Flipdish-Timestamp`                 | ISO-8601 timestamp when the event was sent        |
| (planned) `X-Flipdish-Signature`       | HMAC-SHA256 signature for payload verification    |
| (planned) `X-Flipdish-Idempotency-Key` | Key to support idempotent processing              |

### Verifying requests

Below is a minimal Node.js/TypeScript example. It uses the raw request body, concatenates `${timestamp}.${rawBody}`, computes an HMAC-SHA256 with your subscription secret, and compares using a timing-safe comparison.

```ts
import crypto from 'node:crypto';

function timingSafeEqual(a: string, b: string): boolean {
  const ab = Buffer.from(a, 'utf8');
  const bb = Buffer.from(b, 'utf8');
  if (ab.length !== bb.length) return false;
  return crypto.timingSafeEqual(ab, bb);
}

export function verifySignature(
  rawBody: string,
  timestamp: string,
  signature: string,
  secret: string,
): boolean {
  const payload = `${timestamp}.${rawBody}`;
  const expected = crypto.createHmac('sha256', secret).update(payload, 'utf8').digest('hex');
  return timingSafeEqual(expected, signature);
}
```

Usage sketch (framework-agnostic):

```ts
// Pseudocode – ensure you read the RAW body bytes (do not JSON.parse before signing!)
const rawBody = getRawBodyFromHttpFramework(req); // string or Buffer → string
const timestamp = req.headers['x-flipdish-timestamp'] as string;
const signatureHeader = req.headers['x-flipdish-signature'] as string; // "sha256=<hex>" or similar
const signature = signatureHeader.split('sha256=')[1] ?? signatureHeader; // normalize to hex only

if (!verifySignature(rawBody, timestamp, signature, process.env.FLIPDISH_WEBHOOK_SECRET!)) {
  return res.status(401).send('Invalid signature');
}

// Persist event, then acknowledge with 2xx
res.status(200).send('OK');
```

***

## 3. Testing your endpoint

* Create a subscription to a non-critical URL (staging).
* Trigger a known event (e.g., publish a test menu).
* Confirm you receive a POST and that verification passes.
* Return `2xx` when you **persist** the event; retry logic will handle transient outages.

***

## 4. Operational guidance

* **Time budget:** Keep handlers fast; offload work to a queue.

***

## 5. Where to find event schemas

See [Flipdish events (v3) overview](flipdish-events-v3-overview) for the full list of event types and when to use each, or go to **API Reference → Webhook Events** and open the event type page (e.g., [menu.published.v1](https://developers.flipdish.com/v3.0/reference/post_menu-published-v1#/)) for the exact JSON schema and example payloads.

***

## 6. Troubleshooting

* **We don’t see any requests:** Check that the subscription is **enabled**, the URL is **public** and **HTTPS**, and that your firewall allows Flipdish IPs (if applicable).

***

## 7. Legacy vs New

| Aspect             | Legacy Webhooks                                | Flipdish Webhooks                                                   |
| ------------------ | ---------------------------------------------- | ------------------------------------------------------------------- |
| Ownership          | Webhooks tied to a user/OAuth app              | Org-scoped subscriptions (belong to `orgId`)                        |
| Location in Portal | User → Developer tools → OAuth apps → Webhooks | Settings → Webhooks                                                 |
| Verification       | Legacy app secret, varied patterns             | HMAC-SHA256 over `${timestamp}.${rawBody}` with timing-safe compare |
| Supported events   | Legacy set, limited growth                     | New `v3.0` events (e.g., `menu.published.v1`, `menu.published.v2`)  |