Webhooks
Your tools hear when the service starts.
Voclyn’s webhooks send a signed POST to your address when a service starts, pauses, resumes or ends, so Zapier, Make, n8n or your own server can act on it. Owners and admins add endpoints in Dashboard › Integrations.
Events
| Event | When |
|---|---|
service.started | A room went live. |
service.paused | Worship, a break or a private moment began (data.service.reason says which). |
service.resumed | Captions flow again after a pause. |
service.ended | The service ended: its length, listeners and whether a transcript was saved. |
ping | Only from Send a test, in Dashboard › Integrations. |
Each endpoint chooses its events. A pause is sent once it has held for a moment, so a sound desk reconnecting mid-pause sends nothing. When the room saves transcripts, service.ended waits 10 seconds, so the transcript is whole when it arrives.
The body
Every event has the same shape: its id, type, when it happened (created), the body’s version, the church, and the service in data.service. While paused, reason is worship, private or break; the length, listeners and transcript arrive with service.ended.
{
"id": "6f0e8a52-1c7d-4c55-9a8e-2b8f0d3c4e11",
"type": "service.ended",
"created": "2026-11-01T18:14:40.512Z",
"version": "2026-10-30",
"church": { "id": "0d6c3b1a-5e4f-4a2b-9c8d-7e6f5a4b3c2d", "name": "Grace Church" },
"data": {
"service": {
"id": "9b2e5c1a-3d4f-4a6b-8c7d-1e2f3a4b5c6d",
"room": { "id": "4f1c2a9e-8b3d-4e62-9a71-0c5d6e7f8a9b", "name": "Sanctuary", "link": "https://voclyn.com/grace" },
"status": "ended",
"reason": null,
"started_at": "2026-11-01T17:02:11.004Z",
"ended_at": "2026-11-01T18:14:40.498Z",
"duration_seconds": 4349,
"peak_listeners": 38,
"languages": ["es", "zh-Hant"],
"voice_languages": ["es", "zh-Hant"],
"transcript": true
}
}
}With transcript: true, a full API key fetches it: GET /services/{id}/transcript (how).
Headers
| Header | What it holds |
|---|---|
Voclyn-Signature | t= the time it was signed (Unix seconds), v1= the signature. |
Voclyn-Event | The event’s type, such as service.started. |
Voclyn-Delivery | This delivery’s id; every try of it carries the same one. |
Content-Type | application/json |
Check the signature
Each endpoint has a signing secret (whsec_…), shown when you add it and from its Signing secret button. The signature is the HMAC-SHA256 of the time, a full stop and the body exactly as it arrived, keyed with the whole secret, in hex. Compare it in constant time, and refuse one more than five minutes old, so a recorded delivery can’t be played back later.
import crypto from "node:crypto";
// The body exactly as it arrived (in Express: express.raw({ type: "application/json" })).
function fromVoclyn(rawBody, header, secret) {
const parts = Object.fromEntries((header ?? "").split(",").map((p) => p.split("=")));
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!parts.v1 || !(age <= 300)) return false;
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}import hashlib, hmac, time
def from_voclyn(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in (header or "").split(",") if "=" in p)
t, v1 = parts.get("t", ""), parts.get("v1", "")
if not t.isdigit() or abs(time.time() - int(t)) > 300:
return False
expected = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1)Check the raw body, before any JSON parser touches it: the same event re-serialised won’t match.
Answer, then work
Answer with any 2xx status within 10 seconds, then do the slow work. Anything else counts as a failure and is tried again: an error, no answer in time, or a redirect, which is never followed. Deliveries go only to public https addresses.
Retries and order
A failed delivery is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours: 7 tries in all, over about a day. Then it is marked failed in the dashboard’s list of deliveries, which keeps 30 days.
A retry can arrive after a later event, and an event can arrive twice. Use the event’s id to do its work once, and its created time to tell which came first.
Test an endpoint
Send a test in Dashboard › Integrations › Webhooks delivers a ping event at once and shows how your address answered. Turning an endpoint off stops its deliveries and drops the ones still waiting; turning it on again starts with the next event.
What churches do with them
- Tell the team. On
service.started, a Zapier or Make step posts “We’re live in the Sanctuary” to Slack, Teams or WhatsApp, with the room’s link. - Keep every sermon. On
service.endedwith a transcript, a server fetches it in each language and saves it to your drive or website. - Follow the room.
service.pausedandservice.resumeddrive anything that should rest during worship, such as a sign or a lower third.
Questions
What churches ask.
Something else on your mind? Write to us at hello@voclyn.com.
Yes: a signed POST when a service starts, pauses, resumes or ends, to up to 5 addresses per church, on the paid plans and in the free month.
Check the Voclyn-Signature header: the HMAC-SHA256 of the time, a full stop and the raw body, keyed with the endpoint's signing secret, and no more than five minutes old.
Each delivery is tried again for about a day, then marked failed in Dashboard › Integrations, where the last deliveries are listed.
Yes. Zapier's Webhooks by Zapier, Make and n8n each give you an address that catches them; paste it as an endpoint.
Try it this Sunday.
Set up your church in a minute and hear it live with your own voice. Your QR code keeps working after the free month.
Your first month is on us: the Church plan, normally $99 a month, with unlimited listeners, translation hours and AI voiceover. No card; then choose a plan, or stay on Free.