Action Requests and Placeholders

The REST requests COD Order Calls sends back to your store - confirm, cancel, tag and note POSTed to your action URL - how they are signed, retried and made idempotent, and the placeholders available in them.

3 min read

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 2xx response is success. During the call, the customer then hears the "confirmed" or "cancelled" line.
  • If your endpoint times out or answers 5xx or 429, the customer hears a reassuring line ("we'll update your order shortly"), the order settles as action_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-Key to 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.

Was this page helpful?