Skip to content
Whats91

Create a merchant draft order with catalog products or custom items.

Create Order

Summary

Create a merchant draft order with catalog products or custom items.

Prerequisites

  • Authorization: Bearer w91_live_xxx
  • Content-Type: application/json for JSON requests

Creates a merchant draft order. The draft is not sent to the customer until you call one of the send endpoints. Orders reuse the same order and payment services as the customer dashboard, so a draft created here is visible and manageable in the dashboard.

Warning

Every order endpoint requires the WhatsApp Orders & Payments add-on (code 105) for the resolved sender. Without it the request fails with 403 ORDERS_ADDON_REQUIRED before any order or payment side effect occurs.

POST/api/v3/orders

Create a merchant draft order.

ParameterTypeRequiredDescription
sender_idstringOptionalWhatsApp sender. Aliases: senderId, sender. Optional when the token resolves a sender.
recipientobjectRequiredRecipient as { phone, name } or { contact_uid }. Phone recipients currently must be an Indian WhatsApp number (91 plus 10 digits).
itemsarrayRequiredOne to ten catalog products or custom items.
chargesobjectOptionaltax, tax_description, shipping, discount, discount_description.
currencystringOptionalMust be INR.
notestringOptionalFree-text merchant note stored with the order.
subtotal_amountstringOptionalAssertion. A mismatch rejects the request instead of overriding calculated totals.
total_amountstringOptionalAssertion. Maximum order total is 500000 INR.
cURL
curl -X POST "https://graph.whats91.com/api/v3/orders" \
  -H "Authorization: Bearer w91_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: restaurant-checkout-2091" \
  -d '{
    "sender_id": "919999999999",
    "recipient": { "phone": "919876543210", "name": "Restaurant Guest" },
    "items": [
      { "product_uid": "prod_masala_dosa", "quantity": 1 },
      { "product_uid": "prod_filter_coffee", "quantity": 2 }
    ],
    "charges": {
      "tax": "18.00",
      "tax_description": "GST",
      "shipping": "0.00",
      "discount": "10.00",
      "discount_description": "Table offer"
    },
    "currency": "INR",
    "note": "Table 4",
    "subtotal_amount": "292.00",
    "total_amount": "300.00"
  }'
Custom item
{
  "name": "Catering service",
  "price": "1500.00",
  "quantity": 1,
  "is_physical": false,
  "image_url": "https://cdn.example.com/catering.jpg",
  "retailer_id": "SERVICE-88"
}
200 OK
{
  "success": true,
  "message": "Order created",
  "data": {
    "senderId": "919999999999",
    "phoneNumberId": "phone-number-id",
    "wabaId": "waba-id",
    "order": {
      "uid": "ord_abc123",
      "reference": "ORD-1786000000000-A1B2",
      "source": "merchant",
      "status": "draft",
      "payment_status": "not_requested",
      "currency": "INR",
      "subtotal_amount": "292.00",
      "tax_amount": "18.00",
      "shipping_amount": "0.00",
      "discount_amount": "10.00",
      "total_amount": "300.00",
      "item_count": 2,
      "recipient": {
        "phone": "919876543210",
        "name": "Restaurant Guest",
        "blacklisted": false
      },
      "note": "Table 4",
      "readiness": { "ready_for_payment": false, "reasons": ["not_sent"] },
      "created_at": "2026-08-06T09:00:00.000Z",
      "updated_at": "2026-08-06T09:00:00.000Z"
    }
  },
  "metadata": {
    "apiVersion": "v3",
    "requestId": "request-uuid"
  }
}

Item Rules

  • Maximum 10 items per order.
  • Catalog items reference product_uid; custom items supply name, price, and quantity.
  • Physical custom items must also include country_of_origin, importer_name, and importer_address.
  • Currency must be INR and the order total must not exceed 500000.
  • subtotal_amount and total_amount are assertions: a mismatch is rejected rather than silently accepted.

Idempotency

Send Idempotency-Key as a header, or idempotency_key / idempotencyKey in the body; the header wins. Keys are scoped to the authenticated customer and the specific endpoint, and completed results are retained for 24 hours.

  • A replay returns the stored order without creating another one, and sets metadata.idempotent_replay to true.
  • A concurrent request reusing an in-flight key returns 409 IDEMPOTENCY_REQUEST_IN_PROGRESS with details.retryable: true.
  • The first request binds the key. Reusing it with a different body still replays the first result.
  • Without a key, every create call intentionally creates a new draft.

SDK Examples

Use these examples as starting points for server-side implementations.

cURL
curl -X POST "https://graph.whats91.com/api/v3/orders" \
  -H "Authorization: Bearer w91_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Catering service",
  "price": "1500.00",
  "quantity": 1,
  "is_physical": false,
  "image_url": "https://cdn.example.com/catering.jpg",
  "retailer_id": "SERVICE-88"
}'
Node.js
const response = await fetch("https://graph.whats91.com/api/v3/orders", {
  method: "POST",
  headers: {
    "Authorization": "Bearer w91_live_xxx",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "name": "Catering service",
    "price": "1500.00",
    "quantity": 1,
    "is_physical": false,
    "image_url": "https://cdn.example.com/catering.jpg",
    "retailer_id": "SERVICE-88"
  })
});

const data = await response.json();
console.log(data);
PHP
$ch = curl_init("https://graph.whats91.com/api/v3/orders");
curl_setopt_array($ch, [
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer w91_live_xxx",
    "Content-Type: application/json"
  ],
  CURLOPT_POSTFIELDS => json_encode([
    "name" => "Catering service",
    "price" => "1500.00",
    "quantity" => 1,
    "is_physical" => false,
    "image_url" => "https://cdn.example.com/catering.jpg",
    "retailer_id" => "SERVICE-88"
  ])
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
Python
import requests

response = requests.request(
    "POST",
    "https://graph.whats91.com/api/v3/orders",
    headers={
        "Authorization": "Bearer w91_live_xxx",
        "Content-Type": "application/json",
    },
    json={
        "name": "Catering service",
        "price": "1500.00",
        "quantity": 1,
        "is_physical": False,
        "image_url": "https://cdn.example.com/catering.jpg",
        "retailer_id": "SERVICE-88"
    }
)

print(response.json())
C#
using System.Text;

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer w91_live_xxx");

var request = new HttpRequestMessage(HttpMethod.Post, "https://graph.whats91.com/api/v3/orders");
request.Content = new StringContent(
  """
  {
    "name": "Catering service",
    "price": "1500.00",
    "quantity": 1,
    "is_physical": false,
    "image_url": "https://cdn.example.com/catering.jpg",
    "retailer_id": "SERVICE-88"
  }
  """,
  Encoding.UTF8,
  "application/json"
);

var response = await client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());

Related APIs