Webhooks and the events feed
Get every new call, booking request, urgent message and missed call in your own system the moment it happens: as a signed webhook, or by polling the events feed.
Julius Answer sends events (a summarized call, a booking request, an urgent message, a missed call and more) to your https address as signed JSON, in order, with retries. The same events are available from GET /api/v1/events for systems that poll. Both use the same shape.
Connect a webhook
- In the console: Settings, Connections, Webhook. Paste your https address, choose the events and press Connect. Send a test from the same screen.
- With the API:
PUT /api/v1/connections/webhookwith{"url": "https://…"}(full access). ThenPOST /api/v1/connections/webhook/testsends a sample. - The signing secret (
whsec_…) is shown once, when the webhook is first connected. Store it with your endpoint. - Addresses must be https on the public internet; private and local network addresses are refused.
Event types
| Type | When |
|---|---|
call.summarized | A call ended and its summary is ready |
message.urgent | A caller left an urgent message |
booking.requested | A caller asked for an appointment or a visit |
call.missed | A call the receptionist couldn't take (the reason is in text); a third try from the same caller sends another |
transfer.missed | A caller asked for a person and nobody picked up |
caller.unhappy | A caller sounded unhappy |
promise.due | Something the business promised a caller is coming due or slipping |
household.suggested | Two callers look like the same household; confirm in the console |
call_list.ready | The morning's list of people worth a call today is ready |
text.received | A caller texted your Julius Answer number (texting must be on). Clinics and law firms get it without the text |
The payload
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Julius-Webhooks/1
X-Julius-Event: booking.requested
X-Julius-Delivery: ea8f0f0d-74c5-46ee-95cf-fd05a6d8c6fb
X-Julius-Timestamp: 1792505805
X-Julius-Signature: sha256=5d1f…
{"id": "ea8f0f0d-74c5-46ee-95cf-fd05a6d8c6fb", "type": "booking.requested",
"occurred_at": "2026-10-20T14:16:45.776822+00:00", "business": "Joe Plumbing",
"caller": {"name": "Maria Lopez", "phone": "+13125550142"},
"title": "Booking request: Maria Lopez", "text": "Water heater install, Saturday morning",
"summary": "Water heater install, Saturday morning", "caller_type": null,
"call_id": null, "link": "https://app.julius.dealsflow.ai"}
- id is the same on every retry: use it to ignore duplicates.
- call_id (when the event is about a call) works with
GET /api/v1/calls/{call_id}for the transcript. - caller_type on
call.summarizediscustomer,offer(a sales pitch) or another kind of caller. - Healthcare and legal businesses: caller names and reasons are left out ("Open Julius to read it") unless the owner turns on "Include caller details" for the webhook.
Check the signature
The signature is an HMAC-SHA256, with your signing secret, of the timestamp, a dot and the raw body. Compare it in constant time and refuse old timestamps.
# Python
import hashlib, hmac, time
def verified(secret: str, body: bytes, timestamp: str, signature: str) -> bool:
if abs(time.time() - int(timestamp)) > 300:
return False
digest = hmac.new(secret.encode(), timestamp.encode() + b"." + body, hashlib.sha256).hexdigest()
return hmac.compare_digest("sha256=" + digest, signature)
// Node.js (use the raw body, before any JSON parsing)
import crypto from "node:crypto";
function verified(secret, rawBody, timestamp, signature) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(`${timestamp}.`).update(rawBody).digest("hex");
return expected.length === signature.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
Delivery
- Answer 2xx quickly (within 10 seconds), then do the work. Anything else counts as a failure.
- Retries wait longer each time, up to an hour apart, and events stay in order: a later event never overtakes one that is being retried.
- Turned off at once on
404or410, after five refusals in a row (400,401,403), or after 50 failures. The console shows the reason; saving the webhook again turns it back on. - No old floods: events older than a day aren't replayed, and a new webhook starts with what happens next.
- Redirects aren't followed. Use the final address.
The events feed (polling)
The same events, oldest first, for systems that can't receive webhooks. Read access is enough.
curl "https://app.julius.dealsflow.ai/api/v1/events?since_hours=24" -H "Authorization: Bearer $KEY"
{"events": [ … ], "next": "eyJhdCI6IjIwMjYtMTAtMjBUMTQ6…", "more": false}
- Store
nextand pass it asafternext time: you get only what's new, never the same event twice.nextstays the same when nothing happened. - more: true means there's another page right away; ask again with the new
next. - Filters:
types=call.summarized,booking.requested;limitup to 100;since_hoursup to 168 (only for the first request, withoutafter). - Freshness: events appear about 15 seconds after they happen, so a cursor never skips one that was still being written.
Frequently asked questions
Can I send Julius Answer calls to Zapier or Make?
Yes. Make a catch-hook URL in Zapier (Webhooks by Zapier) or Make (custom webhook), and connect it in the console under Settings, Connections, Webhook. Every new call and booking arrives there as JSON.
Do webhooks include the transcript?
They include the call summary, the caller's name and number, a short headline and a link to the call in the console. Fetch the full transcript with GET /api/v1/calls/{call_id}.
What if my endpoint is down?
Julius Answer retries with growing waits (up to an hour apart) and delivers in order once it's back, for up to a day. An answer of 404 or 410 turns the webhook off at once; the console shows why.
Should I use webhooks or the events feed?
Webhooks if you can receive https requests: they arrive within seconds. The events feed if you can't (a script on a schedule, a spreadsheet, a tool without a public address): poll it every minute or so with the next value.