Developer documentation

Confirm orders on WhatsApp from any platform

Your store is not on Shopify, Salla, Zid, WooCommerce or Easy Orders? Send its orders to Yudoo's REST API. Yudoo sends the WhatsApp confirmation and every automation you turned on, then tells your platform the moment the customer confirms or cancels.

Base URLhttps://app.yudoo.io/api/v1

Everything you need to build a WhatsApp confirmation flow

Quick start

Your first WhatsApp confirmation in minutes.

  1. 1

    Connect your store: Yudoo app → Settings → Store platforms → Other platform (API). Copy the API key (it starts with wp_live_).

  2. 2

    Send an order: POST /orders with the customer's phone and the order details.

  3. 3

    Listen for the answer: add your webhook URL and react to order.confirmed and order.cancelled.

POST /orders
curl -X POST https://app.yudoo.io/api/v1/orders \
  -H "Authorization: Bearer wp_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "1042",
    "number": "1042",
    "status": "pending",
    "customer": { "name": "Mohamed Ali", "phone": "201001234567", "email": "[email protected]" },
    "currency": "EGP",
    "total": 540,
    "shipping_total": 40,
    "payment_method": "cod",
    "shipping_address": { "address1": "12 Tahrir St", "city": "Cairo", "province": "Cairo", "country": "EG" },
    "items": [
      { "name": "Cotton T-shirt", "variant": "White / L", "quantity": 1, "price": 250, "sku": "TS-WH-L" },
      { "name": "Sport socks", "quantity": 2, "price": 125 }
    ],
    "note": "Please deliver after 5 pm"
  }'

01How it works

  1. 1

    Your platform sends each new order to POST /orders.

  2. 2

    Yudoo sends the WhatsApp confirmation from your own WhatsApp Business number, with Confirm and Cancel buttons, plus every automation you turned on for the store (follow-up, shipping update, review request…).

  3. 3

    The customer taps Confirm or Cancel. Yudoo answers them on WhatsApp and sends order.confirmed or order.cancelled to your webhook URL.

  4. 4

    Your platform moves the order (ship it, or cancel it). When it ships, PATCH the order with the tracking number and the Shipping update goes out.

The messages, buttons and timings are the store's automations in the Yudoo app, exactly as for a Shopify or Salla store: the API only brings the orders in and the answers out.

02Getting started

  1. 1

    Sign in to the Yudoo app (app.yudoo.io) and connect your WhatsApp Business number.

  2. 2

    Settings → Store platforms → Other platform (API): enter the store's name and URL, then Connect.

  3. 3

    Copy the API key and the signing secret. The API key is shown only this once; keep both on your server, never in a browser or an app.

  4. 4

    Turn on the automations you want (Cash on Delivery Confirmation first) under Automations.

  5. 5

    Add your webhook URL on the store's card, and press Send a test event.

GET/api/v1/me

Check that your key works: it answers with the store it belongs to.

bash
curl https://app.yudoo.io/api/v1/me -H "Authorization: Bearer wp_live_your_api_key"

{
  "store": { "name": "My Store", "url": "https://mystore.com", "connected_at": "2026-11-04T09:00:00.000Z" },
  "webhook": { "url": "https://mystore.com/yudoo/webhook", "events": ["order.confirmed", "order.cancelled"] }
}

03Authentication

Every request carries the store's API key in the Authorization header. X-API-Key: wp_live_… is accepted too.

http
Authorization: Bearer wp_live_your_api_key
  • One key per store. Make a new key from the store's card any time: the old one stops working at once.
  • Requests go over HTTPS only. Answers are JSON.
  • At most 120 requests a minute per store (429 rate_limited after that).

Never put the key in a storefront, a mobile app or client-side JavaScript: anyone who has it can send orders as your store.

04Create an order

POST/api/v1/orders

Send each order as soon as it is placed. Yudoo stores it and fires the store's order automations: the confirmation for its payment method first. Sending the same id again updates the order instead of creating a second one, so a retry is always safe.

