Partner apps get webhooks from the same system as every NUMU integration, described in Webhooks. Three things differ:Your manifest is the subscription. When a merchant approves your app, NUMU subscribes the URLs in your manifest, for the events whose read scope the merchant granted. You don't call the webhooks API, and an app token cannot.
Deliveries are signed with your client secret.
Two lifecycle events, app.uninstalled and store.redact, go straight to your URL, once, with no retry.
Events#
| Event | Needs | Sent when |
|---|
order.created | orders:read | A new order is placed |
order.paid | orders:read | Payment is confirmed |
order.status_changed | orders:read | The status moves. It carries new_status and tracking_number, so it is also your shipping event |
product.created | catalog:read | A product is added |
product.updated | catalog:read | A product changes |
product.deleted | catalog:read | A product is removed |
app.uninstalled | ā | The merchant uninstalled your app. Required in every manifest |
store.redact | ā | 48 hours after an uninstall, unless the merchant installed again. List it in your manifest to receive it |
Cash on delivery is the norm in Egypt, so most orders are confirmed by phone and paid at the door. Build around order.created and order.status_changed, not only order.paid.A delivery#
{
"event": "order.paid",
"timestamp": "2026-09-23T12:00:00.000000+00:00",
"data": {
"store_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"order_id": "8c2e1f4a-2b7d-4e55-9c1a-1f0c2b7d4e55",
"order_number": "ORD-482913",
"payment_id": "ā¦",
"payment_method": "cod",
"total": "850.00"
}
}
Which store sent it#
One URL receives events from every store that installed your app. Every delivery names its store in data.store_id: route by it.Verify every delivery#
Verify X-NUMU-Signature-V1 exactly as Webhooks shows, using your client secret as the key: HMAC-SHA256 over <timestamp>.<raw body>, compared in constant time, with stale timestamps rejected.Answer 401 to a bad signature. Reviewers send one, and an app that accepts it is not approved. Test your endpoint with the CLI:The second command signs with a wrong key, and passes only if your endpoint answers 401. The sample body carries "sample": true and a zero store_id.Answer fast#
Reply with a 2xx within 10 seconds. Acknowledge first and do the work in a queue afterwards.Deliveries arrive at least once and not in order. X-NUMU-Delivery stays the same across retries of one delivery: use it to skip duplicates.
A non-2xx answer or a timeout is retried 5 times, after 10 seconds, 30 seconds, 2 minutes, 10 minutes and 30 minutes.
After the last attempt, NUMU switches off your subscription on that store and tells the merchant in their dashboard. To turn it back on, send the merchant through the consent screen again (Ask for more scopes): exchanging the new code recreates your subscriptions. 410 Gone switches the subscription off at once. Don't send it unless you mean it.
Lifecycle events#
{
"event": "app.uninstalled",
"timestamp": "2026-09-23T12:00:00.000000+00:00",
"data": { "store_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6" }
}
app.uninstalled: your token for the store stops working as the uninstall completes. Delete it, and stop every job for that store.
store.redact: delete everything you hold about that store and its customers. Egypt's Personal Data Protection Law (151/2020) expects it.
Both are sent once, with no retry. If your endpoint is down at that moment, you miss the event. So also treat a 401 on a store's token as an uninstall, and run your own cleanup 48 hours later.
When deliveries stop#
You receive no events for a store while:the merchant has disabled your app, or uninstalled it;
NUMU has suspended your app, or paused all partner apps.
Don't count on events from that time reaching you later. When a store comes back, read what changed from the API. Modified atĀ 2026-09-24 14:17:29