Custom Webhooks
Receive real-time event notifications in your own system.
Overview
Register any HTTPS URL and Empfio will POST a JSON payload every time a selected event occurs — no polling needed. This is the most flexible integration option, suitable for custom backends, internal tools, or any system that can receive HTTP requests.
Setup
- Go to Settings → Integrations → Webhooks
- Click Add webhook
- Enter your HTTPS endpoint URL
- Select which events to subscribe to
- Save — Empfio will immediately send a test ping to verify the URL is reachable
Payload format
All webhook payloads share this envelope:
{
"event": "booking.created",
"timestamp": "2026-03-07T10:30:00Z",
"organization_id": "550e8400-...",
"data": { ... }
}The data object shape depends on the event type — see Webhook Events for the full reference.
Available events
| Event | When it fires |
|---|---|
lead.created | A new lead is captured |
lead.status_changed | Lead status updated |
booking.created | An appointment is confirmed |
booking.cancelled | An appointment is cancelled |
conversation.escalated | AI agent escalated to a human |
Security
Each webhook request includes an X-Empfio-Signature header — an HMAC-SHA256 signature of the raw request body, signed with your webhook secret.
Verifying signatures
To verify that a webhook came from Empfio:
Python:
import hmac
import hashlib
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
key=secret.encode("utf-8"),
msg=body,
digestmod=hashlib.sha256,
).hexdigest()
return hmac.compare_digest(signature, expected)Node.js:
const crypto = require("crypto");
function verifySignature(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Retries
If your endpoint returns a non-2xx response or times out (10 seconds), Empfio retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | ~1 minute |
| 2nd retry | ~5 minutes |
| 3rd retry | ~30 minutes |
After all retries fail, the event is logged as failed.
Best practices
- Return 200 quickly — process the event asynchronously if your logic takes time
- Handle duplicates — use the
timestampand event data to deduplicate if your endpoint receives the same event twice - Verify signatures — always validate the
X-Empfio-Signatureheader in production - Use HTTPS — webhook URLs must use HTTPS for security