curl -X POST https://app.yudoo.io/api/v1/orders \
  -H "Authorization: Bearer wp_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "1042",
    "number": "1042",
    "status": "pending",
    "customer": { "name": "Mohamed Ali", "phone": "201001234567", "email": "[email protected]" },
    "currency": "EGP",
    "total": 540,
    "shipping_total": 40,
    "payment_method": "cod",
    "shipping_address": { "address1": "12 Tahrir St", "city": "Cairo", "province": "Cairo", "country": "EG" },
    "items": [
      { "name": "Cotton T-shirt", "variant": "White / L", "quantity": 1, "price": 250, "sku": "TS-WH-L" },
      { "name": "Sport socks", "quantity": 2, "price": 125 }
    ],
    "note": "Please deliver after 5 pm"
  }'
FieldTypeDescription
idstringRequired. Your platform's own order id. Sending the same id again updates that order.
customer.phonestringRequired. International format with the country code, digits only (201001234567). The WhatsApp messages go here.
customer.namestringThe customer's name ({{customer_first_name}} in the messages).
customer.emailstringOptional.
numberstringThe number the customer knows ("1042" → #1042). Defaults to #id.
statusstringpending (default), processing, shipped, delivered, cancelled… See Order statuses.
payment_methodstringcod, bank_transfer, instapay, vodafone_cash, card… It picks the confirmation the order gets. See Payment methods.
currencystring3-letter ISO code: EGP, SAR, AED, USD…
totalnumberThe order's total, shipping included.
shipping_totalnumberThe shipping fee.
items[]arrayUp to 200 lines: name (required), quantity, price, variant, sku, product_id, image_url (https).
shipping_addressobjectaddress1, address2, city, province, postal_code, country (ISO-2 or name).
notestringThe customer's order note.
trackingobjectnumber, url, carrier — when the order already shipped.
placed_atstringISO 8601 date. Defaults to the moment Yudoo received the order.

The answer is 201 Created with the order (200 when the id was sent before):

json
{
  "order": {
    "id": "1042",
    "number": "#1042",
    "status": "pending",
    "confirmation": { "status": "pending", "at": null, "cancel_reason": null },
    "customer": { "name": "Mohamed Ali", "phone": "201001234567", "email": "[email protected]" },
    "currency": "EGP",
    "total": 540,
    "payment_method": "cod",
    "items": [
      { "name": "Cotton T-shirt", "variant": "White / L", "quantity": 1, "price": 250, "sku": "TS-WH-L", "product_id": null },
      { "name": "Sport socks", "variant": null, "quantity": 2, "price": 125, "sku": null, "product_id": null }
    ],
    "tracking": null,
    "created_at": "2026-11-04T10:00:00.000Z",
    "updated_at": "2026-11-04T10:00:00.000Z"
  }
}

05Get an order

GET/api/v1/orders/{id}

Read an order by your own id, with the customer's answer in confirmation.

bash
curl https://app.yudoo.io/api/v1/orders/1042 \
  -H "Authorization: Bearer wp_live_your_api_key"
confirmation.statusMeaning
pendingNo answer yet.
confirmedThe customer (or you, in Yudoo) confirmed the order.
cancelledThe customer (or you) cancelled it; cancel_reason holds the reason they chose, when they gave one.

06Update an order

PATCH/api/v1/orders/{id}

Send only what changed: the status, the tracking, the items or the total. A new status fires the matching automation (Shipping update, Ready for pickup, Order cancelled…); a tracking number gives the Shipping update its link.

bash
curl -X PATCH https://app.yudoo.io/api/v1/orders/1042 \
  -H "Authorization: Bearer wp_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "shipped",
    "tracking": { "number": "7845120963", "url": "https://track.example.com/7845120963", "carrier": "Bosta" }
  }'

07Order statuses and payment methods

Send your platform's own words: Yudoo reads them in English and Arabic. These are the ones it understands:

StatusExamples Yudoo understands
Newpending, new, placed, awaiting, unpaid
Processingprocessing, confirmed, preparing, packed, on hold
Ready for pickupready for pickup, ready to collect
Shippedshipped, out for delivery, in transit, dispatched
Delivereddelivered, completed, fulfilled
Failed deliveryfailed delivery, undelivered, customer unreachable
Cancelledcancelled, canceled, rejected, refunded
Returnedreturned, rto
payment_methodConfirmation sent
cod, cash on deliveryCash on Delivery Confirmation (Confirm / Cancel)
bank_transfer, instapay, depositBank Deposit Confirmation (send the payment proof)
vodafone_cash, wallet, manual, pay laterManual Payment Confirmation
card, paid, anything elsePaid Order Confirmation

