Phone Numbers

Buy an Indian DID, list your numbers, register a number for outbound calling, and bind inbound calls to a voice agent, a webhook or a WebSocket media stream.

4 min read

Buying a number gives your tenant ownership of it. Registering it on the carrier enables outbound caller-ID, and binding inbound routes incoming calls to your agent. Each step is a separate call, so you can change inbound routing without touching outbound.

All endpoints use the base URL https://voice-api.edesy.in/v1 and a Bearer API key. See Authentication.

Method Path What it does
POST /dids/purchase-live Buy an Indian DID
GET /phone-numbers List your numbers
GET /phone-numbers/:id/telephony Read a number's live wiring state
POST /phone-numbers/:id/carrier Register for outbound calling
DELETE /phone-numbers/:id/carrier Deregister
PUT /phone-numbers/:id/inbound Bind inbound calls
DELETE /phone-numbers/:id/inbound Unbind inbound calls (outbound stays on)

Buy a number#

POST /dids/purchase-live

The easiest way to pick and buy a number is the portal at voice-app.edesy.in. Through the API, send the live_ref of the number you picked from the available-numbers listing. KYC must be complete before a purchase goes through.

Field Type Required Notes
live_ref string Yes Opaque reference for the number you picked
friendly_name string No Your label for the number
months integer No Billing months to prepay
acknowledged_total number No The total you accepted; the purchase fails with QUOTE_STALE if the price changed
curl -X POST https://voice-api.edesy.in/v1/dids/purchase-live \
  -H "Authorization: Bearer vp_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "live_ref": "LIVE_REF_FROM_LISTING", "friendly_name": "Support line" }'

Response 201:

{
  "data": {
    "phone_number_id": "8f1c2e4a-5b6d-4c7e-9f80-1a2b3c4d5e6f",
    "number": "+917969002802",
    "total_charged": 1000,
    "currency": "INR",
    "new_wallet_balance": 4000,
    "next_billing_at": "2026-11-08T00:00:00Z"
  }
}

The amounts above are illustrative. Use phone_number_id as :id in the calls below.

HTTP code When
400 VALIDATION_ERROR Missing or invalid field
402 INSUFFICIENT_BALANCE Wallet balance is too low
404 NOT_FOUND The number is no longer listed
409 NUMBER_NOT_OWNED Someone else just bought this number
409 QUOTE_STALE The price changed since you saw it

List your numbers#

GET /phone-numbers

Query Default Notes
status Filter by status
number_type Filter by type
region Filter by region
page 1
per_page 20
curl https://voice-api.edesy.in/v1/phone-numbers \
  -H "Authorization: Bearer vp_YOUR_API_KEY"

Each number includes id (the UUID used as :id below), number in E.164 (e.g. +919876543210), friendly_name, status, number_type, voice_enabled, sms_enabled, monthly_cost, inbound_target_type, created_at and updated_at. meta carries total, page, per_page and total_pages.

Read the wiring state#

GET /phone-numbers/:id/telephony

curl https://voice-api.edesy.in/v1/phone-numbers/$ID/telephony \
  -H "Authorization: Bearer vp_YOUR_API_KEY"
{
  "data": {
    "phone_number_id": "8f1c2e4a-5b6d-4c7e-9f80-1a2b3c4d5e6f",
    "number": "+917969002802",
    "carrier_registered": true,
    "outbound_capable": true,
    "inbound_bound": true,
    "inbound_target": "voice_agent",
    "agent_id": "a1b2c3d4-0000-4000-8000-000000000001",
    "workspace_id": "w1b2c3d4-0000-4000-8000-000000000002"
  }
}
Field Meaning
carrier_registered The number is registered on the carrier
outbound_capable You can use the number as caller-ID (same as carrier_registered)
inbound_bound Inbound calls are routed somewhere
inbound_target Where inbound goes: voice_agent, webhook or media_stream
agent_id, workspace_id The bound agent, when inbound_target is voice_agent

The register, bind and unbind calls below return this same object.

Register for outbound#

POST /phone-numbers/:id/carrier

No body. The call is idempotent, so it is safe to repeat. After it succeeds, outbound_capable is true and you can use the number as from in Calls.

curl -X POST https://voice-api.edesy.in/v1/phone-numbers/$ID/carrier \
  -H "Authorization: Bearer vp_YOUR_API_KEY"

DELETE /phone-numbers/:id/carrier deregisters the number and returns {"data": {"status": "deregistered"}}. If inbound is still bound, pass ?force=true or unbind first.

Bind inbound calls#

PUT /phone-numbers/:id/inbound

Choose one target. Binding also registers the number on the carrier if it isn't registered yet.

target Required fields Optional fields
voice_agent agent_id, workspace_id
webhook webhook_url webhook_method (default POST), status_webhook_url
media_stream stream_url (must start with ws:// or wss://) sample_rate (8000 or 16000)

Pass "force": true to take over a number whose inbound is bound elsewhere.

# Stream inbound audio to your own WebSocket agent
curl -X PUT https://voice-api.edesy.in/v1/phone-numbers/$ID/inbound \
  -H "Authorization: Bearer vp_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "target": "media_stream", "stream_url": "wss://agent.example.com/ws", "sample_rate": 8000 }'

DELETE /phone-numbers/:id/inbound removes the binding. Outbound calling keeps working.

Errors#

The wiring endpoints (/telephony, /carrier, /inbound) use lowercase codes:

HTTP code When
400 validation :id is not a valid UUID, or the body is invalid
400 bind_failed The inbound binding was rejected; check the target fields
404 not_found The number doesn't exist or isn't yours
409 inbound_conflict Inbound is bound elsewhere; retry with "force": true
409 inbound_bound Inbound is still bound; unbind first or pass force=true
503 not_configured Telephony provisioning is temporarily unavailable

See Error Codes for the error format.

Was this page helpful? Suggest an edit