REST API

Number Masking API for India

Click-to-call bridge in 3 lines of code. Dial Party A, bridge Party B, both see the masked number. Webhooks for every call event. Rs 1.50/min, INR, GST invoice.

voice-api.edesy.in/v1

Base URL

6

Endpoints

8

Webhook Events

<100ms

Connect Latency

HTTPS + Bearer Auth

HMAC Signed Webhooks

INR + GST Invoice

99.9% Uptime

Quick Start

Make your first masked call

Sign up at masking.edesy.in, grab an API key, fund your wallet (minimum Rs 10), then run this:

cURL
# Initiate a masked call
# We dial Party A first, bridge Party B on answer.

curl -X POST https://voice-api.edesy.in/v1/masking/calls \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "party_a": "9876543210",
    "party_b": "9123456789"
  }'
Node.js
// Node.js (using fetch, native in Node 18+)

const response = await fetch("https://voice-api.edesy.in/v1/masking/calls", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.EDESY_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    party_a: "9876543210",
    party_b: "9123456789",
  }),
});
const { data } = await response.json();
console.log(data.call_sid, data.masked_number);
Python
# Python (using requests)

import os, requests

response = requests.post(
    "https://voice-api.edesy.in/v1/masking/calls",
    headers={
        "Authorization": f"Bearer {os.environ['EDESY_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={"party_a": "9876543210", "party_b": "9123456789"},
)
data = response.json()["data"]
print(data["call_sid"], data["masked_number"])

We dial Party A first. When they answer, we bridge Party B. Both see the masked number as caller ID. The response includes a call_sid you can use to poll status or fetch the recording. Read the full developer documentation for every endpoint, webhook, and error code.

Endpoints

REST API Reference

Six endpoints cover the full call lifecycle. All requests authenticate with a Bearer token from your API key.

POST
Initiate a Call
/masking/calls

Dials Party A first, bridges Party B on answer. Both parties see the masked number as caller ID. Returns immediately with a call_sid.

GET
Get Call Status
/masking/calls/:call_sid

Returns current call state. Poll every 2-3 seconds until terminal (completed, failed, no-answer).

GET
Get Call Recording
/masking/calls/:call_sid/recording

Returns a short-lived signed URL streaming the MP3 recording. Valid for 1 hour.

GET
List Call Logs
/masking/call-logs

Paginated call history with filters for status and date range. Up to 100 per page.

GET
Get Usage Stats
/masking/stats

Aggregate usage statistics for a date range. Defaults to last 30 days.

GET
Get Wallet Balance
/masking/billing

Current wallet balance. Useful for checking funds before initiating a call programmatically.

Webhooks

Webhooks for every call event

Subscribe to call lifecycle events instead of polling. We POST signed JSON to your endpoint at each state transition. Failed deliveries retry with exponential backoff. Every payload includes an HMAC SHA-256 signature for verification.

call.initiated

Party A is being dialed. Call has not yet connected.

call.ringing

Party A's phone is ringing.

call.answered

Party A answered. We are now dialing Party B to bridge.

call.bridged

Both parties are connected and talking.

call.completed

Call ended normally. Includes duration and recording URL.

call.failed

Call failed. Includes hangup_cause for diagnostics.

call.no-answer

Party A did not answer within the timeout window.

recording.ready

Recording is processed and available for download.

Sample webhook payload (call.completed)

JSON
{
  "event": "call.completed",
  "call_sid": "d28be7cd-9840-4b23-9627-199f1ef81fc6",
  "data": {
    "caller_number": "917969002802",
    "target_number": "9876543210",
    "masked_number": "917969002802",
    "direction": "click_to_call",
    "status": "completed",
    "started_at": "2026-05-11T08:30:00Z",
    "answered_at": "2026-05-11T08:30:08Z",
    "ended_at": "2026-05-11T08:31:42Z",
    "duration_sec": 94,
    "bill_duration_sec": 120,
    "price": 3,
    "recording_url": "recordings/2026/05/11/d28be7cd.wav"
  }
}
Authentication

Bearer token over HTTPS

Every request must include an Authorization: Bearer YOUR_API_KEY header. Create and rotate keys on the API Keys page of the portal.

Per-org API keys

Multiple keys per organization. Rotate without downtime.

HMAC-signed webhooks

Verify the X-Edesy-Signature header before processing.

100 req/min default

Rate limit. Higher limits on Enterprise.

Errors

Error codes

401 - unauthorized
Missing or invalid API key. Check the Authorization header.
400 - invalid_numbers
party_a and party_b are the same number, or not valid 10-digit Indian mobiles.
402 - insufficient_balance
Wallet balance is below the minimum for a 1-minute call. Top up at the portal wallet page.
404 - not_found
call_sid does not exist or recording is not yet available.
429 - rate_limited
API rate limit exceeded. Default limit is 100 requests/minute per API key.
Pricing

Rs 1.50 per masked minute

No monthly fees. No setup fees. No minimum commitment. Prepaid wallet, INR billing, 18% GST, GSTIN supported for Input Tax Credit. Top up via Razorpay.

Need help with integration?
We'll integrate this into your stack
If you'd rather have us wire the API into your CRM, build the webhook handler, and set up compliance — from Rs 14,999, 5-day go-live.

API FAQ

Start Building

Sign up, fund a wallet, ship masked calls today. No sales call required.