Webhooks
GetItDone outbound webhooks (Pro+): the task event catalog, Standard Webhooks signature verification, delivery retries, and secret rotation.
Register an HTTPS endpoint and receive signed events when tasks change. Available on Pro and Team plans.
Event types
These event types are exactly what the delivery worker emits:
task.createdtask.updatedtask.archived
Example signed delivery
POST https://your-app.example.com/webhooks/getitdone
webhook-id: msg_2abc...
webhook-timestamp: 1753305600
webhook-signature: v1,g0hM9SsE9...base64signature...
{
"type": "task.created",
"data": {
"id": "T-123",
"title": "Ship the public API",
"status": "TODO"
}
}Verify the signature
Payloads are signed with the Standard Webhooks
scheme. Each request carries webhook-id, webhook-timestamp and
webhook-signature headers. Always verify the raw request bytes — parsing
and re-serializing the body first breaks the signature.
import { verifyWebhook } from '@nowgetitdone/sdk/webhooks'
// In your route handler — pass the RAW request body bytes, never a re-parsed
// object (re-serializing breaks the signature).
const result = verifyWebhook({
headers: request.headers,
rawBody: await request.text(),
secret: process.env.GETITDONE_WEBHOOK_SECRET, // whsec_… (pass both during rotation)
})
if (!result.valid) {
return new Response('invalid signature', { status: 400 })
}The SDK ships a verifyWebhook helper (published on npm —
source).
import { createHmac, timingSafeEqual } from 'node:crypto'
// Runtime-agnostic Standard Webhooks verification (no SDK required).
function verify(headers, rawBody, whsec) {
const id = headers['webhook-id']
const timestamp = headers['webhook-timestamp']
const signatureHeader = headers['webhook-signature'] // "v1,<base64> v1,<base64> …"
const secretBytes = Buffer.from(whsec.split('_')[1], 'base64')
const signed = `${id}.${timestamp}.${rawBody}`
const expected = createHmac('sha256', secretBytes)
.update(signed)
.digest('base64')
return signatureHeader.split(' ').some(part => {
const sig = part.split(',')[1]
return (
sig !== undefined &&
sig.length === expected.length &&
timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
)
})
}Retries & auto-disable
Deliveries that fail (non-2xx, timeout, connection error) are retried with backoff. An endpoint that keeps failing is automatically disabled to protect your receiver and our delivery pipeline; re-enable it once your endpoint is healthy again. Read the delivery log per endpoint to see attempts and their outcomes.
Secret rotation
Each endpoint has a signing secret prefixed whsec_. Rotating it keeps the
previous secret valid for a 24-hour grace window so in-flight deliveries
still verify. Accept both secrets during rotation, then drop the old one after
the window.
URL rules
Endpoint URLs must be https on port 443 and must not resolve to a private
or internal address. A rejected URL answers
422 webhook_url_rejected.
API documentation
The GetItDone public REST API — authentication, quickstart, errors, rate limits, pagination, idempotency, and webhooks.
API error codes
Every RFC 9457 problem code the GetItDone public API can return — the stable machine-readable code your integration switches on, with what each means and how to fix it.