---
title: "Webhooks"
subtitle: "Get real-time notifications when activities happen on your calendar"
slug: "webhooks"
url: "https://help.luma.com/p/webhooks"
tags: ["Integrations"]
---

Webhooks allow you to receive real-time notifications when events happen on your Luma calendar—like when a guest registers, an event is created, or a ticket is purchased. Instead of polling the API, webhooks push data to your server as soon as events occur.

Webhooks are **only available for Luma Plus subscribers**. Find out more about [Luma Plus here](https://luma.com/pricing).

## Creating a Webhook

1. Visit the [Calendars Home](https://luma.com/home/calendars) and select your calendar
2. Navigate to **Settings → Developer**
3. In the **Webhooks** section, click **Create**
4. Enter your webhook URL
5. Select which action types you want to receive (or choose “All Actions”)
6. Click **Create** to save

## Available Event Types

**Calendar & Audience**

- **Calendar Event Added** - Triggered when any event appears on your calendar, including events managed by the calendar, events syndicated from other calendars, and external events you’ve added.
- **Calendar Person Subscribed** - Triggered when someone subscribes to your calendar.

**Events**

- **Event Created** - Triggered when a new event is created on your calendar.
- **Event Updated** - Triggered when event details are modified (title, date, location, etc.). Does not trigger for guest registrations.
- **Event Canceled** - Triggered when an event is canceled.

**Guests & Registrations**

- **Guest Registered** - Triggered when someone registers for an event. This includes re-registrations after a guest cancels and registers again.
- **Guest Updated** - Triggered when a guest registers and whenever guest information changes (check-in status, approval status, profile updates). Note: This fires for both new registrations and updates to existing guests.

**Tickets**

- **Ticket Registered** - Triggered for each individual ticket purchase. If a guest buys 3 tickets, this triggers 3 times, allowing you to track individual ticket sales and create unique experiences per ticket.

**Understanding Guest vs Ticket Events**

Use **Guest Registered** when you want to trigger once per person registering—for example, to send a welcome email. Use **Ticket Registered** when you need to track each individual ticket—for example, if you want to generate a unique QR code or badge for every ticket purchased.

## Webhook Payloads

Webhook payloads include a `type` field indicating the event type and a `data` object with the relevant information. For detailed payload schemas and field descriptions, see the [API documentation](https://docs.luma.com).

## Managing Webhooks

You can manage your webhooks from **Settings → Developer**:

- **View deliveries** - Click on a webhook to see recent delivery attempts and their status (delivered, pending, or failed)
- **Pause and resume** - Temporarily pause a webhook if you need to stop receiving events, then resume when ready
- **Delete** - Remove webhooks you no longer need

## Retry Behavior

If your webhook endpoint fails to respond or returns an error, Luma will automatically retry delivery:

- Up to 3 retry attempts
- Retries occur with exponential backoff (1 minute, 2 minutes, then 4 minutes)
- If your endpoint returns a 410 Gone status, the webhook will be automatically paused

## Verifying Webhook Signatures

Every webhook request includes a cryptographic signature so you can verify it came from Luma. When you create a webhook, Luma generates a secret (starting with `whsec_`) that you should store securely.

**Headers included with every webhook request:**

- `Webhook-Signature` — Signature in the format `t=<timestamp>,v1=<signature>`
- `Webhook-Id` — Unique ID for the webhook event
- `Webhook-Timestamp` — Unix timestamp (seconds) when the webhook was sent

**How to verify:**

1. Extract the timestamp (`t`) and signature (`v1`) from the `Webhook-Signature` header
2. Construct the signed payload: `{timestamp}.{request_body}` (the timestamp, a period, then the raw JSON body)
3. Compute an HMAC-SHA256 of the signed payload using your webhook secret
4. Compare the hex digest to the `v1` value from the header using a constant-time comparison

**Node.js / Bun example:**

```javascript
import { createHmac, timingSafeEqual } from "crypto";

function verifyWebhookSignature(secret, signatureHeader, body) {
  const parts = {};
  for (const part of signatureHeader.split(",")) {
    const idx = part.indexOf("=");
    parts[part.slice(0, idx)] = part.slice(idx + 1);
  }

  const signedPayload = `${parts["t"]}.${body}`;
  const expected = createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");

  const expectedBuf = Buffer.from(expected);
  const actualBuf = Buffer.from(parts["v1"]);
  return (
    expectedBuf.length === actualBuf.length &&
    timingSafeEqual(expectedBuf, actualBuf)
  );
}

// In your webhook handler:
const isValid = verifyWebhookSignature(
  "whsec_your_secret",
  req.headers["webhook-signature"],
  rawBody,
);
```

**Python example:**

```python
import hashlib
import hmac

def verify_webhook_signature(secret, signature_header, body):
    parts = {}
    for part in signature_header.split(","):
        key, _, value = part.partition("=")
        parts[key] = value

    signed_payload = f"{parts['t']}.{body}"
    expected = hmac.new(
        secret.encode(), signed_payload.encode(), hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, parts["v1"])
```

**Tip:** Check the timestamp to prevent replay attacks — reject requests where the timestamp is more than a few minutes old.

## Best Practices

- **Verify signatures** - Always verify the `Webhook-Signature` header to confirm requests are from Luma
- **Respond quickly** - Your endpoint should respond within 5 seconds
- **Return success codes** - Return a 2xx status code to indicate successful receipt
- **Handle duplicates** - In rare cases, you may receive the same event more than once—design your system to handle this gracefully

## Troubleshooting

If your webhooks aren’t working as expected:

- **Check delivery status** - View recent deliveries in the webhook panel to see if events are being sent and whether they’re succeeding or failing
- **Verify your endpoint** - Make sure your webhook URL is publicly accessible
- **Check your server logs** - Look for any errors in how your server is processing incoming webhooks
