Agent skill
Paragon Webhooks Skill
Receive and authenticate Paragon webhooks. Use when setting up a Paragon Webhook Destination endpoint, checking the shared secret header you configured, debugging 401s, or handling monitoring events like workflow_failure and credentials_failure.
Install this skill
npx skills add hookdeck/webhook-skills --skill paragon-webhooks
Paragon is an embedded integrations platform. A Webhook Destination (Settings > Monitoring, paid plans) sends a JSON POST to your URL when something goes wrong in your Paragon project: a workflow fails or a connected user's credential can no longer be refreshed. Requests are not signed. You authenticate them with a secret header that you add to the destination yourself.
This skill covers Webhook Destinations only. Managed Sync webhooks have no auth option and are not covered.
When to Use This Skill
- How do I receive Paragon webhooks?
- How do I authenticate Paragon Webhook Destination requests?
- Does Paragon sign webhook payloads? (No: use a shared secret header)
- How do I handle
workflow_failureandcredentials_failureevents? - Why does my Paragon webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(String(a)).digest();
const hb = crypto.createHash('sha256').update(String(b)).digest();
return crypto.timingSafeEqual(ha, hb);
}
function verifyParagonRequest(headers, secret = process.env.PARAGON_WEBHOOK_SECRET,
headerName = process.env.PARAGON_WEBHOOK_HEADER || 'Authorization') {
if (!secret) return false; // never accept when unconfigured
const received = headers[headerName.toLowerCase()]; // Node lowercases header names
return typeof received === 'string' && received !== '' && safeEqual(received, secret);
}
- You choose the header: add it to the Webhook Destination under Show more request options (headers). The name is user-chosen, so read it from
PARAGON_WEBHOOK_HEADER(defaultAuthorization) and look it up case-insensitively. PARAGON_WEBHOOK_SECRETis the exact, whole header value, including anyBearerprefix you typed. Compare the whole value; do not strip or parse a prefix.- Return
401for a missing header or a mismatch,400for a body that isn't a JSON object,200once handled. - Python:
hmac.compare_digeston bytes (encode both sides;strraises on non-ASCII). - Read the body raw (
express.raw({ type: '*/*' })) so any content type works, and parse JSON only after the credential check.
Security limits: a static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS, and rotate the credential (update the destination header and PARAGON_WEBHOOK_SECRET) if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
The payload has a top-level type field (repeated in data.type for workflow failures), a message, and user, project and integration objects.
Event (type) | Sent When |
|---|---|
workflow_failure | A workflow reaches the Failed status after an error. Adds workflow and data.error, data.executionUrl, data.workflowExecution, data.stepExecution |
credentials_failure | An OAuth refresh fails and can't be retried; the user's credential is marked invalid. data has userId and error (e.g. invalid_grant). Ask the user to reconnect in the Connect Portal |
If you change the Request Payload field of the destination, the body is whatever you template, so the type field may not be present.
Environment Variables
PARAGON_WEBHOOK_HEADER=Authorization # header name you added to the destination
PARAGON_WEBHOOK_SECRET=your-shared-secret # exact whole header value, incl. any "Bearer "
Local Development
Paragon can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 paragon --path /webhooks/paragon
Use the printed URL as the Webhook Destination URL in Paragon.
Resources
- references/overview.md - What Webhook Destinations are, events and payloads
- references/setup.md - Add a Webhook Destination and the secret header
- references/verification.md - Credential check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests