Skip to content

Webhooks

We post every status change to the URL you register with us. This is the primary channel; polling is the fallback for when your endpoint was unreachable.

Envelope

http
POST https://your-domain.example/hooks/hizliyo
X-Hizliyo-Signature: sha256=4f7c1e9a2b8d0356f1a4c7e93b2d5087...
X-Hizliyo-Event: delivery.status_changed
X-Hizliyo-Attempt: 1
Content-Type: application/json
json
{
  "eventId": "01J9XK4M2T7QW3ZR8B6N5FVCDA",
  "event": "delivery.status_changed",
  "occurredAt": "2026-08-04T19:03:22+03:00",
  "data": {
    "orderId": "e5a9f0d3-7c21-4b88-a06e-3f9182bd4471",
    "externalOrderId": "ADS-ORD-99213",
    "status": "PickedUp",
    "courier": {
      "name": "Emre K.",
      "phone": "5307654321",
      "vehicleType": "Motorcycle"
    },
    "location": { "latitude": 39.921004, "longitude": 32.855210 },
    "etaDropoffAt": "2026-08-04T19:14:00+03:00"
  }
}

The data object is byte-for-byte the same shape returned by GET /orders/{orderId} — write one parser, use it in both places.

Courier surnames are abbreviated. We pass on the minimum needed to complete the delivery.

Event types

EventWhenPayload
delivery.status_changedAny status transitionOrder and courier state
store.activatedFleet confirmed the storeStore id and fleet
store.migratingBranch is becoming a Hızlıyo partnerStore id and closing date
store.deactivatedStore closedStore id and reason: MigratedToHizliyo, FleetSuspended, VendorRequest
fleet.payment_overdueThe store's fleet has an overdue balanceStore ids at risk

Ignore what you do not know

New event types may be added without a version bump. Treat an unrecognised X-Hizliyo-Event as a no-op and return 2xx — do not fail the request.

Verifying the signature

Compute an HMAC-SHA256 of the raw request body with your webhook secret and compare it to the header in constant time. Never parse the body before verifying it.

js
import crypto from 'node:crypto';

function verify(rawBody, headerValue, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody, 'utf8')
    .digest('hex');

  const a = Buffer.from(headerValue ?? '');
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
csharp
public static bool Verify(string rawBody, string headerValue, string secret)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody));
    var expected = "sha256=" + Convert.ToHexString(hash).ToLowerInvariant();

    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(headerValue ?? string.Empty),
        Encoding.UTF8.GetBytes(expected));
}

Use the raw body

Serialising the parsed JSON back to a string changes whitespace and key order, and the signature will never match. Capture the raw bytes before any middleware touches them.

Responding

Return 2xx as soon as you have stored the event. Do the real work afterwards — a webhook handler that waits on your database or on a third party will time out and cause a retry.

Any non-2xx or a timeout counts as a failure.

Retries

Failed deliveries are retried with exponential backoff for up to 24 hours. X-Hizliyo-Attempt tells you which attempt this is. After 24 hours we stop and flag the endpoint as unreachable in our panel.

If your endpoint was down, catch up by querying the affected orders — see Order status.

This is not the same as order retries

We retry outbound notifications because the package is already on its way and you need to know. We never retry your inbound order request — see Orders.

Duplicates and ordering

Events carry a unique eventId. Under retry conditions the same event may arrive more than once — deduplicate on eventId.

Order is not guaranteed. Use occurredAt, and ignore a status that is behind the one you have already recorded for that order.

Hızlıyo Vendor API v1