When a customer presses 1 or 2, Edesy sends an action request to your store so the order is updated during the call. For a webhook-connected store, every action is a signed JSON POST to the action URL you set on the store. Your endpoint applies the change and answers with a 2xx status.
The requests#
All four actions go to your action URL as POST with a JSON body.
| Action | When | Body |
|---|---|---|
confirm |
Customer pressed 1 | {"action": "confirm", "order_id", "order_name", "call_sid", "at"} |
cancel |
Customer pressed 2 | {"action": "cancel", "order_id", "order_name", "call_sid", "at"} |
tag |
An outcome action adds a tag | {"action": "tag", "order_id", "tag", "note", "call_sid"} |
note |
An outcome action adds a note | {"action": "note", "order_id", "note", "call_sid"} |
Example confirm:
{
"action": "confirm",
"order_id": "10452",
"order_name": "#10452",
"call_sid": "c4b1e2d0-...",
"at": "2026-10-12T06:12:05Z"
}
order_id is the order.id you sent in the webhook. at is when the customer answered, in UTC (RFC 3339).
No action URL? Leave it empty. Outcomes are still recorded on the Orders screen and you can act on them there.
Verify the request#
Each request carries:
| Header | Value |
|---|---|
X-Edesy-Timestamp |
Unix seconds when we sent it |
X-Edesy-Signature |
sha256= + hex HMAC-SHA256 of timestamp + "." + raw_body |
X-Edesy-Call-Sid |
The call that produced the action |
X-Edesy-Idempotency-Key |
The same value on every retry of this request |
The signature uses your account's IVR signing secret, not the store's webhook secret. Find it, and rotate it, in IVR Studio at ivr.edesy.in under API Keys > Signing secret.
import crypto from "node:crypto";
function verify(rawBody, ts, signature, secret) {
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex");
return signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Success, failure and retries#
- Any
2xxresponse is success. During the call, the customer then hears the "confirmed" or "cancelled" line. - If your endpoint times out or answers
5xxor429, the customer hears a reassuring line ("we'll update your order shortly"), the order settles asaction_failed, and the same request is retried in the background. - An error body in the form
{"error": {"code": "...", "message": "..."}}is shown in the order's timeline. - Make your handler safe to run twice. Use
X-Edesy-Idempotency-Keyto ignore a request you have already applied.
The action request has a short time budget during the call, so answer quickly and do slow work afterwards.
Placeholders#
Action request templates, outcome tags and notes can use these tokens. They are filled in per order and call.
| Placeholder | What it is | Example |
|---|---|---|
{order.external_id} |
Your order id | 10452 |
{order.name} |
Order name the customer sees | #10452 |
{order.number} |
Order number | 10452 |
{order.phone} |
Customer phone, E.164 | +919800000000 |
{order.customer_name} |
Customer name | Priya Sharma |
{order.cod_amount} |
Cash to collect, in rupees | 1110 |
{order.cod_amount_paise} |
Cash to collect, in paise | 111000 |
{order.total} |
Order total, in rupees | 1310 |
{call.sid} |
Our call id | c4b1e2d0-... |
{call.time} |
When the customer answered, IST, readable | 12 Oct 2026 11:42 AM IST |
{call.time_iso} |
When the customer answered, UTC, RFC 3339 | 2026-10-12T06:12:05Z |
{job.attempts} |
Call attempt number | 2 |
{labels.confirmed}, {labels.cancelled}, {labels.pending}, {labels.unreachable}, {labels.no_input} |
The call rule's label for that outcome | unreachable |
{action.tag} |
The tag set on the outcome action | unreachable |
{action.note} |
The note set on the outcome action | Not reachable after 3 calls |
Labels are set per call rule; see Outcome Actions.
Editing a request#
Some store integrations let you change the request behind each action (method, path, JSON body and extra headers) on a Requests tab, with a placeholder picker, a live preview and a test send. See Test and Revisions. For a webhook-connected store the requests are the fixed JSON bodies above, sent to your action URL; shape them however you like on your side.