How it works
- You register a webhook with a URL and the events you care about. Parlay returns a
signing_secret(shown once — store it securely). - When a matching event fires server-side, Parlay POSTs the event JSON to your URL with a signed timestamp header.
- Your endpoint verifies the signature, processes the event, and returns 2xx within 30 seconds.
- If your endpoint is unreachable or returns 5xx, Parlay retries with exponential backoff. Retry schedule: ~1 min, 5 min, 25 min, 2 hr, 8 hr (5 attempts total).
Events
| Event | Fires when | Payload data shape |
|---|---|---|
analysis.completed | An analysis finishes successfully | { analysis_id, org_id, rep_id, scores, status } |
analysis.failed | An analysis fails to complete | { analysis_id, org_id, rep_id, error } |
persona.assigned | A persona_assign job completes | { org_id, rep_id, job_id, result } |
methodology.assigned | A methodology_assign job completes | { org_id, rep_id, job_id, result } |
profile.synthesized | A synthesis job completes | { org_id, rep_id, job_id, result } |
playbook.draft.completed | A playbook draft finishes generation | { draft_id, source_count, gemini_input_tokens, gemini_output_tokens, cost_usd_cents } |
playbook.published | A draft is promoted to an active playbook | { playbook_id, draft_id, version } |
insights.generated | An org insights snapshot completes | { org_id, snapshot_id } |
webhook.test | You called the test endpoint | { note: "test", timestamp } |
Register a webhook
signing_secret immediately — there’s no API to retrieve it again. If you lose it, call rotate_webhook_secret to generate a new one.
Receiving and verifying
Each webhook arrives as aPOST with these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | parlay-webhook/1 |
X-Parlay-Event | The event name (e.g. analysis.completed) |
X-Parlay-Delivery | UUID of this delivery attempt |
X-Parlay-Signature | t=<unix_timestamp>,v1=<hex_hmac> (Stripe convention) |
Signature verification (TypeScript)
Signature verification (Python)
Test your endpoint
Once registered, you can fire a test event:webhook.test event to your URL. Use this in your local dev (with ngrok or a Cloudflare Tunnel) to confirm signature verification works before going live.
Best practices
Respond fast, process async
Respond fast, process async
Return 2xx within a few seconds. If processing takes longer than that, push the event onto a queue and process it in a background worker. Webhooks that take 30+ seconds will time out and be retried.
Make your handler idempotent
Make your handler idempotent
The same event may be delivered more than once (network retries, your endpoint timing out). Use
event.id as a dedupe key in your DB.Verify the signature on every request
Verify the signature on every request
Anyone can POST to your URL. The signature header is what proves it came from Parlay. Reject any request without a valid signature.
Handle event types you don't care about gracefully
Handle event types you don't care about gracefully
If you only listen for
analysis.completed, but Parlay adds analysis.transcribed later, your endpoint will receive both. Switch on event and ignore unknown types — don’t 4xx, that triggers retries.Use multiple webhooks for environment isolation
Use multiple webhooks for environment isolation
Register one webhook for staging (
https://staging.your-app.com/...) and another for production. Both get the same events — your code routes by livemode.Rotating the signing secret
If the secret is ever compromised:signing_secret (only shown once). The old secret stops working immediately. Update your endpoint’s secret first, then rotate, to avoid a brief verification gap.
Pausing without losing config
paused: false.
