Deutero sends signed HTTP POSTs to your endpoint when things happen: an interview starts or completes, a study fills up, credits run out. Deliveries use the Standard Webhooks scheme.
There are two kinds of delivery, and the same code verifies both:
Subscribe an endpoint
create and rotate_secret are the only calls that return the signing secret. Store it before doing anything else.
Verify and parse deliveries
deutero.webhooks needs no API key, so it works in a receiver that never calls the API.
Good practice
- Use the raw body. The signature covers the exact bytes Deutero sent. Parsing and re-serializing the JSON changes them.
- Deduplicate. Deliveries can be retried.
event.webhook_id (the webhook-id header) is unique per message.
- Respond quickly with a 2xx, and do slow work in a background job.
- Don’t trust participant fields as identity.
external_participant_id and web_source come from the participant’s link. The signature proves the event came from Deutero, not who the participant is.
Event types
survey_id is the study ID under its older name. created_via is dashboard, import or api (which includes MCP clients).
Every event also has type, timestamp, webhook_id and simulated. Flow signals and any event type the SDK doesn’t know yet come back as a plain WebhookEvent, with data as a dict. simulated is True for signals sent from a simulated interview.
Get the live list, with each type’s payload fields, from the API:
Timestamps and replays
Deliveries whose webhook-timestamp is more than 5 minutes old (or in the future) are rejected, which protects against replay attacks. Change the window with tolerance= in seconds, or pass tolerance=None to verify stored deliveries you’re replaying:
Test your receiver locally
webhooks.sign builds the headers Deutero would send, so you can exercise your endpoint without a real delivery:
Manage endpoints
delete permanently removes an endpoint, its secret and its delivery log. To stop deliveries temporarily, set enabled=False instead.