A click-to-call API connects two phones on request. With Edesy you send one POST /v1/click-to-call with the agent's and the customer's numbers. Edesy rings the agent first, and when they answer it dials the customer from your Indian business number and bridges the call. The call is recorded by default, and every stage comes back to you as a signed webhook. It costs Rs 1.50 per minute.
Here's the whole integration, from API key to a call result in your CRM.
What you need
| Item | Where |
|---|---|
| An Edesy account | voice-app.edesy.in. New accounts get Rs 20 welcome credit |
| An Indian number | Buy one in the voice app after KYC. It is the caller ID |
An API key with calls:write |
Voice app, API keys. See API keys |
| An HTTPS endpoint | To receive Call Events |
Minute 0-3: place your first call
curl -X POST https://voice-api.edesy.in/v1/click-to-call \
-H "Authorization: Bearer $EDESY_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent": { "phone": "+9198XXXXXXXX" },
"customer": { "phone": "+9197XXXXXXXX" },
"correlation": { "lead_id": "L-1042", "crm_call_id": "call_8812" }
}'Your phone (the agent) rings. Answer it, and the customer's phone rings. The API returned straight away:
{ "data": { "call_id": "...", "status": "initiated" } }The request fields:
| Field | Required | Notes |
|---|---|---|
agent.phone |
yes | Rung first |
customer.phone |
yes | Dialled after the agent answers; must differ from the agent |
caller_id |
no | An active number of your workspace; default is your first active number |
record |
no | Default true |
max_duration_sec |
no | 10 to 7,200, default 3,600 |
correlation |
no | Up to 10 of your own keys, echoed on every event |
Full reference: click-to-call docs.
Minute 3-6: register a Call Events endpoint
curl -X POST https://voice-api.edesy.in/v1/webhooks/subscriptions \
-H "Authorization: Bearer $EDESY_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/edesy/events",
"format": "edesy.call.v1"
}'No event_types means all eight events. The response includes a whsec_ signing secret, shown only this once. Store it as EDESY_WHSEC.
Minute 6-12: receive and verify events
Every delivery carries Standard Webhooks headers: webhook-id, webhook-timestamp and webhook-signature. The signature is v1, plus a base64 HMAC-SHA256 of id.timestamp.body, keyed with the base64-decoded part of your secret after whsec_.
import base64, hashlib, hmac, json, os, time
from flask import Flask, request
app = Flask(__name__)
SECRET = os.environ["EDESY_WHSEC"]
KEY = base64.b64decode(SECRET[len("whsec_"):])
def verified(req) -> bool:
msg_id = req.headers.get("webhook-id", "")
ts = req.headers.get("webhook-timestamp", "")
if not ts.isdigit() or abs(time.time() - int(ts)) > 300:
return False
signed = f"{msg_id}.{ts}.".encode() + req.get_data()
want = base64.b64encode(hmac.new(KEY, signed, hashlib.sha256).digest()).decode()
return any(s.startswith("v1,") and hmac.compare_digest(s[3:], want)
for s in req.headers.get("webhook-signature", "").split())
@app.post("/edesy/events")
def events():
if not verified(request):
return "", 401
ev = json.loads(request.get_data())
save_event(ev) # upsert by ev["call"]["id"]; skip a webhook-id you've seen
return "", 204Three rules make the handler robust:
- Upsert by
call.id. Each event carries a full snapshot of the call, so arrival order doesn't matter. Keep the furtheststatusyou've seen. - De-duplicate on
webhook-id. Retries reuse it. - Answer within 5 seconds. Queue slow work. Failed deliveries are retried; see retries and replay.
Minute 12-15: write the result back
The events you'll use most:
| Event | Write to your CRM |
|---|---|
call.completed |
call.outcome (answered, no_answer, busy, failed, canceled, voicemail), duration_sec, talk_sec |
recording.ready |
recording.url |
call.analyzed |
analysis.summary, analysis.action_items, suggested analysis.disposition |
Your correlation.lead_id comes back on each one, so you know which record to update. For what to do with the summary, see automatic call summaries in your CRM. The event schema is in the Call Events v1 docs.
Add the button
In your app or CRM, the button calls your backend, never Edesy directly, so the API key stays on the server:
// Browser
await fetch('/api/call', { method: 'POST', body: JSON.stringify({ leadId }) });
// Server (Node)
app.post('/api/call', async (req, res) => {
const lead = await db.leads.find(req.body.leadId);
const agent = await db.users.find(req.user.id);
const r = await fetch('https://voice-api.edesy.in/v1/click-to-call', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.EDESY_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
agent: { phone: agent.phone },
customer: { phone: lead.phone },
correlation: { lead_id: lead.id },
}),
});
res.status(r.status).json(await r.json());
});To end a call from your UI, POST /v1/click-to-call/{call_id}/hangup.
Errors to handle
| Status | Code | What to show the user |
|---|---|---|
| 400 | validation_error |
Which field is wrong (details.field), for example a non-Indian number |
| 402 | insufficient_balance |
Ask an admin to top up the wallet |
| 409 | NO_CALLER_ID |
The workspace needs an active number |
| 429 | A call limit was reached; try again shortly |
Using LeadSquared or Zoho CRM?
You don't need to write code. Edesy's LeadSquared and Zoho CRM connectors use this same API and write calls into the CRM for you. HubSpot, Salesforce, Freshworks, Bitrix24 and Pipedrive have no connector yet, so this guide is the way to go for them.
Only need a "Call Now" link on your website? The free click-to-call button generator makes one without an API.
Pricing
Rs 1.50 per minute, 60-second pulse, plus number rental. No separate fee for Call Events, transcripts or summaries. Prices exclude 18% GST. See the click-to-call API page.
FAQ
What does the click-to-call API do?
POST /v1/click-to-call rings the agent's phone first, showing your Edesy number. When the agent answers, Edesy dials the customer and bridges the two. The call is recorded by default and reported through Call Events.
Which numbers does it support?
Indian numbers only, for both the agent and the customer. The caller ID must be an active number in your workspace.
How do I get the call result?
Register a webhook subscription with format edesy.call.v1. You receive call.initiated, call.ringing, call.answered and call.completed, then recording.ready, transcript.ready and call.analyzed for recorded calls.
How much does it cost?
Rs 1.50 per minute with a 60-second pulse, plus number rental, excluding 18% GST. Call Events, transcripts and summaries have no separate fee.
Can I use it with HubSpot or Salesforce?
Yes, from your own backend. There is no native HubSpot or Salesforce connector, but a CRM button or workflow can call your server, which calls the API and writes the Call Events back.