Quickstart
Create a Happa plugin draft, verify signed requests, and receive your first test.ping in under 10 minutes.
Step 1 - Create a plugin draft
Open the Developer Portal and create a new plugin. For your first draft, use:
- Webhook URL: a temporary tunnel URL such as
https://abc123.ngrok.app/webhooks/happa - Scopes: start with
events.readandattendees.read - Pricing:
freewhile you test
Save the draft and copy the generated webhookSecret. You will use it locally in the next step.
Step 2 - Install dependencies
mkdir happa-plugin-demo
cd happa-plugin-demo
npm init -y
npm install express
Step 3 - Create a working webhook server
This example verifies the HMAC signature before parsing JSON and responds quickly so Happa does not time out the delivery.
const express = require("express");
const crypto = require("crypto");
const app = express();
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
throw new Error("WEBHOOK_SECRET is required");
}
app.get("/health", (_req, res) => {
res.json({ ok: true });
});
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, WEBHOOK_SECRET)) {
return res.status(401).json({ error: "Invalid signature" });
}
const payload = JSON.parse(req.body.toString("utf8"));
console.log("Received", payload.type, payload.data);
res.status(200).json({ received: true });
});
app.listen(3000, () => {
console.log("Listening on http://localhost:3000");
});
Step 4 - Run it behind a tunnel
WEBHOOK_SECRET=whsec_your_secret node index.js
# ngrok
ngrok http 3000
# or cloudflared
cloudflared tunnel --url http://localhost:3000
Update your plugin draft so the webhook URL points to the tunnel endpoint plus /webhooks/happa.
Step 5 - Send a test webhook
In the plugin detail screen, click Test Webhook. Your server should log something like this:
Received test.ping {
message: "This is a test webhook from Happa. Your plugin is connected correctly.",
pluginId: "plg_123"
}
You're connected. At this point your endpoint is verifying signatures correctly and can receive real deliveries.
Step 6 - Store installation tokens for API access
Webhook deliveries do not include the installation API token. That token is created when a user installs your plugin, and your own onboarding flow should send it to your backend for storage.
// Example endpoint in your own backend
app.post("/happa/install", express.json(), async (req, res) => {
const { installationId, userId, apiToken, grantedScopes } = req.body;
await saveInstallation({
installationId,
userId,
apiToken,
grantedScopes
});
res.json({ stored: true });
});
Step 7 - Call the REST API
const response = await fetch(
"https://us-central1-happa-1aff4.cloudfunctions.net/pluginApi/events",
{
headers: {
Authorization: `Bearer ${apiToken}`
}
}
);
const data = await response.json();
console.log(data.events);
Next steps
- Read the Webhooks guide before you process real user traffic.
- Use the Scopes reference to request only the permissions you need.
- Check Event Types for payload shapes you can safely code against.