Zopping

Events & webhooks

Stop polling. Zopping emits events as things happen — orders, inventory, customers, products — so your system can react in near-real-time instead of scanning for changes.

How it works

  1. Zopping fires internal events on state changes (order created, status changed, stock updated, …).
  2. You enable an event integration for your organization — a template that routes a given event to a partner destination.
  3. Zopping pushes the event to that destination as it happens.

Webhooks

Zopping pushes an HTTP webhook to your configured endpoint the moment something changes. Each event carries the resource's identifier and only the fields that changed — call the API with that id when you need the full record.

The request you receive

Zopping delivers each event as an HTTP POST with a JSON body and these headers:

HeaderMeaning
X-Event-TypeThe event name (e.g. order.placed).
X-Delivery-IDUnique id for this delivery, reused on retries — use it to deduplicate.
X-Event-TimestampWhen the event occurred — epoch milliseconds (e.g. 1754558096789).
X-Webhook-SignatureHex signature of the raw request body — verify it before trusting the payload.

The X-Event-Type header tells you which event this is — the JSON body then carries only the fields for that event (the payloads below).

Order Events

Every order event carries the order's id — its internal reference number, always present — and, when you set one on the order, your own clientId (the external order id). Match on whichever your system keys by. Each event carries these plus only the fields that changed.

EventFires whenPayload
order.placedAn order is created.id, clientId, status
order.statusUpdatedThe order moves to a new status.id, clientId, status
order.updatedOne or more order details change in an edit — delivery address, delivery slot/date, fulfillment store, or order type.id, clientId + only the fields that changed (addressId, slotId, preferredDate, storeId, type)
order.itemUpdatedOrder items change — quantity edits, additions or removals.id, clientId, items[] of { itemId, oldQuantity, newQuantity }
order.cancelledThe order is cancelled.id, clientId

Payload fields

order.updated always carries id (and clientId when set) and then only the keys that changed in that edit — an edit that changes just the store sends only storeId; an edit that changes the store and the type sends both.

FieldTypeNotes
idstringThe order's internal reference number — always present.
clientIdstringYour own client id for the order (its external order id), present only when you set one.
statusstringThe order's status (e.g. PENDING, DISPATCHED).
addressIdintegerThe delivery address the order moved to.
slotIdintegerThe delivery slot the order moved to.
preferredDatestringThe preferred delivery date the order moved to (YYYY-MM-DD).
storeIdintegerThe fulfillment store the order moved to.
typestringThe order type the order moved to — DELIVERY or PICKUP.
itemsarrayObjects of { itemId, oldQuantity, newQuantity } — all three integer.

An added item comes through as oldQuantity: 0, a removed item as newQuantity: 0.

Examples

// order.placed
{ "id": "1771", "clientId": "BLK-2026-002", "status": "PENDING" }

// order.statusUpdated
{ "id": "1771", "clientId": "BLK-2026-002", "status": "DISPATCHED" }

// order.updated — only the delivery address changed
{ "id": "1771", "clientId": "BLK-2026-002", "addressId": 90876 }

// order.updated — only the slot/date changed
{ "id": "1771", "clientId": "BLK-2026-002", "slotId": 44, "preferredDate": "2026-08-15" }

// order.updated — store and type changed in the same edit
{ "id": "1771", "clientId": "BLK-2026-002", "storeId": 7226, "type": "PICKUP" }

// order.itemUpdated
{ "id": "1771", "clientId": "BLK-2026-002", "items": [ { "itemId": 11111362279, "oldQuantity": 2, "newQuantity": 3 } ] }

// order.cancelled
{ "id": "1771", "clientId": "BLK-2026-002" }

Inventory Events

Every inventory event carries the same three identifiers. The event tells you which record changed, not what changed — fetch the item's current state from the inventory API and update from that. The same product in three stores is three separate records, so you get one event per store.

EventFires whenPayload
inventory.itemAddedA product is stocked in a store for the first time. Never fires twice for the same product/store — a repeat write fires inventory.itemUpdated instead.productId, storeId, sellerId
inventory.itemUpdatedAny change to an existing item — stock count, MRP/discount, status (enabled / disabled / out-of-stock), unlimited-stock flag, tax, location. Admin edits, API writes and bulk CSV uploads all fire it (one event per row, so expect bursts).productId, storeId, sellerId
inventory.itemDeletedAn inventory record is deleted — the product is no longer stocked in that store. The identifiers refer to a record that no longer exists, so don't fetch it. Deleting the product from the catalogue does not fire this.productId, storeId, sellerId

Payload fields

FieldTypeNotes
productIdintegerThe catalogue product.
storeIdintegerWhich store's inventory changed — the same product in multiple stores is a separate record per store.
sellerIdinteger0 = the organization's own inventory; non-zero = a marketplace seller's inventory for that product.

Examples

// inventory.itemAdded — product 42 stocked in store 7 for the first time
{ "productId": 42, "storeId": 7, "sellerId": 0 }

// inventory.itemUpdated — stock or price of product 42 changed in store 7
{ "productId": 42, "storeId": 7, "sellerId": 0 }

// inventory.itemUpdated — a marketplace seller's inventory of product 91 changed
{ "productId": 91, "storeId": 7, "sellerId": 318 }

// inventory.itemDeleted — product 42 no longer stocked in store 7
{ "productId": 42, "storeId": 7, "sellerId": 0 }

