Configure webhooks for asynchronous OpenEvidence operations. Covers DeepConsult lifecycle events, signature verification, event handlers, idempotency, and webhook testing.
| Event Type | Description | Key Payload |
|---|---|---|
deepconsult.started |
Processing began | consultId, estimatedTime |
deepconsult.progress |
Progress update | consultId, progress (0-100) |
deepconsult.completed |
Finished successfully | consultId, report |
deepconsult.failed |
Processing failed | consultId, error, retryable |
rate_limit.warning |
Approaching rate limit | remaining, limit, resetAt |
api_key.expiring |
Key expiration warning | keyId, expiresAt |
Set up Express route at /webhooks/openevidence with signature verification middleware. Parse t=timestamp,v1=signature format from x-openevidence-signature header.
Compute HMAC-SHA256 of ${timestamp}.${payload} with webhook secret. Use timing-safe comparison. Reject timestamps older than 5 minutes (replay protection).
Create handlers for each event type: update database status for DeepConsult lifecycle, send push/email notifications on completion, alert ops on failures and rate limit warnings.
Track processed webhook IDs in database with 24-hour TTL. Skip already-processed events to handle retries safely.
Call client.webhooks.register() on app startup with endpoint URL, event list, and secret.
| Webhook Issue | Detection | Resolution |
|---|---|---|
| Invalid signature | 401 response | Check webhook secret configuration |
| Missing events | No handler called | Verify webhook registration |
| Duplicate processing | Multiple notifications | Enable idempotency tracking |
| Timeout | Webhook fails | Process async, return 200 immediately |
x-openevidence-signature: t=1234567890,v1=abc123def456... # 1234567890 = configured value
set -euo pipefail
# Generate test signature and POST to local endpoint
curl -X POST http://localhost:3000/webhooks/openevidence \ # 3000: 3 seconds in ms
-H "Content-Type: application/json" \
-H "x-openevidence-signature: t=$(date +%s),v1=TEST" \
-d '{"event":"deepconsult.completed","data":{"consultId":"test"}}'
See detailed implementation for advanced patterns.