Webhook events
Each delivery’s body is an envelope (event, data, timestamp, version), described in How webhooks work. This page lists what each event’s data holds.
The examples are formatted for reading, with made-up values. On the wire the JSON is compact, keys are sorted, and non-ASCII characters are escaped.
| Event | Sent when |
|---|---|
ORDER_CREATED |
An order is placed. |
PAYMENT_CAPTURED |
An online payment is captured. |
ORDER_FULFILLED |
The last item on an order is dispatched. |
RETURN_REQUESTED |
A customer asks to return items. |
ORDER_ISSUE_REPORTED |
A customer reports a problem with a fulfilled order. |
PRODUCT_UPDATED |
Staff save a product’s details. |
STOCK_LOW |
The daily low-stock scan finds low stock. |
CONSIGNMENT_DISPATCHED |
Goods leave for a retailer on a delivery challan. |
CONSIGNMENT_SOLD |
A retailer reports selling or keeping a consignment item. |
PING |
Staff send a test delivery. |
CONSIGNMENT_OVERDUE |
Defined, but not sent at this build. |
ORDER_CREATED
Section titled “ORDER_CREATED”Sent when an order is placed: at storefront checkout, when staff finalise a draft order, or when an institutional customer accepts a quote with their purchase-order number. A cart with both in-stock and made-to-order items becomes two orders, and each sends its own event.
| Field | Type | Meaning |
|---|---|---|
number |
string | Order number. Use it with order(number:). |
status |
string | Order status when the event was raised. At this build it is always UNCONFIRMED, even when the order is confirmed in the same step (for example cash on delivery). Read the current status with a query. |
paymentMethod |
string | PREPAID, COD or CREDIT. |
segment |
string | HOME, SCHOOL, COLLEGE or UNIVERSITY. |
grandTotalPaise |
integer | Order total in paise, the same as Order.grandTotal.paise. |
currency |
string | Always INR. |
lines |
array | One entry per order line. |
lines[].sku |
string | Variant SKU. |
lines[].quantity |
integer | Quantity ordered. |
lines[].totalPaise |
integer | GST-inclusive line total in paise, the same as OrderLine.total.paise. |
{ "data": { "currency": "INR", "grandTotalPaise": 2499900, "lines": [ { "quantity": 1, "sku": "DEMO-SOFA-3S", "totalPaise": 2299900 }, { "quantity": 2, "sku": "DEMO-CUSHION-SET", "totalPaise": 200000 } ], "number": "RFO/2026-27/00000", "paymentMethod": "PREPAID", "segment": "HOME", "status": "UNCONFIRMED" }, "event": "ORDER_CREATED", "timestamp": "2026-10-17T04:45:02.123456+00:00", "version": "1"}PAYMENT_CAPTURED
Section titled “PAYMENT_CAPTURED”Sent when an online payment is confirmed as captured by the payment gateway. It is sent once per payment:
- a made-to-order order has two payments (advance and balance), so it sends two events;
- one gateway charge that pays for a split cart sends one event per order, each with the same
externalPaymentId.
It is not sent for cash-on-delivery or credit payments recorded by staff.
| Field | Type | Meaning |
|---|---|---|
orderNumber |
string | The order this payment belongs to. |
gateway |
string | The payment gateway that took the money, for example razorpay. |
amountPaise |
integer | This payment’s amount in paise (not necessarily the order total). |
externalPaymentId |
string | The gateway’s own payment reference. |
{ "data": { "amountPaise": 2499900, "externalPaymentId": "pay_DEMO00000000", "gateway": "razorpay", "orderNumber": "RFO/2026-27/00000" }, "event": "PAYMENT_CAPTURED", "timestamp": "2026-10-17T04:46:10.004211+00:00", "version": "1"}ORDER_FULFILLED
Section titled “ORDER_FULFILLED”Sent when a fulfilment covers the last unfulfilled item, so the order’s status becomes FULFILLED. That means shipped with a courier, sent with our delivery crew, or ready for pickup. It is not proof of delivery. Part shipments don’t send it.
data has the same fields as ORDER_CREATED, with status set to FULFILLED.
{ "data": { "currency": "INR", "grandTotalPaise": 2499900, "lines": [ { "quantity": 1, "sku": "DEMO-SOFA-3S", "totalPaise": 2299900 }, { "quantity": 2, "sku": "DEMO-CUSHION-SET", "totalPaise": 200000 } ], "number": "RFO/2026-27/00000", "paymentMethod": "PREPAID", "segment": "HOME", "status": "FULFILLED" }, "event": "ORDER_FULFILLED", "timestamp": "2026-10-20T09:12:40.551870+00:00", "version": "1"}RETURN_REQUESTED
Section titled “RETURN_REQUESTED”Sent when a customer asks to return items from an order that has been fulfilled (fully or in part), or from a cancelled order some of whose items had already been sent. The request comes from the storefront or the returns assistant. There is no published query for returns, so this payload is all you get.
| Field | Type | Meaning |
|---|---|---|
orderNumber |
string | The order the items came from. |
returnId |
integer | Our internal number for the return request. It is not a GraphQL ID. |
reason |
string | The customer’s own words. Treat it as personal data. |
lines |
array | Items to return. |
lines[].sku |
string | Variant SKU. |
lines[].quantity |
integer | Quantity to return. |
{ "data": { "lines": [{ "quantity": 1, "sku": "DEMO-SOFA-3S" }], "orderNumber": "RFO/2026-27/00000", "reason": "Example reason text.", "returnId": 0 }, "event": "RETURN_REQUESTED", "timestamp": "2026-10-25T06:00:00.000123+00:00", "version": "1"}ORDER_ISSUE_REPORTED
Section titled “ORDER_ISSUE_REPORTED”Sent when a customer reports a problem, such as damage or missing parts, with an order that has been fulfilled (fully or in part). There is no published query for issues.
| Field | Type | Meaning |
|---|---|---|
orderNumber |
string | The order concerned. |
issueId |
integer | Our internal number for the issue. It is not a GraphQL ID. |
type |
string | DAMAGED_IN_TRANSIT, MISSING_PARTS, MANUFACTURING_DEFECT, WRONG_ITEM, ASSEMBLY_PROBLEM or OTHER. |
description |
string | The customer’s own words. Treat it as personal data. |
{ "data": { "description": "Example description text.", "issueId": 0, "orderNumber": "RFO/2026-27/00000", "type": "MISSING_PARTS" }, "event": "ORDER_ISSUE_REPORTED", "timestamp": "2026-10-21T08:30:00.250000+00:00", "version": "1"}PRODUCT_UPDATED
Section titled “PRODUCT_UPDATED”Sent when staff save the product’s own details (name, description, brand, category, tax codes, product-level price and MRP, dimensions and similar fields). It is sent on every save, even if nothing changed. It is not sent when a product is created, published, unpublished or deleted, or for edits to variants (including a variant’s own price), media or stock.
| Field | Type | Meaning |
|---|---|---|
slug |
string | The product’s slug. Use it with product(slug:). Saving a product can’t change it. |
name |
string | Product name. It may contain any script, for example Telugu. |
status |
string | DRAFT or PUBLISHED. |
{ "data": { "name": "టేకు చెక్క కుర్చీ", "slug": "demo-teak-chair", "status": "PUBLISHED" }, "event": "PRODUCT_UPDATED", "timestamp": "2026-10-17T04:45:02.123456+00:00", "version": "1"}On the wire, that name arrives escaped:
{"data":{"name":"\u0c1f\u0c47\u0c15\u0c41 \u0c1a\u0c46\u0c15\u0c4d\u0c15 \u0c15\u0c41\u0c30\u0c4d\u0c1a\u0c40","slug":"demo-teak-chair","status":"PUBLISHED"},"event":"PRODUCT_UPDATED","timestamp":"2026-10-17T04:45:02.123456+00:00","version":"1"}STOCK_LOW
Section titled “STOCK_LOW”Sent when the daily low-stock scan finds stock at or below its threshold. The scan runs at 07:00 IST by default; staff can change the schedule. If nothing is low, nothing is sent. Only warehouses that sell online are scanned.
| Field | Type | Meaning |
|---|---|---|
items |
array | Up to 50 rows, lowest available stock first. One row per variant per warehouse, so a SKU can appear more than once. The warehouse is not named. |
items[].sku |
string | Variant SKU. |
items[].product |
string | Product name. |
items[].available |
integer | Units on hand minus units reserved. |
items[].threshold |
integer | The low-stock threshold set for that stock row. |
{ "data": { "items": [ { "available": 0, "product": "Demo Teak Chair", "sku": "DEMO-CHAIR-TEAK", "threshold": 2 }, { "available": 1, "product": "Demo Three-Seater Sofa", "sku": "DEMO-SOFA-3S", "threshold": 3 } ] }, "event": "STOCK_LOW", "timestamp": "2026-10-17T01:30:00.418203+00:00", "version": "1"}CONSIGNMENT_DISPATCHED
Section titled “CONSIGNMENT_DISPATCHED”Sent when staff dispatch goods to a retailer on a delivery challan. If raising the event fails, the dispatch still goes ahead and no event is sent, so reconcile with consignments.
| Field | Type | Meaning |
|---|---|---|
number |
string | Challan number. |
dealer_id |
integer | The retailer’s internal number. Build the Dealer ID from it to query: see Consignment to ERP. |
unit_count |
integer | Number of items on the challan. |
total_value_paise |
integer | GST-inclusive value of the goods at transfer price, in paise. |
dispatched_at |
string or null | When the goods left, ISO 8601 with a UTC offset. |
{ "data": { "dealer_id": 0, "dispatched_at": "2026-10-17T05:30:00+00:00", "number": "RFDC/2627/00000", "total_value_paise": 8450000, "unit_count": 6 }, "event": "CONSIGNMENT_DISPATCHED", "timestamp": "2026-10-17T05:30:01.207346+00:00", "version": "1"}CONSIGNMENT_SOLD
Section titled “CONSIGNMENT_SOLD”Sent when a retailer reports that an item from consignment stock has gone: sold to a customer, or kept by the retailer themselves. The payload doesn’t say which. It is sent once per report; if the retailer’s app sends the same report again, no second event follows. If raising the event fails, the sale still stands and no event is sent, so reconcile with consignmentInvoices.
| Field | Type | Meaning |
|---|---|---|
sale_id |
integer | Our internal number for the sale. |
dealer_id |
integer | The retailer’s internal number. |
sku |
string | Variant SKU of the item sold. |
transfer_price_paise |
integer | What we invoice the retailer for the item, in paise. |
sold_at |
string or null | When the retailer says it sold, ISO 8601 with a UTC offset. |
{ "data": { "dealer_id": 0, "sale_id": 0, "sku": "DEMO-BED-QUEEN", "sold_at": "2026-10-18T12:05:00+00:00", "transfer_price_paise": 2100000 }, "event": "CONSIGNMENT_SOLD", "timestamp": "2026-10-18T12:05:03.880012+00:00", "version": "1"}Sent when staff send a test delivery to one webhook. It goes to that webhook even if PING is not among its events. See Checking your receiver with PING.
| Field | Type | Meaning |
|---|---|---|
message |
string | Always RaviFurniture webhook test. |
{ "data": { "message": "RaviFurniture webhook test" }, "event": "PING", "timestamp": "2026-10-17T04:45:02.123456+00:00", "version": "1"}CONSIGNMENT_OVERDUE
Section titled “CONSIGNMENT_OVERDUE”CONSIGNMENT_OVERDUE exists in the list of event types, so staff can tick it, but nothing sends it at this build. Don’t build anything that waits for it.
Unknown events and new fields
Section titled “Unknown events and new fields”Payloads follow an additive-only rule: fields can be added, but existing fields are not removed or changed. Ignore fields you don’t recognise, and acknowledge (with 2xx) any event type you don’t handle.