Skip to main content

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 resultExpected delivery behavior
200–299Delivered successfully.
400Final failure; no automatic retry.
408, 425, or 429Automatic retry scheduled.
500–599Automatic retry scheduled.
Network error or timeoutAutomatic 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​

  1. Configure a test Application with a destination and webhook signing secret.
  2. Trigger a real call.created or call.completed event.
  3. Capture the exact raw body and canonical X-MiniVoice-* headers.
  4. Verify the signature before parsing JSON.
  5. Confirm the body has id, type, created_at, sequence, and data.
  6. Confirm X-MiniVoice-Event-ID equals id and X-MiniVoice-Event-Type equals type.
  7. Return 500 once and confirm the next attempt keeps id and sequence but has a new X-MiniVoice-Attempt-ID.
  8. Confirm your handler produces a business side effect only once.
  9. 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.canceled and reason: 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.