Webhooks deliver signed event payloads to your URL when nodes or phases are created, updated, or deleted. Use them to connect ProductBrain to Make.com, Zapier, n8n, or any workflow tool.
Register a webhook
{
"projectId": "my-project",
"url": "https://your-endpoint.example.com/webhook",
"events": ["node.added", "node.updated"]
}
url must be HTTPS and publicly routable
events is optional — omit to subscribe to all six event types
- Maximum 10 webhooks per project
Response:
{
"id": "wh_abc123",
"url": "https://your-endpoint.example.com/webhook",
"events": ["node.added", "node.updated"],
"secret": "whsec_..."
}
The secret is returned once at registration. Store it — you’ll need it to verify signatures.
Event types
| Event | Fires when |
|---|
node.added | A node is created |
node.updated | A node’s data changes (label, status, phase, etc.) |
node.deleted | A node is deleted |
iteration.added | A phase is created |
iteration.updated | A phase is renamed or status changes |
iteration.deleted | A phase is deleted |
Phases are named iteration in the API. The event names and the payload field are the stable API identifier and won’t change; the product UI calls the same thing a phase.
Payload
{
"event": "node.updated",
"project_id": "my-project",
"timestamp": "2026-06-11T10:30:00Z",
"node": {
"id": "job-42",
"type": "job",
"parentId": "approach-1",
"data": {
"label": "Price comparison shows for scanned barcode",
"status": "delivered",
"iteration": "MVP"
}
}
}
For iteration events, the payload includes iteration instead of node. Rename events include previous_name.
| Header | Description |
|---|
X-ProductBrain-Event | Event type (e.g. node.updated) |
X-ProductBrain-Delivery | Delivery ID — stable across all retries of one event. Dedup on it to stay idempotent. |
X-ProductBrain-Signature | sha256=<HMAC-SHA256 hex digest of raw body> |
Verifying signatures
const crypto = require('crypto');
function verify(body, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Delivery semantics
At-least-once. Every event is persisted durably before the first attempt, so a receiver that’s briefly down never loses it. We attempt delivery immediately; if your endpoint returns a non-2xx status or doesn’t respond within 5 seconds, we retry with exponential backoff (~2, 4, 8, 16, 32 minutes) up to 6 attempts total, then mark the delivery failed.
Because it’s at-least-once, your receiver must be idempotent — the same event can arrive more than once (e.g. you process it but acknowledge past the 5-second window, so we retry). Dedup on X-ProductBrain-Delivery, which is stable across every retry of one event.
Each attempt re-signs the body with your webhook’s current secret, so rotating the secret never breaks an in-flight retry.
- Health monitoring —
GET /api/v1/webhooks returns the last delivery time and HTTP status per webhook; poll it to monitor health.
- Reconcile failures — a delivery that exhausts all 6 attempts is marked
failed and not retried further; catch up by reading GET /api/v1/nodes.
List webhooks
GET /api/v1/webhooks?projectId=my-project
Delete a webhook
DELETE /api/v1/webhooks?projectId=my-project&webhookId=wh_abc123
Integration guides
Webhooks power the delivery tracker integrations via Make.com: