Dynamic Routing Webhook

Let your own server decide, per call, who a masked number connects to. When a call arrives we POST a signed request to your endpoint and bridge to the number it returns.

Dynamic Routing Webhook

Dynamic routing lets your server decide, in real time, who a masked number connects to on each call — instead of pre-registering a fixed mapping. Point a number at your HTTPS endpoint; when a call arrives, we ask your endpoint for the target and bridge the call to whatever it returns (or reject it).

This is ideal for round-robin agent assignment, time- or location-based routing, and blocking abusive callers — any decision your own systems already know how to make.

Enabling it

Set a number's routing mode to webhook and give it your endpoint. See Enroll Your Own Numbers for the PATCH /masking/numbers/:id/routing call, including webhook_url, webhook_method, webhook_secret, and fallback_internal.

The request we send

When a call reaches a webhook-mode number, we POST a JSON body to your endpoint:

{
  "event": "masking.route",
  "call_sid": "d28be7cd-9840-4b23-9627-199f1ef81fc6",
  "caller": "9876543210",
  "masked_number": "918071387146",
  "direction": "inbound",
  "timestamp": "2026-07-14T18:00:00Z"
}
Field Description
event Always masking.route.
call_sid The call's id (empty for test events).
caller The normalized number of the person calling.
masked_number The virtual number that was dialed.
direction inbound.
timestamp RFC 3339 UTC.

Signature verification

If you set a webhook_secret, we sign the raw request body with HMAC-SHA256 and send it in the X-Edesy-Signature header, formatted as sha256=<hex>. Recompute the HMAC over the exact body you received and compare to confirm the request genuinely came from us.

Limits

  • Timeout: your endpoint must respond within 5 seconds — the caller is on the line while you decide.
  • Response size: we read at most 8 KB of your response.
  • Method: POST (default) or GET.

The response we expect

Reply with JSON telling us what to do:

{
  "action": "connect",
  "target_number": "9123456789",
  "caller_id": "918071387146"
}
Field Type Description
action string connect (default when a target_number is present) or reject.
target_number string Number to bridge to. Required when action is connect.
caller_id string Optional caller-ID override. Defaults to the masked number.
reject_reason string Optional message played to the caller when action is reject.

To turn a call away, return:

{ "action": "reject", "reject_reason": "This line is closed. Please try again during business hours." }

Fallback

If your endpoint errors, times out, or returns a non-2xx status, behaviour depends on the number's fallback_internal setting: when enabled, we fall back to the number's internal mapping; when disabled, the call is rejected. Set this based on how critical it is that calls always connect.

Testing

Use POST /masking/numbers/:id/routing/test to send a synthetic masking.route event to your endpoint and see the HTTP status, latency, raw body, and the decision we parsed — so you can validate your integration before going live.