Sviluppatori
Webhook
In questa pagina
Configura un endpoint
- Aggiungi al tuo server una route https che legga il corpo grezzo e risponda
2xxentro 10 secondi. Fai il lavoro dopo aver risposto. - Crea l’endpoint con l’API, o chiedi al nostro team. Salva il segreto
whsec_che restituisce: viene mostrato una sola volta. - Verifica ogni richiesta (qui sotto), poi deduplica sull’
iddell’evento. - Inviati un evento di test per ogni tipo che gestisci.
L’evento
Ogni evento ha lo stesso involucro. data contiene i campi propri del tipo.
idstring- Inizia con evt_. Uguale a ogni nuovo tentativo, e nell’intestazione webhook-id.
typestring- Uno degli eventi qui sotto.
api_versionstring- La versione in cui è scritto data: quella del tuo endpoint.
created_atstring- Quando è successo, che può essere prima che tu lo riceva.
testboolean- true per gli eventi di test.
dataobject- ID con cui recuperare l’oggetto completo, e cosa è cambiato.
POST /hooks/ninjabuy HTTP/1.1
Host: acme-goods.com
Content-Type: application/json
User-Agent: NinjaBuy-Webhooks/1.0
webhook-id: evt_4m2x
webhook-timestamp: 1793685600
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
{
"id": "evt_4m2x",
"type": "order.shipped",
"api_version": "2026-11",
"created_at": "2026-11-03T06:00:00Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"shipment": "shp_5r8w",
"carrier": "YunExpress",
"tracking_number": "YT2630812345678",
"tracking_url": "https://t.17track.net/en#nums=YT2630812345678",
"shipping_charged": { "amount": "134.52", "currency": "CNY" },
"pushed_to_channel": true
}
}Eventi
order.created
Un ordine ci è arrivato, dal tuo negozio Shopify o dall’API. Pagato, o awaiting_payment.
{
"id": "evt_1a7c",
"type": "order.created",
"api_version": "2026-11",
"created_at": "2026-11-01T09:14:02Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"source": "shopify",
"status": "buying",
"payment_status": "paid",
"lines": [
{
"line": "lin_9a2c",
"variant": "var_41hz",
"sku": "5187209554",
"quantity": 1
}
]
}
}order.paid
Un ordine è stato pagato dal saldo. Scatta anche per gli ordini che aspettavano una ricarica.
{
"id": "evt_1a9f",
"type": "order.paid",
"api_version": "2026-11",
"created_at": "2026-11-01T11:02:40Z",
"test": false,
"data": {
"order": "ord_3n8a",
"external_id": "1013",
"amount": { "amount": "57.00", "currency": "CNY" },
"paid_from": "balance",
"was_awaiting_payment": true
}
}order.bought
Abbiamo pagato il venditore. Le righe sono with_seller finché non arrivano nel nostro magazzino.
{
"id": "evt_2c4e",
"type": "order.bought",
"api_version": "2026-11",
"created_at": "2026-11-01T13:30:05Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"lines": [
{
"line": "lin_9a2c",
"sku": "5187209554",
"quantity": 1,
"status": "with_seller"
}
]
}
}order.checked
Gli articoli sono nel nostro magazzino, controllati, con le loro foto. Segue l’imballaggio.
{
"id": "evt_9w1c",
"type": "order.checked",
"api_version": "2026-11",
"created_at": "2026-11-02T03:20:11Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"fulfillment_order": "fo_7k2m",
"lines": [
{
"line": "lin_9a2c",
"qc": "passed",
"photos": [
"https://media.ninjabuy.com/qc/ord_3n7q/1.webp",
"https://media.ninjabuy.com/qc/ord_3n7q/2.webp"
]
}
]
}
}order.shipped
Un pacco è partito con tracciamento; il costo di spedizione è definitivo. Anche il negozio da cui è arrivato l’ordine ha il tracciamento.
{
"id": "evt_4m2x",
"type": "order.shipped",
"api_version": "2026-11",
"created_at": "2026-11-03T06:00:00Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"shipment": "shp_5r8w",
"carrier": "YunExpress",
"tracking_number": "YT2630812345678",
"tracking_url": "https://t.17track.net/en#nums=YT2630812345678",
"shipping_charged": { "amount": "134.52", "currency": "CNY" },
"pushed_to_channel": true
}
}product.cost_changed
Un venditore ha cambiato un prezzo, e cosa ha fatto la tua regola di sincronizzazione: prezzo ricalcolato, pausa o notifica.
{
"id": "evt_8q0d",
"type": "product.cost_changed",
"api_version": "2026-11",
"created_at": "2026-11-04T02:10:00Z",
"test": false,
"data": {
"product": "prd_1x0a",
"variant": "var_2b7k",
"sku": "4410923871",
"cost": {
"from": { "amount": "42.00", "currency": "CNY" },
"to": { "amount": "48.00", "currency": "CNY" }
},
"action": "repriced",
"price": {
"from": { "amount": "18.99", "currency": "USD" },
"to": { "amount": "20.99", "currency": "USD" }
},
"rule": "rule_default"
}
}product.out_of_stock
Il venditore ha esaurito una variante e il tuo scaffale non ne ha: la portiamo a 0 sui tuoi canali.
{
"id": "evt_2b9n",
"type": "product.out_of_stock",
"api_version": "2026-11",
"created_at": "2026-11-04T02:11:30Z",
"test": false,
"data": {
"product": "prd_8f2k",
"variant": "var_41j0",
"sku": "5187209555",
"warehouse_on_hand": 0,
"channels_updated": [
{
"channel": "shopify",
"shop": "acme-goods.myshopify.com",
"available": 0
}
]
}
}qc.issue
Il nostro controllo ha trovato un problema con un articolo: colore o taglia sbagliati, danno, un pezzo mancante. Scegli entro respond_by; senza risposta lo tratteniamo e ti scriviamo.
{
"id": "evt_6c3h",
"type": "qc.issue",
"api_version": "2026-11",
"created_at": "2026-11-02T03:20:11Z",
"test": false,
"data": {
"order": "ord_3n7q",
"external_id": "1012",
"fulfillment_order": "fo_7k2m",
"line": "lin_9a2c",
"sku": "5187209554",
"issue": "wrong_color",
"note": "Received grey, ordered black.",
"photos": ["https://media.ninjabuy.com/qc/ord_3n7q/3.webp"],
"choices": ["ship_anyway", "exchange", "refund"],
"respond_by": "2026-11-05T03:20:11Z"
}
}Firme
Le consegne sono firmate secondo Standard Webhooks, quindi una libreria che forse usi già le verifica. Ogni consegna ha tre intestazioni:
| Intestazione | Cosa contiene |
|---|---|
| webhook-id | L’ID dell’evento. |
| webhook-timestamp | Quando abbiamo inviato questo tentativo, in secondi Unix. |
| webhook-signature | v1, poi una virgola e l’HMAC-SHA256 in base64 di id.timestamp.body con il tuo segreto. Durante una rotazione, due, separate da uno spazio. |
La chiave è il tuo segreto dopo whsec_, decodificato da base64. Firma il corpo grezzo esattamente come è arrivato; rifiuta un timestamp sfasato di più di cinque minuti, così una richiesta intercettata non può essere ripetuta.
Verifica una consegna
Con la libreria standardwebhooks, o in poche righe tue:
Nuovi tentativi e ordine
Tutto ciò che non è un 2xx entro 10 secondi è un errore, e riproviamo, ogni volta con lo stesso webhook-id e nuovi timestamp e firma:
| Tentativo | Dopo quello precedente |
|---|---|
| 1 | Subito |
| 2 | 5 secondi |
| 3 | 5 minuti |
| 4 | 30 minuti |
| 5 | 2 ore |
| 6 | 5 ore |
| 7 | 10 ore |
| 8 | 10 ore, l’ultima |
- Dopo l’ultimo tentativo la consegna è fallita. Inviala di nuovo da l’elenco delle consegne.
- Un endpoint le cui consegne sono fallite tutte per cinque giorni viene disattivato, dopo un’email. Riattivarlo invia ciò che ha perso negli ultimi tre giorni.
- Gli eventi possono arrivare due volte e fuori ordine. Deduplica su id e, quando l’ordine conta, recupera l’oggetto e fidati del suo stato invece che di quello dell’evento.