Payment Events

Fires when money moves on an order — a rider collects cash, a payment settles, or a refund completes.

EventFires whenPayload
payment.riderCollectedA rider (delivery agent) collects COD at the doorstep on delivery. This is the collection moment — the order's payment_status may still be PENDING until the trip is settled.orderId, orderClientId
payment.completedA payment on the order is settled — online capture (CUSTOMER), POS/admin register-payment (REGISTER_PAYMENT), or a rider trip's cash settled at fin-close (FIN_CLOSE).orderId, orderClientId, collectedVia
payment.refundCompletedA refund on the order completes — online (gateway-confirmed) or a cash / wallet refund recorded by the merchant. Fires once, on the transition into COMPLETED.orderId, orderClientId

For a COD order collected by a rider you receive two events across its lifecycle: payment.riderCollected at the doorstep, then payment.completed (collectedVia: "FIN_CLOSE") when the trip's cash is settled.

Payload fields

FieldTypeNotes
orderIdstringThe order's internal reference number — always present.
orderClientIdstringYour own client id for the order (its external order id), present only when you set one.
collectedViastringpayment.completed only — how the payment was settled (FIN_CLOSE, REGISTER_PAYMENT, CUSTOMER). Not sent on payment.riderCollected (the event name already says how).

The payloads are deliberately minimal; a consumer that needs the amount, method, settlement state or gateway references calls the order API with the orderId.

payment.riderCollected ≠ settled. It marks that the customer handed over cash, not that the order's payment_status is PAID. Treat payment.completed as the settlement signal.

Examples

// payment.riderCollected — rider took the cash at the door
{ "orderId": "1771", "orderClientId": "BLK-2026-002" }

// payment.completed — trip cash settled at fin-close
{ "orderId": "1771", "orderClientId": "BLK-2026-002", "collectedVia": "FIN_CLOSE" }

// payment.refundCompleted
{ "orderId": "1771", "orderClientId": "BLK-2026-002" }

Customer Events

Every customer event carries the customerId — the identifier the customer APIs use — plus only the fields that changed. Membership and address changes are reported as customer events, because the customer is the record you look up to act on them.

EventFires whenPayload
customer.createdA customer account is created — storefront signup, admin entry, guest conversion or import.customerId
customer.updatedOne or more of the customer's details change in an edit — name, phone, email, profile image, your client id, or which address is default. Editing a phone number or email address directly fires this too.customerId + only the fields that changed (name, firstName, lastName, phone, email, image, clientId, defaultAddressId)
customer.statusUpdatedThe customer is enabled, disabled or archived.customerId, status
customer.deletedDeletion of the customer account is requested. See the note below.customerId
customer.membershipActivatedA membership becomes usable — bought outright, or once its payment clears.customerId, membershipId
customer.membershipExpiredA membership passes its end date.customerId, membershipId
customer.addressCreatedAn address is added to the customer.customerId, addressId
customer.addressUpdatedOne of the customer's addresses is changed.customerId, addressId

Payload fields

customer.updated always carries customerId and then only the keys that changed in that edit — an edit that changes just the phone sends only phone; an edit that changes the name and email sends both.

FieldTypeNotes
customerIdintegerThe customer's id.
statusstringThe status the customer moved to — ENABLED, DISABLED or ARCHIVED.
namestringThe customer's full name.
firstName, lastNamestringThe parts of the customer's name.
phonestringThe customer's default phone number.
emailstringThe customer's default email address.
imagestringProfile-image URL.
clientIdstringYour own identifier for the customer.
defaultAddressIdintegerThe address that became the default.
membershipIdintegerThe membership the event concerns.
addressIdintegerThe address added or changed.

A membership reports the two states you can act on: customer.membershipActivated when it becomes usable and customer.membershipExpired when it lapses. Activation covers both routes in — bought outright, or paid for online — so you never have to watch for a purchase and an activation separately. Neither event carries a status, because the event name is the status.

A status change does not appear in customer.updated — customer.statusUpdated reports it instead, and an edit that changed nothing else sends no customer.updated at all.

customer.updated is the only event that carries personal data, so verify X-Webhook-Signature and serve your endpoint over HTTPS.

Customer tagging is not delivered over webhook; poll the customer if you need its tags.

customer.deleted fires on the delete request, not on the purge. The record enters a retention period and is removed later. Treat it as "this customer is going away", not "this customer is gone".

Examples

// customer.created
{ "customerId": 4821 }

// customer.updated — only the phone number changed
{ "customerId": 4821, "phone": "+919812345678" }

// customer.updated — name and email changed in the same edit
{ "customerId": 4821, "name": "A Bansal", "email": "a.bansal@example.com" }

// customer.updated — a different address became the default
{ "customerId": 4821, "defaultAddressId": 90876 }

// customer.statusUpdated
{ "customerId": 4821, "status": "DISABLED" }

// customer.deleted
{ "customerId": 4821 }

// customer.membershipActivated
{ "customerId": 4821, "membershipId": 12 }

// customer.membershipExpired
{ "customerId": 4821, "membershipId": 12 }

// customer.addressCreated
{ "customerId": 4821, "addressId": 90876 }

// customer.addressUpdated
{ "customerId": 4821, "addressId": 90876 }

Going further

  • Receive & fulfill orders — the polling counterpart and the order lifecycle these events track.
  • Extensions — some event flows depend on the relevant extension being enabled.