Your own backend
Store leads in your database and trigger your own onboarding logic.
Send every submission, booking and visitor action to any HTTPS endpoint as JSON. Requests are signed with HMAC-SHA256, carry a delivery ID for deduplication and are retried when your server is down.
What arrives in Webhooks
A signed JSON POST with the answers and page context
Triggers
You will need
A visitor submits your widget, WidgetJar delivers it in seconds. Press the button to send another one.
Your widget
Talk to our team
Waiting for the first delivery…
Store leads in your database and trigger your own onboarding logic.
Run a Cloudflare Worker or AWS Lambda for each sign-up.
Feed button clicks and contact requests into your data warehouse.
Create an endpoint that accepts POST requests with a JSON body and answers with a 2xx status.
In WidgetJar, add the Webhook integration with your URL and a long random secret.
Verify the X-WidgetJar-Signature header on your server, then click “Send test”.
Integrations are set per widget. Open a widget in the WidgetJar panel and choose Integrations → Add integration. Every connected tool has a “Send test” button so you can check it before going live.
Everything your endpoint needs to know to receive WidgetJar events safely.
WidgetJar sends a POST with a JSON body. Your answers are in data, with keys named after your form fields.
| Header | Value |
|---|---|
| Content-Type | application/json |
| User-Agent | WidgetJar-Webhook/1.0 |
| X-WidgetJar-Event | The event type, for example submission |
| X-WidgetJar-Delivery | Unique delivery ID, the same on every retry |
| X-WidgetJar-Timestamp | Unix time in seconds when the request was signed |
| X-WidgetJar-Signature | sha256=<hex HMAC>, sent when you set a secret |
{
"id": "1842",
"event_type": "submission",
"widget_id": "0b8b6f8e-6f4f-4a52-9a5c-8f0f2b2f1c11",
"timestamp": "2026-11-02 10:00:00.000",
"data": {
"name": "Ada Lovelace",
"email": "[email protected]",
"message": "Can we book a demo next week?"
},
"context": {
"page": "https://example.com/contact",
"country": "GB",
"device": "desktop"
}
}| event_type | Meaning | Sent by |
|---|---|---|
| submission | New form submission | Detected form, popup and other form widgets |
| booking | New booking | Reservation widget |
| ticket_click | Ticket link opened | Event calendar |
| calendar_add | Event added to a calendar | Event calendar |
| event_share | Event link copied | Event calendar |
| event_view | Event details opened | Event calendar |
| button_click | Button clicked | Detected button |
| contact_click | Contact link clicked | Detected button |
Event calendar and detected button actions are only sent for the actions you tick on the integration in the panel.
When you set a secret, WidgetJar computes an HMAC-SHA256 of {timestamp}.{raw body} and sends it as X-WidgetJar-Signature. Recompute it on your server, compare in constant time and reject requests older than five minutes.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const SECRET = process.env.WIDGETJAR_WEBHOOK_SECRET;
// Keep the raw body: the signature covers the exact bytes we sent.
app.post('/hooks/widgetjar', express.raw({ type: 'application/json' }), (req, res) => {
const timestamp = req.get('X-WidgetJar-Timestamp');
const signature = req.get('X-WidgetJar-Signature') ?? '';
const expected =
'sha256=' +
crypto.createHmac('sha256', SECRET).update(`${timestamp}.${req.body}`).digest('hex');
const valid =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
if (!valid || !fresh) return res.sendStatus(401);
const event = JSON.parse(req.body);
// Use event.id to ignore a delivery you have already processed.
console.log(event.event_type, event.data);
res.sendStatus(200);
});import hashlib, hmac, os, time
from flask import Flask, abort, request
app = Flask(__name__)
SECRET = os.environ["WIDGETJAR_WEBHOOK_SECRET"].encode()
@app.post("/hooks/widgetjar")
def widgetjar_hook():
timestamp = request.headers.get("X-WidgetJar-Timestamp", "")
signature = request.headers.get("X-WidgetJar-Signature", "")
body = request.get_data() # raw bytes
expected = "sha256=" + hmac.new(
SECRET, timestamp.encode() + b"." + body, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
abort(401)
if abs(time.time() - int(timestamp or 0)) > 300:
abort(401)
event = request.get_json()
print(event["event_type"], event["data"])
return "", 200Create a widget or add WidgetJar to the form you already have. Integrations are included on every plan.
Get started free