Agent skill
Reducto Webhooks Skill
Receive and verify Reducto webhooks for async document parsing jobs. Use when setting up Reducto webhook handlers, debugging Svix signature verification (svix-id, svix-timestamp, svix-signature), or handling async.update job notifications with status Completed or Failed from /parse_async, /extract_async, /split_async, or /pipeline_async.
Install this skill
npx skills add hookdeck/webhook-skills --skill reducto-webhooks
Reducto (document parsing / extraction API) delivers async job notifications through Svix. When a job submitted with async.webhook.mode = "svix" finishes, Reducto sends an async.update webhook whose body reports the job status (Completed or Failed) and job_id.
When to Use This Skill
- How do I receive Reducto webhooks for async parse/extract jobs?
- How do I verify Reducto webhook signatures (
svix-signature)? - How do I handle a Reducto job that
CompletedorFailed? - Why is my Reducto webhook signature verification failing?
- How do I pass
metadatathrough a Reducto async job and read it in the webhook?
Verification (core)
Reducto webhooks use the Svix scheme. Signed content is ${svix-id}.${svix-timestamp}.${raw_, HMAC-SHA256 keyed with the base64-decoded bytes after whsec_, base64-encoded, sent as space-delimited v1,<sig> entries. The official svix SDK (Node and Python) handles all of it, including the 5-minute timestamp tolerance and constant-time comparison.
import { Webhook } from 'svix'; // svix 2.x is ESM-only
const wh = new Webhook(process.env.REDUCTO_WEBHOOK_SECRET); // "whsec_..." from Svix dashboard
wh.verify(rawBody, { // raw Buffer/string, NOT parsed JSON
'svix-id': req.headers['svix-id'],
'svix-timestamp': req.headers['svix-timestamp'],
'svix-signature': req.headers['svix-signature'],
}); // throws WebhookVerificationError on bad signature / stale timestamp
// svix 2.x verify() returns undefined — parse the raw body after it passes
const { status, job_id, metadata } = JSON.parse(rawBody.toString('utf8'));
Python uses the same API: Webhook(secret).verify(body_ from svix.webhooks, raising WebhookVerificationError.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Webhook Event and Payload
Reducto sends a single Svix event type, async.update, for every async job. The body has no type field — branch on status:
status | Triggered When | Common Use Cases |
|---|---|---|
Completed | Async job finished successfully | Fetch results with client.job.get(job_id), store output |
Failed | Async job failed | Log, alert, retry the job |
{
"status": "Completed",
"job_id": "a1b9090e-c9ae-420b-9726-f658afbbe338",
"metadata": { "user_id": "123", "document_type": "invoice" }
}
The webhook only carries status — retrieve the actual results via the Reducto API using job_id. metadata echoes whatever you passed in async.metadata.
Environment Variables
# Svix signing secret for your endpoint (starts with whsec_)
REDUCTO_WEBHOOK_SECRET=whsec_xxxxx
Get it in Reducto Studio → Webhooks (opens the Svix dashboard) → your endpoint → Signing Secret.
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 reducto --path /webhooks/reducto
Reference Materials
- references/overview.md - How Reducto async webhooks work, payload, statuses
- references/setup.md - Svix dashboard setup, signing secret, submitting jobs with webhooks
- references/verification.md - Signature verification details and gotchas