Subscribe a URL to event types and get back a signing secret.
Webhooks are managed via REST today — there are no SDK helpers yet. The POST response includes the signing `secret` exactly once; capture it then and store it as an environment variable on the receiving service.
// POST /v1/orgs/:orgId/projects/:projectId/webhooksconst res = await fetch(
`https://api.meetdewey.com/v1/orgs/${orgId}/projects/${projectId}/webhooks`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DEWEY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://api.example.com/hooks/dewey',
events: ['document.ready', 'document.error', 'research.completed'],
description: 'Production ingestion hook',
}),
},
)
const endpoint = await res.json()
// Store endpoint.secret immediately — it's only returned now.
console.log(endpoint.id, endpoint.secret)
Validate the X-Dewey-Signature header with a timing-safe HMAC comparison.
Every delivery carries an `X-Dewey-Signature` header — `sha256=<hex>` of the raw request body, computed with your endpoint's signing secret. Always use a constant-time comparison; a non-timing-safe `===` leaks information about partial matches. Two more useful headers ship alongside: `X-Dewey-Event-Id` (the delivery UUID) and `X-Dewey-Event-Type`.
import { createHmac, timingSafeEqual } from'node:crypto'
function verifyWebhook(
rawBody: string,
signature: string,
secret: string,
): boolean {
const expected =
'sha256=' +
createHmac('sha256', secret).update(rawBody).digest('hex')
return (
signature.length === expected.length &&
timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
)
}
// Express / Next.js example — use raw body, not parsed JSON
app.post(
'/hooks/dewey',
express.raw({ type: 'application/json' }),
(req, res) => {
const sig = req.headers['x-dewey-signature'] as string
if (!verifyWebhook(req.body.toString(), sig, process.env.DEWEY_WEBHOOK_SECRET!)) {
return res.sendStatus(401)
}
const event = JSON.parse(req.body.toString())
// Ack fast, process async
res.sendStatus(200)
queue.enqueue(event)
},
)
List recent deliveries, see response codes, and retry failed ones.
Dewey retries failed deliveries up to 10 times with exponential backoff. Anything still failing after the last attempt is parked in the deliveries list with the response body and status code captured. Manually retrying re-runs the delivery from scratch and resets the attempt count.
// GET /v1/orgs/:orgId/projects/:projectId/webhooks/:endpointId/deliveriesconst res = await fetch(
`https://api.meetdewey.com/v1/orgs/${orgId}/projects/${projectId}/webhooks/${endpointId}/deliveries`,
{ headers: { Authorization: `Bearer ${process.env.DEWEY_API_KEY}` } },
)
const deliveries = await res.json()
for (const d of deliveries) {
console.log(d.id, d.status, d.attempts, '—', d.eventType)
}
// POST /v1/orgs/:orgId/projects/:projectId/webhooks/:endpointId/deliveries/:id/retryawait fetch(
`https://api.meetdewey.com/v1/orgs/${orgId}/projects/${projectId}/webhooks/${endpointId}/deliveries/${deliveries[0].id}/retry`,
{
method: 'POST',
headers: { Authorization: `Bearer ${process.env.DEWEY_API_KEY}` },
},
)