Webhooks
WebPinch sends a signed HTTP POST to a URL of your choosing whenever something you subscribed to happens in a project: a task is created, its status changes, a comment is posted, an audit finishes.
This is what Zapier, Make, n8n and your own services use to react to WebPinch in real time instead of polling.
Event types
| Event | When |
|---|---|
task.created | A task is created from the dashboard, the REST API, MCP, or a guest pin (widget, extension, review link) |
task.updated | One or more fields on a task change |
task.status_changed | Status (kanban column) changes — fires in addition to task.updated |
comment.created | A team member comments on a task |
audit.completed | A site audit finishes successfully |
audit.failed | A site audit fails |
Subscribers receive only the events they registered for. Reordering cards inside a column does not send task.updated.
Registration
Webhooks are managed with a personal access token that has the webhooks:write scope. You can register a webhook on any project you can open, and you see and manage only the webhooks you registered.
# Register
curl https://www.webpinch.com/api/v1/webhooks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"projectId": "...",
"url": "https://example.com/webhooks/webpinch",
"events": ["task.created", "task.status_changed"]
}'
# List (optionally for one project)
curl https://www.webpinch.com/api/v1/webhooks?projectId=... \
-H "Authorization: Bearer $TOKEN"
# Get one
curl https://www.webpinch.com/api/v1/webhooks/$ID \
-H "Authorization: Bearer $TOKEN"
# Delete
curl -X DELETE https://www.webpinch.com/api/v1/webhooks/$ID \
-H "Authorization: Bearer $TOKEN"The registration response includes a secret (whsec_…). It is shown once — store it somewhere safe; you need it to verify delivery signatures.
The URL must use https and resolve to a public address. A project can have up to 20 active webhooks. A webhook stops automatically if its creator loses access to the project.
Delivery format
Each delivery is an HTTP POST to your URL with:
POST /webhooks/webpinch HTTP/1.1
Content-Type: application/json
X-Webpinch-Event: task.status_changed
X-Webpinch-Delivery: 01HX...
X-Webpinch-Signature: sha256=<hex>Body shape:
{
"id": "01HX...",
"type": "task.status_changed",
"createdAt": "2026-05-04T10:30:00Z",
"projectId": "...",
"data": {
"taskId": "...",
"from": "in_progress",
"to": "done"
}
}Event data
Every task event (task.* and comment.created) also carries data.task: the task exactly as GET /api/v1/tasks/:id returns it, so you don’t need a second request to act on the event.
| Event | data fields |
|---|---|
task.created | taskId, task |
task.updated | taskId, changes (the field names that changed), task |
task.status_changed | taskId, from, to, task |
comment.created | taskId, commentId, comment (id, body, author, createdAt), task |
audit.completed | auditId, audit (status, rootUrl, summary, timestamps) |
audit.failed | auditId, audit (as above, plus error) |
Signature verification
X-Webpinch-Signature: sha256=<hex> is HMAC-SHA256(rawBody, webhookSecret) rendered as a lowercase hex string.
Always verify the signature before trusting the body. Example in Node:
import crypto from "node:crypto";
function verifyWebpinchSignature(rawBody, signatureHeader, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(expected)
);
}Use a constant-time comparison (like timingSafeEqual) to prevent timing attacks.
Responding, retries and ordering
- Respond fast with a 2xx. Anything else — a 3xx, 4xx, 5xx, or no answer within 10 seconds — counts as a failed attempt. Redirects are not followed.
- Retries: a failed delivery is retried 5 minutes, 1 hour, 6 hours and 24 hours after the first attempt (5 attempts in total).
- Unsubscribe: answer
410 Goneand the webhook is switched off immediately. A webhook whose deliveries keep failing every retry is switched off after 10 such deliveries in a row. - Ordering: events are dispatched in the order they happen, but retries can cause out-of-order delivery on your end. Always treat events as eventually-consistent.
- At-least-once: the same event may be delivered more than once. Dedupe by
id(the same for every subscriber of an event) orX-Webpinch-Delivery(unique per subscriber).
Local testing
Point a webhook at something like webhook.site to see real deliveries, or run a tunnel (ngrok, Cloudflare Tunnel) in front of your local handler — the URL must be publicly reachable.
See also
Last updated on