08Webhooks

Add your webhook URL on the store's card in Yudoo (https only). Yudoo POSTs a JSON event to it whenever an order gets an answer:

EventWhen
order.confirmedThe customer tapped Confirm, or you confirmed the order in Yudoo (data.by = customer / merchant).
order.cancelledThe customer tapped Cancel, or you cancelled it in Yudoo. The reason, when given, is in data.order.confirmation.cancel_reason.
webhook.testYou pressed Send a test event, or called POST /webhooks/test.
http
POST https://mystore.com/yudoo/webhook
Content-Type: application/json
Yudoo-Event: order.cancelled
Yudoo-Delivery: evt_k3j9x2m1q8w7e6r5t4y3u2i1
Yudoo-Signature: t=1793782800,v1=5f2b8c…e91a

{
  "id": "evt_k3j9x2m1q8w7e6r5t4y3u2i1",
  "type": "order.cancelled",
  "created_at": "2026-11-04T10:20:00.000Z",
  "data": {
    "by": "customer",
    "order": {
      "id": "1042",
      "number": "#1042",
      "status": "pending",
      "confirmation": { "status": "cancelled", "at": "2026-11-04T10:19:58.000Z", "cancel_reason": "Changed my mind" },
      "customer": { "name": "Mohamed Ali", "phone": "201001234567", "email": "[email protected]" },
      "…": "the same order object as GET /orders/{id}"
    }
  }
}
  • Answer with any 2xx within 10 seconds. Do the slow work after answering.
  • A delivery that fails is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h and 24 h, then marked failed. The store's card shows the latest deliveries.
  • The same event can arrive twice (a retry after a timeout): skip an id you already handled.
  • Always check the signature before trusting an event.

POST/api/v1/webhooks/test

bash
curl -X POST https://app.yudoo.io/api/v1/webhooks/test -H "Authorization: Bearer wp_live_your_api_key"

{ "delivery": { "id": "evt_…", "delivered": true, "error": null } }

09Signature verification

Every webhook carries a Yudoo-Signature header: t is the time it was sent (Unix seconds) and v1 the HMAC-SHA256 of "t.raw_body" with your signing secret, in hex.

http
Yudoo-Signature: t=1793782800,v1=5f2b8c0d…e91a
  1. 1

    Read the raw request body, before parsing the JSON.

  2. 2

    Compute HMAC-SHA256(signing_secret, t + "." + raw_body) in hex.

  3. 3

    Compare it with v1 in constant time, and refuse a t older than 5 minutes.

import crypto from "node:crypto";
import express from "express";

const app = express();
// Read the RAW body: the signature is over the exact bytes Yudoo sent.
app.post("/yudoo/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("Yudoo-Signature") ?? "";
  const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto
    .createHmac("sha256", process.env.WHATPRO_SIGNING_SECRET)
    .update(`${t}.${req.body}`)
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  if (!fresh || !v1 || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))) {
    return res.status(400).send("bad signature");
  }
  const event = JSON.parse(req.body);
  // event.type: "order.confirmed" | "order.cancelled" | "webhook.test"
  res.sendStatus(200);
});

10Errors

A failed request answers with an HTTP status and one error object:

http
HTTP/1.1 422 Unprocessable Entity

{
  "error": {
    "code": "missing_field",
    "message": "`customer.phone` is required, in international format with the country code (e.g. 201001234567).",
    "field": "customer.phone"
  }
}
StatuscodeMeaning
400invalid_jsonThe body is not a JSON object.
401missing_api_key / invalid_api_keyNo key, a wrong or rotated key, or the store was disconnected.
404order_not_foundNo order with this id was sent to this store.
405method_not_allowedWrong method for this endpoint.
409no_webhook_urlThe test event needs a webhook URL first.
413body_too_largeThe body is larger than 64 KB.
422missing_field / invalid_field / invalid_orderA field is missing or wrong; `field` names it.
429rate_limitedMore than 120 requests a minute for the store.
500server_errorThe order was not saved; send it again.

Stuck, or need a field we don't have yet? Our team answers on WhatsApp.