Entwickler
Webhooks
Auf dieser Seite
Einen Endpunkt einrichten
- Füge deinem Server eine https-Route hinzu, die den Roh-Body liest und innerhalb von 10 Sekunden mit
2xxantwortet. Erledige die Arbeit nach der Antwort. - Leg den Endpunkt mit der API an oder frag unser Team. Speichere das
whsec_-Secret aus der Antwort: Es wird nur einmal angezeigt. - Verifiziere jede Anfrage (siehe unten), dann dedupliziere nach der Event-
id. - Schick dir selbst ein Test-Event jedes Typs, den du verarbeitest.
Das Event
Jedes Event hat denselben Umschlag. data enthält die Felder des jeweiligen Typs.
idstring- Beginnt mit evt_. Bei jeder Wiederholung gleich und im webhook-id-Header.
typestring- Eines der Events unten.
api_versionstring- Die Version, in der data geschrieben ist: die deines Endpunkts.
created_atstring- Wann es passiert ist, was vor deinem Empfang sein kann.
testboolean- true für Test-Events.
dataobject- IDs, mit denen du das vollständige Objekt holst, und was sich geändert hat.
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
}
}Events
order.created
Eine Bestellung ist bei uns angekommen, aus deinem Shopify-Shop oder über die API. Bezahlt oder 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
Eine Bestellung wurde aus deinem Guthaben bezahlt. Wird auch für Bestellungen ausgelöst, die auf eine Aufladung gewartet haben.
{
"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
Wir haben den Verkäufer bezahlt. Die Positionen sind with_seller, bis sie in unserem Lager ankommen.
{
"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
Die Artikel sind in unserem Lager, geprüft, mit ihren Fotos. Als Nächstes wird gepackt.
{
"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
Ein Paket ist mit Sendungsverfolgung unterwegs; die Versandkosten sind final. Der Shop, aus dem die Bestellung kam, hat die Sendungsverfolgung ebenfalls.
{
"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
Ein Verkäufer hat einen Preis geändert, und was deine Sync-Regel daraufhin getan hat: neu bepreist, pausiert oder benachrichtigt.
{
"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
Der Verkäufer ist bei einer Variante ausverkauft und dein Regal hat keine: Wir setzen sie auf deinen Kanälen auf 0.
{
"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
Unsere Prüfung hat ein Problem mit einem Artikel gefunden: falsche Farbe oder Größe, Schaden, ein fehlendes Teil. Entscheide bis respond_by; ohne Antwort halten wir ihn zurück und schreiben dir.
{
"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"
}
}Signaturen
Zustellungen werden nach Standard Webhooks signiert, eine Bibliothek, die du vielleicht schon nutzt, verifiziert sie also. Jede Zustellung bringt drei Header mit:
| Header | Was es enthält |
|---|---|
| webhook-id | Die Event-ID. |
| webhook-timestamp | Wann wir diesen Versuch gesendet haben, in Unix-Sekunden. |
| webhook-signature | v1, dann ein Komma und der base64-codierte HMAC-SHA256 von id.timestamp.body mit deinem Secret. Während einer Rotation zwei davon, durch ein Leerzeichen getrennt. |
Der Schlüssel ist dein Secret nach whsec_, base64-dekodiert. Signiere den Roh-Body genau so, wie er angekommen ist; lehne einen Zeitstempel ab, der mehr als fünf Minuten abweicht, damit eine abgefangene Anfrage nicht wiederholt werden kann.
Eine Zustellung verifizieren
Mit der Bibliothek standardwebhooks oder in ein paar eigenen Zeilen:
Wiederholungen und Reihenfolge
Alles außer einem 2xx innerhalb von 10 Sekunden ist ein Fehlschlag, und wir versuchen es erneut, jedes Mal mit derselben webhook-id und neuem Zeitstempel und neuer Signatur:
| Versuch | Nach dem vorherigen |
|---|---|
| 1 | Sofort |
| 2 | 5 Sekunden |
| 3 | 5 Minuten |
| 4 | 30 Minuten |
| 5 | 2 Stunden |
| 6 | 5 Stunden |
| 7 | 10 Stunden |
| 8 | 10 Stunden, der letzte |
- Nach dem letzten Versuch ist die Zustellung fehlgeschlagen. Sende sie erneut aus der Zustellungsliste.
- Ein Endpunkt, dessen Zustellungen fünf Tage lang alle fehlgeschlagen sind, wird deaktiviert, nachdem wir dir eine E-Mail geschickt haben. Aktivierst du ihn wieder, wird gesendet, was er in den letzten drei Tagen verpasst hat.
- Events können doppelt und in falscher Reihenfolge ankommen. Dedupliziere nach id, und wenn die Reihenfolge zählt, hol das Objekt und vertrau seinem Status statt dem des Events.