Webhook Testing
Use a receiver that can deliberately return 2xx, 400, 408, 429, 500, and a timeout. Verify the canonical headers, raw-body signature, retry classification, stable event ID, changing attempt ID, and idempotent processing.
Test matrix
| Receiver result | Expected delivery behavior |
|---|---|
200–299 | Delivered successfully. |
400 | Final failure; no automatic retry. |
408, 425, or 429 | Automatic retry scheduled. |
500–599 | Automatic retry scheduled. |
| Network error or timeout | Automatic retry scheduled. |
The maximum attempt count is account-configured. Do not write tests that assume the platform fallback is the account's permanent contract.
Manual checklist
- Configure a test Application with a destination and webhook signing secret.
- Trigger a real
call.createdorcall.completedevent. - Capture the exact raw body and canonical
X-MiniVoice-*headers. - Verify the signature before parsing JSON.
- Confirm the body has
id,type,created_at,sequence, anddata. - Confirm
X-MiniVoice-Event-IDequalsidandX-MiniVoice-Event-Typeequalstype. - Return
500once and confirm the next attempt keepsidandsequencebut has a newX-MiniVoice-Attempt-ID. - Confirm your handler produces a business side effect only once.
- Allow automatic attempts to end, then test manual resend when the Inspector reports
can_resend.
Keep the MiniVoice call ID, event ID, attempt IDs, request bodies, and receiver responses with the test result. These values make delivery failures reproducible.
Lifecycle checks
- Ringing: Place a call through a route that returns authoritative remote ringing progress and check for
call.ringing. - Early media: Use a route or test destination that produces pre-answer media and check for
call.early_media. - Cancellation: Create an outbound call and cancel it before answer. Check for
call.created→call.canceledandreason: api_canceled.
Carrier and network signaling varies. Do not require every call to emit call.ringing, call.early_media, or both. For every received event, apply the same delivery checks above: retries retain the event id, sequence, and payload, while each delivery attempt has a distinct attempt ID.
See Inspector and Troubleshooting for the receiver example and Retries and Idempotency for the exact retry policy.