Webhooks
Real-time event notifications for voice conversations, prior authorization updates, adverse events, and patient interactions. Configure webhooks to integrate Isomera events with your EHR, CRM, or operational systems.
Overview
Webhooks deliver HTTP POST requests to your configured endpoints when events occur in the Isomera platform. All payloads are JSON-encoded and include standard metadata for event identification and deduplication.
Event Categories
| Category | Events |
|---|---|
| Voice Calls | call.started, call.completed, call.failed |
| Patient Safety | adverse_event.reported, adverse_event.escalated |
| Prior Authorization | pa.required, pa.submitted, pa.decision |
| Benefits | benefits.verified, coverage.confirmed |
| Cases | case.created, conversation.created |
| HealthConnect Demo | benefits.edi.completed, payer_call.requested |
| Patient Actions | callback.requested, patient.verified |
Payload Structure
All webhook payloads follow a consistent structure:
{
"id": "evt_7x8y9z0a1b2c3d4e",
"event": "call.completed",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:35:00.000Z",
"data": {
"object": "voice_call",
// Event-specific data
},
"livemode": true
}HealthConnect demo events
When using the HealthConnect widget demo, Isomera can emit outgoing integration webhooks for key workflow steps. These are best-effort and are only sent when configured.
Configuration
Set OUTGOING_WEBHOOK_URL (and optionally OUTGOING_WEBHOOK_SECRET) in the Isomera backend.
Events
case.created— a Case is created when widget chat startsconversation.created— the canonical Conversation is created (chat or voice)benefits.edi.completed— EDI benefits snapshot completed (via/verify)payer_call.requested— follow-up action created to call the payer (via/start_payer_call)
{
"id": "evt_8f0f9c8f1f2a4b0bb5f3eae7c9d12b34",
"event": "case.created",
"api_version": "2025-12-26",
"created_at": "2025-12-26T07:40:00.000Z",
"data": {
"object": "case",
"id": "f7c2a5c6-3c2a-4c43-8e92-1a2b3c4d5e6f",
"status": "open",
"client_slug": "healthconnect",
"widget_client_id": "0c7b3c4d-1a2b-4c43-8e92-3c2a5c6f7d8e"
},
"livemode": false
}call.started
Triggered when a voice conversation begins. Use this to update real-time dashboards or initiate parallel processes like patient record lookups.
Unique voice call ID in the Isomera system.
External conversation identifier for tracking.
Caller's phone number in E.164 format.
Call direction: inbound or outbound.
Type of call: patient_inbound or insurance_outbound.
AI agent handling the call: benefits, insurance, medical.
{
"id": "evt_a1b2c3d4e5f6g7h8",
"event": "call.started",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:30:00.000Z",
"data": {
"object": "voice_call",
"id": 1847,
"call_id": "conv_8701kb8rek5dfb7sc9qm",
"phone_number": "+14155551234",
"direction": "inbound",
"call_type": "patient_inbound",
"agent_type": "benefits",
"status": "in_progress",
"started_at": "2024-12-18T10:30:00.000Z"
},
"livemode": true
}call.completed
Sent when a voice conversation ends successfully. Includes complete call analysis, transcript summary, patient satisfaction, and any follow-up actions identified during the call.
Linked patient ID after verification, or null if unverified.
Total call duration in seconds.
Primary outcome: coverage_confirmed, pa_required,callback_scheduled, verification_failed.
Patient rating from 1-5, collected at end of call.
Topics covered: benefits_verification, pa_requirements, cost_sharing, etc.
Medications mentioned during the call.
AI-generated analysis including summary, key findings, and recommended actions.
List of required follow-up actions with priority and due dates.
Call compliance percentage (0-100) against configured SOP rules.
{
"id": "evt_b2c3d4e5f6g7h8i9",
"event": "call.completed",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:35:00.000Z",
"data": {
"object": "voice_call",
"id": 1847,
"call_id": "conv_8701kb8rek5dfb7sc9qm",
"phone_number": "+14155551234",
"direction": "inbound",
"status": "completed",
"patient_id": 456,
"duration_seconds": 312,
"call_outcome": "pa_required",
"patient_satisfaction_score": 4,
"verified": true,
"verification_method": "name_dob_memberid",
"topics_discussed": [
"benefits_verification",
"pa_requirements",
"cost_sharing"
],
"medications_discussed": ["Rixomab"],
"jcodes_discussed": {
"Rixomab": "J0000"
},
"analysis": {
"summary": "Patient called to verify coverage for Rixomab. Prior authorization is required. Deductible has been met, patient has 20% coinsurance.",
"key_findings": [
"Prior authorization required for Rixomab",
"Annual deductible of $1,500 has been met",
"20% coinsurance applies after deductible",
"PA can be submitted via fax or portal"
],
"next_steps_for_patient": "Provider will submit PA request. Patient will be notified within 5-7 business days.",
"internal_notes": "Patient was well-informed and satisfied with the information provided."
},
"follow_up_actions": [
{
"action": "Submit prior authorization",
"assigned_to": "provider",
"priority": "high",
"status": "pending",
"due_date": "2024-12-21"
}
],
"compliance_score": 94.5,
"callback_requested": false,
"email_summary_requested": true,
"callback_email": "patient@email.com",
"audio_url": "/api/v1/calls/1847/audio",
"started_at": "2024-12-18T10:30:00.000Z",
"completed_at": "2024-12-18T10:35:12.000Z"
},
"livemode": true
}adverse_event.reported
Sent when a patient reports an adverse event during a call. Contains structured adverse event data for pharmacovigilance workflows and FDA reporting requirements.
When severity is severe or life_threatening, the requires_immediate_action flag is true. Configure your integration to trigger immediate alerts to medical safety staff.
Unique adverse event case identifier for tracking.
Event severity: mild, moderate, severe,life_threatening.
List of reported symptoms.
Medication associated with the adverse event.
When the adverse event occurred (ISO 8601 date).
True for severe/life-threatening events requiring urgent response.
{
"id": "evt_c3d4e5f6g7h8i9j0",
"event": "adverse_event.reported",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:33:00.000Z",
"data": {
"object": "adverse_event",
"id": 234,
"case_number": "AE-456-1734517980",
"voice_call_id": 1847,
"patient_id": 456,
"patient": {
"id": 456,
"first_name": "John",
"last_name": "Doe",
"mrn": "MRN1456"
},
"severity": "moderate",
"requires_immediate_action": false,
"description": "Patient reported persistent headache and mild fatigue starting 3 days after infusion",
"medication_name": "Rixomab",
"symptoms": [
"headache",
"fatigue",
"mild nausea"
],
"occurrence_date": "2024-12-15",
"reported_date": "2024-12-18T10:33:00.000Z",
"status": "pending_review",
"medical_team_notified": false
},
"livemode": true
}{
"id": "evt_urgent123456789",
"event": "adverse_event.reported",
"api_version": "2024-12-01",
"created_at": "2024-12-18T14:22:00.000Z",
"data": {
"object": "adverse_event",
"case_number": "AE-789-1734531720",
"severity": "life_threatening",
"requires_immediate_action": true,
"description": "Patient reporting difficulty breathing and chest tightness",
"medication_name": "Humira",
"symptoms": [
"dyspnea",
"chest_tightness",
"facial_swelling"
],
"status": "pending_review",
"medical_team_notified": true
},
"livemode": true
}pa.required
Triggered when a prior authorization requirement is identified during a benefits verification call or insurance outreach. Includes submission details and turnaround expectations.
Drug requiring prior authorization.
Insurance payer information.
Available PA submission methods (fax, portal, phone).
Expected business days for PA decision.
{
"id": "evt_d4e5f6g7h8i9j0k1",
"event": "pa.required",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:34:00.000Z",
"data": {
"object": "pa_requirement",
"voice_call_id": 1847,
"patient_id": 456,
"medication_name": "Rixomab",
"j_code": "J0000",
"payer": {
"id": "aetna",
"name": "Aetna"
},
"plan": {
"id": "aetna_ppo_2024",
"name": "Aetna PPO Select"
},
"pa_on_file": false,
"submission_methods": [
{
"method": "fax",
"fax_number": "1-800-555-0199",
"preferred": true
},
{
"method": "portal",
"portal_url": "https://portal.aetna.com/pa"
}
],
"turnaround_days": 5,
"required_documents": [
"Clinical notes",
"Lab results",
"Treatment history"
],
"step_therapy_required": false,
"identified_at": "2024-12-18T10:34:00.000Z"
},
"livemode": true
}callback.requested
Sent when a patient requests a callback from a human specialist or requests an email summary of their call. Use this to route to your call center queue or trigger automated email workflows.
Type of callback: phone or email.
Reason for the callback request.
Urgency level: low, normal, high, urgent.
Patient's preferred callback time: morning, afternoon, evening.
{
"id": "evt_e5f6g7h8i9j0k1l2",
"event": "callback.requested",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:34:30.000Z",
"data": {
"object": "callback_request",
"voice_call_id": 1847,
"patient_id": 456,
"patient": {
"id": 456,
"first_name": "John",
"last_name": "Doe",
"phone": "+14155551234"
},
"callback_type": "phone",
"callback_phone": "+14155551234",
"reason": "Patient has questions about PA appeal process",
"priority": "high",
"preferred_time": "afternoon",
"call_summary": "Patient verified coverage for Rixomab. PA is required. Patient wants to discuss appeal options if PA is denied.",
"requested_at": "2024-12-18T10:34:30.000Z"
},
"livemode": true
}{
"id": "evt_f6g7h8i9j0k1l2m3",
"event": "callback.requested",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:35:00.000Z",
"data": {
"object": "callback_request",
"callback_type": "email",
"callback_email": "patient@email.com",
"patient_id": 456,
"reason": "Send written summary of benefits verification",
"priority": "normal",
"call_summary": "Coverage confirmed for Rixomab with 20% coinsurance. PA required - provider will submit.",
"include_in_email": [
"coverage_details",
"pa_requirements",
"cost_estimate",
"next_steps"
]
},
"livemode": true
}benefits.verified
Triggered when insurance benefits have been successfully verified. Benefits can be verified through multiple methods: EDI/clearinghouse, AI voice call, or a combination. The verification_sources array indicates which methods were used, and field_sources shows the provenance of each field.
Methods used for verification: edi, voice, pdf. Multiple sources may be used for comprehensive verification.
Voice call ID if voice verification was used, otherwise null.
Maps each benefit field to its source method (edi, voice, pdf) for audit trails.
Verification Methods
| Method | Speed | Data Available |
|---|---|---|
edi | ~2 seconds | Eligibility, deductibles, copays, network status |
voice | 3-8 minutes | PA requirements, accumulators, step therapy, submission details |
pdf | ~10 seconds | Plan details, tier copays, formulary status |
{
"id": "evt_g7h8i9j0k1l2m3n4",
"event": "benefits.verified",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:36:00.000Z",
"data": {
"object": "benefit_verification",
"id": 892,
"patient_id": 456,
"payer": {
"id": "aetna",
"name": "Aetna"
},
"plan": {
"id": "aetna_ppo_2024",
"name": "Aetna PPO Select"
},
"verification_sources": ["edi"],
"voice_call_id": null,
"status": "completed",
"coverage_status": "active",
"in_network": true,
"deductible_individual": 1500,
"deductible_met_ytd": 1500,
"out_of_pocket_max": 6000,
"out_of_pocket_met_ytd": 2340,
"copay_specialist": 40,
"coinsurance_percent": 20,
"pa_required": null,
"field_sources": {
"coverage_status": "edi",
"deductible_individual": "edi",
"deductible_met_ytd": "edi",
"copay_specialist": "edi",
"coinsurance_percent": "edi"
},
"verified_at": "2024-12-18T10:36:02.000Z",
"duration_seconds": 2
},
"livemode": true
}{
"id": "evt_h8i9j0k1l2m3n4o5",
"event": "benefits.verified",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:41:00.000Z",
"data": {
"object": "benefit_verification",
"patient_id": 456,
"medication_name": "Rixomab",
"verification_sources": ["voice"],
"voice_call_id": 1848,
"status": "completed",
"pa_required": true,
"pa_on_file": false,
"pa_submission_method": "fax",
"pa_fax_number": "1-800-555-0199",
"pa_turnaround_days": 5,
"step_therapy_required": false,
"in_network": true,
"coinsurance_percent": 20,
"deductible_met": true,
"reference_number": "REF-2024121800456",
"rep_name": "Sarah M.",
"field_sources": {
"pa_required": "voice",
"pa_submission_method": "voice",
"pa_fax_number": "voice",
"coinsurance_percent": "voice",
"step_therapy_required": "voice"
},
"verified_at": "2024-12-18T10:41:00.000Z",
"duration_seconds": 312
},
"livemode": true
}{
"id": "evt_i9j0k1l2m3n4o5p6",
"event": "benefits.verified",
"api_version": "2024-12-01",
"created_at": "2024-12-18T10:45:00.000Z",
"data": {
"object": "benefit_verification",
"patient_id": 456,
"medication_name": "Rixomab",
"verification_sources": ["edi", "voice"],
"voice_call_id": 1848,
"status": "completed",
"coverage_status": "active",
"in_network": true,
"deductible_individual": 1500,
"deductible_met_ytd": 1500,
"out_of_pocket_max": 6000,
"out_of_pocket_met_ytd": 2340,
"copay_specialist": 40,
"coinsurance_percent": 20,
"pa_required": true,
"pa_on_file": false,
"pa_submission_method": "fax",
"pa_fax_number": "1-800-555-0199",
"pa_turnaround_days": 5,
"step_therapy_required": false,
"reference_number": "REF-2024121800456",
"rep_name": "Sarah M.",
"field_sources": {
"coverage_status": "edi",
"deductible_individual": "edi",
"deductible_met_ytd": "edi",
"copay_specialist": "edi",
"coinsurance_percent": "edi",
"pa_required": "voice",
"pa_submission_method": "voice",
"pa_fax_number": "voice",
"step_therapy_required": "voice"
},
"method_results": [
{
"method": "edi",
"status": "completed",
"duration_seconds": 2,
"completed_at": "2024-12-18T10:36:02.000Z"
},
{
"method": "voice",
"status": "completed",
"duration_seconds": 312,
"completed_at": "2024-12-18T10:45:00.000Z"
}
],
"verified_at": "2024-12-18T10:45:00.000Z"
},
"livemode": true
}Webhook Configuration
Register and manage webhook endpoints through the API. You can subscribe to specific event types and configure separate endpoints for different event categories.
Register a webhook
/webhooksHTTPS endpoint URL to receive webhook events. Must use TLS 1.2+.
List of event types to subscribe to. Use ["*"] for all events.
Human-readable description of the webhook purpose.
Arbitrary key-value pairs for your reference.
List webhooks
/webhooksRetrieve a webhook
/webhooks/{webhook_id}Update a webhook
/webhooks/{webhook_id}Delete a webhook
/webhooks/{webhook_id}curl https://api.isomera.ai/v1/webhooks \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/isomera",
"events": [
"call.completed",
"adverse_event.reported",
"pa.required"
],
"description": "Production EHR integration"
}'{
"id": "wh_1a2b3c4d5e6f7g8h",
"object": "webhook",
"url": "https://your-server.com/webhooks/isomera",
"events": [
"call.completed",
"adverse_event.reported",
"pa.required"
],
"description": "Production EHR integration",
"status": "active",
"secret": "whsec_7x8y9z0a1b2c3d4e5f6g7h8i9j0k",
"created_at": "2024-12-18T10:00:00.000Z"
}{
"url": "https://your-server.com/webhooks",
"events": ["*"]
}Signature Verification
Every webhook request includes a signature for verifying authenticity. Always validate signatures before processing webhook payloads to prevent spoofing attacks.
Request Headers
| Header | Description |
|---|---|
X-Isomera-Signature | HMAC-SHA256 signature of the request body |
X-Isomera-Timestamp | Unix timestamp when the request was sent |
X-Isomera-Event | Event type (e.g., call.completed) |
X-Isomera-Delivery-Id | Unique delivery ID for idempotency |
Signature Format
The signature is computed as sha256=HMAC-SHA256(timestamp.payload, secret). The signed payload is the timestamp concatenated with the raw request body.
Reject requests where X-Isomera-Timestamp is more than 5 minutes old to prevent replay attacks.
import hmac
import hashlib
import time
def verify_webhook(
payload: bytes,
signature: str,
timestamp: str,
secret: str,
tolerance: int = 300 # 5 minutes
) -> bool:
"""Verify Isomera webhook signature."""
# Check timestamp to prevent replay attacks
current_time = int(time.time())
request_time = int(timestamp)
if abs(current_time - request_time) > tolerance:
return False
# Compute expected signature
signed_payload = f"{timestamp}.".encode() + payload
expected = hmac.new(
secret.encode(),
signed_payload,
hashlib.sha256
).hexdigest()
# Compare signatures
return hmac.compare_digest(
f"sha256={expected}",
signature
)
# Flask example
@app.route("/webhooks/isomera", methods=["POST"])
def handle_webhook():
payload = request.get_data()
signature = request.headers.get("X-Isomera-Signature")
timestamp = request.headers.get("X-Isomera-Timestamp")
delivery_id = request.headers.get("X-Isomera-Delivery-Id")
if not verify_webhook(payload, signature, timestamp, WEBHOOK_SECRET):
return "Invalid signature", 401
# Check for duplicate delivery
if is_duplicate(delivery_id):
return "Already processed", 200
event = json.loads(payload)
# Route to handler based on event type
match event["event"]:
case "call.completed":
handle_call_completed(event["data"])
case "adverse_event.reported":
handle_adverse_event(event["data"])
case "pa.required":
handle_pa_required(event["data"])
return "OK", 200const crypto = require('crypto');
function verifyWebhook(payload, signature, timestamp, secret) {
// Check timestamp (5 minute tolerance)
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
return false;
}
// Compute expected signature
const signedPayload = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(`sha256=${expected}`),
Buffer.from(signature)
);
}
// Express example
app.post('/webhooks/isomera', (req, res) => {
const signature = req.headers['x-isomera-signature'];
const timestamp = req.headers['x-isomera-timestamp'];
if (!verifyWebhook(req.rawBody, signature, timestamp, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = req.body;
console.log(`Received ${event.event}`);
// Process event...
res.status(200).send('OK');
});Retry Policy
Failed webhook deliveries are automatically retried with exponential backoff. A delivery is considered failed if your endpoint returns a non-2xx status code or doesn't respond within 30 seconds.
Retry Schedule
| Attempt | Delay After Failure |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 24 hours |
After 5 failed attempts, the webhook delivery is marked as failed and won't be retried automatically. You can manually retry failed deliveries through the API.
View delivery attempts
/webhooks/{webhook_id}/deliveriesRetry a failed delivery
/webhooks/deliveries/{delivery_id}/retry1. RESPOND QUICKLY
Return 2xx status within 30 seconds. Process
events asynchronously in a background queue.
2. HANDLE DUPLICATES
Use X-Isomera-Delivery-Id for idempotency.
Store processed IDs to detect duplicates.
3. VERIFY SIGNATURES
Always validate HMAC signature before processing.
Never trust unverified payloads.
4. CHECK TIMESTAMPS
Reject requests with timestamps > 5 minutes old
to prevent replay attacks.
5. MONITOR FAILURES
Set up alerts for repeated delivery failures.
Review webhook logs regularly.
6. USE HTTPS
Webhook URLs must use HTTPS with TLS 1.2+.
Self-signed certificates are not supported.curl https://api.isomera.ai/v1/webhooks/wh_abc123/deliveries \
-H "Authorization: Bearer sk_live_..." \
-G -d "status=failed" -d "limit=10"{
"data": [
{
"id": "del_x1y2z3a4b5c6d7e8",
"webhook_id": "wh_abc123",
"event_id": "evt_a1b2c3d4e5f6g7h8",
"event_type": "call.completed",
"status": "failed",
"attempts": 5,
"last_attempt_at": "2024-12-19T10:30:00.000Z",
"last_error": "Connection timeout",
"next_retry_at": null
}
],
"has_more": false
}Testing Webhooks
Use the webhook testing endpoint to send sample events to your endpoint during development. Test events are clearly marked with livemode: false.
Send a test event
/webhooks/{webhook_id}/testEvent type to simulate (e.g., "call.completed").
curl -X POST https://api.isomera.ai/v1/webhooks/wh_abc123/test \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"event_type": "call.completed"}'{
"id": "evt_test_1234567890",
"event": "call.completed",
"api_version": "2024-12-01",
"created_at": "2024-12-18T12:00:00.000Z",
"data": {
"object": "voice_call",
"id": 99999,
"call_id": "conv_test_abcdefghij",
"status": "completed",
"patient_id": null,
"duration_seconds": 180,
"call_outcome": "coverage_confirmed"
},
"livemode": false
}