Webhooks

Happa delivers real-time notifications to your plugin as signed POST requests. Verify the signature on the raw request body before you trust or parse the payload.

Delivery contract

Property Value
MethodPOST
Content typeapplication/json
Signature headerX-Happa-Signature: sha256=<hex>
Event headerX-Happa-Event: rsvp.created
User agentHappa-Webhooks/1.0
TimeoutProduction deliveries time out after roughly 8 seconds.

Payload envelope

{
  "id": "evt_1749254400_k3j9m2",
  "type": "rsvp.created",
  "timestamp": "2026-06-07T14:30:00.000Z",
  "data": {
    "eventId": "abc123",
    "eventTitle": "Summer Block Party",
    "userId": "user456",
    "joinMode": "open"
  }
}

Node.js verification example

const express = require("express");
const crypto = require("crypto");

const app = express();

app.use("/webhooks/happa", express.raw({ type: "application/json" }));

function verifySignature(rawBody, signatureHeader, secret) {
  const expected = "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");

  try {
    return crypto.timingSafeEqual(
      Buffer.from(signatureHeader || "", "utf8"),
      Buffer.from(expected, "utf8")
    );
  } catch {
    return false;
  }
}

app.post("/webhooks/happa", (req, res) => {
  const signature = req.headers["x-happa-signature"];

  if (!verifySignature(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }

  const payload = JSON.parse(req.body.toString("utf8"));
  queueWebhookForAsyncProcessing(payload);

  res.sendStatus(200);
});

Python verification example

import hashlib
import hmac
from flask import Flask, request

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_..."

def verify_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode("utf-8"),
        raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(signature_header or "", expected)

@app.post("/webhooks/happa")
def happa_webhook():
    if not verify_signature(request.get_data(), request.headers.get("X-Happa-Signature"), WEBHOOK_SECRET):
        return "Invalid signature", 401

    payload = request.get_json(force=True)
    print(payload["type"], payload["data"])
    return "ok", 200

Operational guidance

Failure handling

Failed deliveries appear in the Developer Portal's webhook logs with the HTTP status code and the first part of your response body. Happa does not currently retry failed deliveries automatically, so fix the issue and trigger another test.ping or wait for the next real event.

Payload reference: Use the Event Types reference for the exact fields Happa sends today, including ticket.sold and join-request events.