# Inbound Routing

> Decide where calls to an Edesy number go - 11 targets including a VoiceML webhook, SIP URI, media stream, Conversation Relay bot, AI voice agent, SIP trunk, IVR flow and missed call. PUT /v1/phone-numbers/{id}/inbound and PATCH routing.

Source: https://edesy.in/docs/voice-api/phone-numbers/inbound-routing

Each number has one inbound target: the place every incoming call goes. There are 11 kinds of target, from a webhook that returns VoiceML to a SIP URI, a raw-audio media stream, a Conversation Relay bot or an IVR Studio flow. Set it once in the portal under **When a call comes in**, or with the API, and change it whenever you like.

## The 11 targets

| # | Target | What happens | Set with |
|---|---|---|---|
| 1 | Webhook URL | Edesy fetches your URL, which returns [VoiceML](https://edesy.in/docs/voice-api/calls/voiceml) | `PUT .../inbound` `webhook`, or routing `webhook` |
| 2 | SIP URI or saved SIP destination | The call is sent to your SIP endpoint: LiveKit Cloud, Vapi, Bolna, Retell or any SIP server | routing `sip` |
| 3 | Media stream | Raw L16 audio to your WebSocket bot. See [Media Streams](https://edesy.in/docs/voice-api/streaming/media-streams) | `PUT .../inbound` `media_stream`, or routing `stream` |
| 4 | Conversation Relay | Text in, text out bot. See [Conversation Relay](https://edesy.in/docs/voice-api/streaming/conversation-relay) | `PUT .../inbound` `conversation_relay`, or routing `relay` |
| 5 | Edesy AI voice agent | An agent built at [voice-agent.edesy.in](https://voice-agent.edesy.in) answers | `PUT .../inbound` `voice_agent`, or routing `voiceagent` |
| 6 | Your SIP trunk | The call goes to one of your Edesy SIP trunks and on to its configured destination | routing `trunk` |
| 7 | Browser / app softphones | Rings your team's registered browser softphones | Portal |
| 8 | IVR Studio flow | Runs a published flow built at [ivr.edesy.in](https://ivr.edesy.in) | routing `ivr` |
| 9 | Application preset | Follows a saved [application](https://edesy.in/docs/voice-api/calls/applications) | routing `application` |
| 10 | Missed call | The call is rejected before it is answered (the caller pays nothing) and a `call.missed` webhook is sent | routing `missed_call` |
| 11 | Call forwarding rule | Rings your team's phones with a forwarding rule from [forwarding.edesy.in](https://forwarding.edesy.in) | Forwarding portal |

Callers on your [blocklist](https://edesy.in/docs/voice-api/account/blocklist-and-dnc) are refused before any target runs.

## Quick binding: `PUT /v1/phone-numbers/{id}/inbound`

`PUT https://voice-api.edesy.in/v1/phone-numbers/{id}/inbound`

One call that sets up the number for a webhook, media stream, relay bot or AI voice agent, including making it ready for outbound use.

```bash
curl -X PUT https://voice-api.edesy.in/v1/phone-numbers/{id}/inbound \
  -H "Authorization: Bearer $EDESY_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "target": "webhook", "webhook_url": "https://example.com/edesy/inbound", "webhook_method": "POST" }'
```

| Field | Required | Description |
|---|---|---|
| `target` | yes | `webhook`, `media_stream`, `conversation_relay` or `voice_agent` |
| `webhook_url`, `webhook_method` | for `webhook` | URL returning VoiceML; `POST` (default) or `GET` |
| `stream_url`, `sample_rate` | for `media_stream` | `ws://` or `wss://` URL; `16000` (default) or `8000` |
| `relay_url`, `relay_settings` | for `conversation_relay` | `ws://` or `wss://` URL; optional settings object |
| `agent_id`, `workspace_id` | for `voice_agent` | Identify the agent on the Edesy AI voice agent platform |
| `force` | no | Take over a number currently bound to something set up outside these APIs |

`DELETE /v1/phone-numbers/{id}/inbound` detaches the inbound target. Outbound calls from the number keep working.

If the number is already bound to an unrelated setup, `PUT` returns `409 inbound_conflict` unless you pass `"force": true`.

## Full routing: `PATCH /v1/phone-numbers/{id}/routing`

Covers every API-settable target. Send `inbound_target_type` plus the field that target needs.

```bash
curl -X PATCH https://voice-api.edesy.in/v1/phone-numbers/{id}/routing \
  -H "Authorization: Bearer $EDESY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inbound_target_type": "sip",
    "inbound_sip_uri": "sip:agent@sip.example.com",
    "inbound_sip_username": "edesy",
    "inbound_sip_password": "your-password",
    "inbound_sip_headers": { "X-Agent": "support" }
  }'
```

| `inbound_target_type` | Required fields | Optional fields |
|---|---|---|
| `webhook` | `inbound_webhook_url` | `inbound_webhook_method` |
| `sip` | `inbound_sip_uri` | `inbound_sip_username`, `inbound_sip_password` (digest auth), `inbound_sip_headers` (replaces the whole set; `{}` clears it) |
| `stream` | `inbound_stream_url` | `inbound_stream_sample_rate` (`8000` or `16000`) |
| `relay` | `inbound_relay_url` | `inbound_relay_config` (replaced as a whole) |
| `trunk` | `inbound_trunk_id` | |
| `voiceagent` | | |
| `ivr` | `ivr_flow_id` | |
| `application` | `application_id` | |
| `missed_call` | | |

The same request also accepts `outbound_caller_id`, `friendly_name` and `recording_enabled`. A missing required field returns `400 VALIDATION_ERROR`.

Saved SIP destinations (an address book of SIP endpoints with a test-call button) live under `/v1/sip-destinations`. For trunks and agent platforms, see [SIP for AI voice agents](https://edesy.in/docs/sip-trunk-for-ai-voice-agents) and [SIP trunking](https://edesy.in/sip-trunking).

## What your webhook receives

For the webhook target, Edesy sends a form-encoded request to your URL when a call arrives, with `CallSid`, `AccountSid`, `From` (the caller), `To` (your number), `CallStatus` and `Direction` set to `inbound`. Reply with VoiceML. To refuse a call without answering, return [`reject`](https://edesy.in/docs/voice-api/calls/voiceml#reject) as the first instruction.

```json
{
  "instructions": [
    { "verb": "say", "text": "Thanks for calling Acme. Connecting you now." },
    { "verb": "dial", "number": "+9198XXXXXXXX", "timeout": 25 },
    { "verb": "say", "text": "Sorry, nobody is available. Please call again later." }
  ]
}
```

## Check the result

- `GET /v1/phone-numbers/{id}/telephony` reports `outbound_capable` and `inbound_bound`.
- Place a test call to the number and read `GET /v1/calls?to=+9180XXXXXXXX`.
- See [Number Health and Webhook Logs](https://edesy.in/docs/voice-api/phone-numbers/number-health-and-webhook-logs).
