Onboard webhooks
When Hosted progress changes, you may want Slack or your own worker notified, without shipping host context or flags through the wire.
Onboard webhooks carry operational identifiers only. Configure an HTTPS endpoint in the dashboard (webhooks are dashboard-only, not MCP).
Events
| Event | When |
|---|---|
| onboard.flow_started | Hosted flow first evaluated for this user. Data: flowId, userId, workspaceKey, revision. |
| onboard.item_completed | Hosted step completed. Ids and stepKey only. |
| onboard.item_skipped | Optional step skipped. Ids and stepKey only. |
| onboard.flow_completed | Required items satisfied. Does not fire on dismiss. |
| onboard.flow_dismissed | User dismissed the widget. Not 100% complete. |
| onboard.flow_snoozed | User snoozed. Data includes until timestamp as an id/time, not host flags. |
| onboard.flow_resumed | Snooze or dismiss cleared. |
| onboard.progress_reset | Personal generation bumped. Shared workspace steps stay. |
Envelope fields also include workspaceId, productId (onboard), and timestamp. Reference: webhooks docs.
BYO adapters keep progress on your backend and do not emit these Bootstrapware Onboard webhooks for your store. Use onEvent on the widget instead.
Signature
Deliveries POST JSON and sign the raw body with HMAC-SHA256. The hex digest is sent as header X-Bootstrapware-Signature. A failed delivery is retried once.
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyBootstrapwareSignature(
rawBody: string,
header: string | null,
secret: string,
) {
if (!header) return false;
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(header, "hex");
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}Parse JSON only after verification. Do not re-serialize the body before hashing.
Related: Webhooks docs · Security · API overview · Summaries