Webhooks
A webhook pushes events to your system the moment they happen — the push counterpart to polling request status. There are two levels:
Project webhook — configured on a project's Settings page; captures events from that project only (submissions, requests, templates, API keys, voice-vendor setup…).
Account webhook — configured on Settings → Webhooks; can subscribe to every event in the system, across all projects plus account-level actions (sign-ins, channel integrations, account templates…).
Both show the full list of subscribable events, grouped by category, with checkboxes — tick a whole category or individual events, then save.
Setup
Project webhook: open the project → Settings → Webhook section. Account webhook: Settings → Webhooks.
Enter an http(s) URL, tick the events to capture, and save.
Click Send sample — VocaLoop POSTs a sample payload to the URL and shows you the HTTP status code your endpoint returned.
To try it without writing an endpoint, point the URL at the built-in echo helper http://localhost:3000/api/v1/webhook-echo (dev only — it logs the payload to the backend console and answers {"received": true}), or use a service like webhook.site.
Submission payload
When a project webhook subscribes to Submission received (the default), every submission on the project — whether it came from a sent fill link, the project's direct link, a direct API submission, or a builder preview test — triggers one POST with the full submission:
{
"event": "project.submission",
"project_id": "1f00…",
"project_name": "Job Application",
"workspace_id": "1f00…",
"workspace_name": "Job Application",
"request_id": "9be4…",
"status": "submitted",
"channel": "email",
"recipient": "jane@example.com",
"data": { "full_name": "Jane Doe", "position": "Engineer" },
"analytics": { "ip": "203.0.113.7", "timezone": "Asia/Kolkata", "…": "…" },
"submitted_at": "2026-07-16T09:14:31Z"
}request_idis the same id the send call returned — use it to correlate.channelis where the recipient actually submitted from (email,whatsapp,text, orpreviewfor builder tests);recipientisnullfor direct-link submissions.datakeys are the project's field names.
Event payload
Every other subscribed event (and submissions on the account webhook) is delivered as a compact activity event — the same entries you see on the Usage → Logs page:
{
"event": "request.sent",
"actor": "user",
"detail": "Request sent to jane@example.com",
"project_id": "1f00…",
"project_name": "Job Application",
"context": { "submission_id": "9be4…" },
"occurred_at": "2026-07-16T09:14:31+00:00"
}eventis the dotted action name shown next to each checkbox (project.created,api_key.deleted,account.signed_in, …).actoris who did it:user,api(an API key), orrecipient.project_id/project_namearenullfor account-level events.contextcarries event-specific extras (ids, channel names, …).
Delivery semantics
Best-effort, one attempt — there are no retries. A failure (non-2xx, timeout, unreachable host) is logged on the backend and never blocks or fails the action itself.
10-second timeout — respond quickly; accept the payload and process it asynchronously if your handling is slow.
Events are delivered only after the action has actually been committed, so you never hear about something that was rolled back.
Because delivery can be missed, treat webhooks as a fast path and status polling as the source of truth for anything critical.
Securing your endpoint
Payloads aren't signed, so:
Use an unguessable URL path (it acts as a shared secret) and always HTTPS in production.
workspace_id/workspace_nameare legacy duplicates ofproject_id/project_name, kept for receivers built before the projects rename.Validate that
project_idmatches a project you expect, and treatdataas untrusted user input.If you need certainty, confirm via
GET /api/v1/external/requests?ids=— the API-key-authenticated read can't be spoofed.