Webhook guide
We send a request to your URL whenever a shipment status changes, so your software does not have to keep asking us.
Setup
- Add an endpoint in the panel (Panel → Webhooks). The URL must be HTTPS.
- Copy your signing secret — it is shown ONLY ONCE.
- Pick the events you want. If you pick none, you get ALL of them.
- Use “Send a test event” to verify your endpoint; no real shipment needed.
Events
| shipment.created | A shipment was saved and its fee taken from the wallet. |
| shipment.labeled | A shipping label was produced; a tracking number now exists. |
| shipment.status_changed | The shipment status changed. The payload includes the previous status. |
| shipment.delivered | The shipment was delivered. Sent IN ADDITION to the status-change event, because most integrations only care about this one. |
| shipment.cancelled | The shipment was cancelled and its fee returned to the wallet. |
| ping | The test event you send from the panel, so you can verify your endpoint without creating a real shipment. |
Headers
| X-Shipzone-Signature | `v1=<hex>` — HMAC-SHA256 of the raw body. |
| X-Shipzone-Event | The event name (e.g. `shipment.delivered`). |
| X-Shipzone-Delivery | Delivery id. It stays the SAME across retries — use it to avoid processing an event twice. |
Verify the signature
Anyone who knows your URL can send you a request. Processing data without verifying the signature leaves you open to a forged “delivered” event.
# Python (Flask)
import hashlib, hmac
from flask import Flask, request
GIZLI = "panelde bir kez gösterilen imza anahtarı"
app = Flask(__name__)
@app.post("/shipzone-kanca")
def kanca():
govde = request.get_data() # HAM GÖVDE — parse etmeyin
beklenen = "v1=" + hmac.new(
GIZLI.encode(), govde, hashlib.sha256).hexdigest()
if not hmac.compare_digest(beklenen,
request.headers.get("X-Shipzone-Signature", "")):
return "", 401 # imza tutmuyor
olay = request.headers["X-Shipzone-Event"]
# ... işinizi yapın ...
return "", 200 # 2xx = teslim alındıRetries
Any delivery that does not return 2xx is retried: 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours. Then we give up. If you return 410 Gone we give up on that event immediately.
`X-Shipzone-Delivery` stays the SAME across retries. If you see the same id twice, do not process it again — the network may simply have eaten your response.
Endpoints can disable themselves
After 20 consecutive failures we disable the endpoint; retrying a dead address forever delays events for endpoints that do work. The panel shows why, and saving a corrected URL enables it again.
Timeout
Your endpoint gets 10 seconds. Do not do slow work inside the request: queue the event on your side and return 2xx right away.