Webhooks

HIOBuy pushes fulfillment lifecycle events to your HTTPS endpoint when an app runs in warehouse fulfillment mode. Configure endpoints in the Developer Portal → Webhooks . Delivery is asynchronous; Public API responses never wait on your webhook server.

Prerequisite: Channel Auth → fulfillment mode = HIOBuy warehouse. Self-fulfillment apps can view the portal intro but cannot create or enable endpoints.

Overview {#overview}

ItemValue
TransportPOST JSON to your endpoint URL
Timeout10 seconds
SuccessHTTP 2xx
Signing secretwhsec_… (shown once at create / rotate)
Signature headerHioBuy-Signature
Event id headerHioBuy-Event-Id
User-AgentHioBuy-Webhooks/1.0
RetriesUp to 5 attempts: immediate, then 1m / 5m / 30m / 2h
Test eventsPortal Send testlivemode: false, id prefix evt_test_

Registration, secret rotation, delivery history, and manual retry are portal-only (session auth). There is no Public /v1/webhooks/* management API in v1.

Event catalog {#events}

Subscribe per endpoint. At least one event is required.

CategoryEvent typeWhen it fires
Procurementprocurement.failedWarehouse procurement failed
Procurementprocurement.out_of_stockSKU unavailable during procurement
Warehousepackage.receivedDomestic parcel received at warehouse
Warehousepackage.exceptionInbound package exception (exception_type in data)
Consolidationconsolidation.completedPackages consolidated for outbound
Shippingshipment.createdInternational shipment created
Shippingshipment.dispatchedShipment handed to carrier
Shippingshipment.deliveredDelivered to recipient
Shippingshipment.exceptionOutbound exception (exception_type in data)
Accountbalance.lowWarehouse wallet below threshold

Live warehouse→developer push is rolled out progressively; use Send test in the portal to validate your receiver today.

Public envelope {#envelope}

Every delivery body is a JSON object:

{
  "id": "evt_01HXYZ…",
  "type": "package.received",
  "created_at": "2026-08-16T04:00:00.000Z",
  "livemode": true,
  "app_id": "app_…",
  "data": {
    "package_id": "pkg_…",
    "order_id": "ord_…",
    "status": "received"
  }
}
FieldTypeDescription
idstringPublic event id (evt_… live, evt_test_… test)
typestringEvent type from the catalog
created_atstringISO-8601 timestamp
livemodebooleanfalse for portal test events
app_idstringYour app id
dataobjectEvent-specific payload (no warehouse-internal fields)

Example data shapes {#data-shapes}

Shapes below match portal test samples; live payloads follow the same keys with production ids.

procurement.failed

{
  "order_id": "ord_…",
  "status": "failed",
  "reason": "supplier_rejected"
}

package.exception

{
  "package_id": "pkg_…",
  "order_id": "ord_…",
  "status": "exception",
  "exception_type": "DAMAGED"
}

shipment.dispatched

{
  "shipment_id": "shp_…",
  "tracking_number": "…",
  "status": "dispatched"
}

balance.low

{
  "available_amount": 12.5,
  "currency": "USD",
  "threshold": 50
}

HTTP delivery {#delivery}

POST /your/webhook-path HTTP/1.1
Host: your-server.example
Content-Type: application/json
User-Agent: HioBuy-Webhooks/1.0
HioBuy-Event-Id: evt_01HXYZ…
HioBuy-Signature: t=1723780800,v1=abcdef0123456789…
  • Endpoint URL must be HTTPS in production. Non-production environments may use http://localhost / tunnel hosts for local debugging.
  • Respond with 2xx within 10s. Any other status or network error schedules a retry (until attempts are exhausted).
  • Treat deliveries as at-least-once. Deduplicate on id (and optionally HioBuy-Event-Id).

Signature verification {#signature}

  1. Read HioBuy-Signature (t=<unix_seconds>,v1=<hex_hmac>).
  2. Build the signed payload: "{t}.{raw_body}" where raw_body is the exact request body bytes (UTF-8 JSON string as received).
  3. Compute HMAC-SHA256(secret, signed_payload) and hex-encode the digest.
  4. Compare to v1 with a constant-time equality check.
  5. Reject if |now - t| is too large (recommended tolerance: 5 minutes) to limit replay.

Node.js example

import crypto from "node:crypto";
 
function verifyHioBuySignature(rawBody, signatureHeader, secret, toleranceSec = 300) {
  const match = /^t=(\d+),v1=([0-9a-f]+)$/i.exec(signatureHeader || "");
  if (!match) return false;
 
  const t = Number(match[1]);
  const expected = match[2].toLowerCase();
  if (!Number.isFinite(t)) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceSec) return false;
 
  const signed = `${t}.${rawBody}`;
  const digest = crypto
    .createHmac("sha256", secret)
    .update(signed, "utf8")
    .digest("hex");
 
  const a = Buffer.from(digest, "utf8");
  const b = Buffer.from(expected, "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Store the whsec_… secret only on your server. Portal lists show a masked hint (whsec_••••… + last 6 characters). Rotate anytime; update your server before the next delivery.

Retries & delivery status {#retries}

AttemptDelay after previous failure
1Immediate (queued)
21 minute
35 minutes
430 minutes
52 hours

After the final failed attempt the delivery is marked failed. You can Retry now from the portal delivery detail for a manual re-queue.

Delivery statuses: pending · success · retrying · failed.

Portal workflow {#portal}

  1. Set the app to warehouse fulfillment under Channel Auth .
  2. Open Webhooks Add endpoint.
  3. Enter HTTPS URL, optional description, and subscribe to events.
  4. Copy the signing secret when shown (once).
  5. Use Send test event and inspect recent deliveries / attempt history.
  6. Rotate secret, disable, or delete when needed.

Limits & retention {#limits}

LimitValue
Payload size≤ 64 KB
Attempt response body stored≤ 4 KB
Event / delivery retention~30 days
Attempt retention~14 days

Get Support

Need integration help? Contact Developer Support at support@hiobuy.com · Response within 1–2 business days

Email support