Skip to main content

Receive a webhook once in your business system

Return events use Developer Platform subscriptions and their signing secret. Address-confirmation callbacks can instead have a per-request signing secret. Do not mix those two secrets. Subscribe to exact event types available in the Developer Console; do not assume wildcard subscription support.

Verify before parsing​

Alkax sends X-Alkax-Event-Id, X-Alkax-Timestamp (Unix seconds), and X-Alkax-Signature (v1= followed by lowercase hex HMAC-SHA256). The signed input is UTF-8 timestamp, a period, and the exact raw request-body bytes. Use the signing secret string as the HMAC key. Do not base64-decode it or reserialize JSON first. Reject stale timestamps using a five-minute freshness window, future timestamps outside that window, malformed signatures and mismatched envelope/header event IDs. Keep clocks synchronized. Use constant-time comparison.

Expose your receiver through a public HTTPS endpoint. Alkax will not send to loopback, private, link-local or DNS-resolved private destinations, and redirects are not followed. Keep signing secrets out of source code, logs and public configuration.

Framework pitfalls: Express must capture express.raw() before JSON middleware; ASP.NET must buffer/read the untouched request body before model conversion; Flask should use request.get_data() before parsing; proxies must not rewrite body bytes. Forward all three Alkax headers. Never validate against a prettified payload.

Durable acceptance and duplicates​

Verify first. In one database transaction, persist the environment/event identity and raw-body hash under a unique constraint, then record the business action or enqueue durable work. Commit before returning 2xx. A crash after commit but before the response causes a duplicate: acknowledge it without applying the action again only when its stored body hash matches. A changed body under the same identity is an integrity failure; apply no state changes. Fetch current canonical state with your environment key before making ordering-sensitive decisions. Delivery order is not guaranteed. Keep deduplication records for at least your supported replay lifetime.

Do not call an external refund service inside an unprotected retry loop. Use your own transactional outbox and idempotency contract. Alkax's return.received is physical receipt, not a refund authorization.

Delivery policy​

Both modern webhook senders use a ten-second timeout and up to eight automatic attempts. Project-subscription retries use delays of 2, 4, 8, 16, 32, 64 and 128 minutes. Per-confirmation callbacks use approximately 1, 2, 4, 8, 16, 32 and 64 minutes with jitter. These schedules belong to different senders. Non-success 4xx responses are terminal except 408, 425 and 429; 5xx and transport failures retry. Return 503 for transient storage failure and 2xx for durably accepted duplicates. Do not return 200 before storage succeeds. Permanent bad signatures require repairing the secret/body handling before replay, not repeated blind delivery.

In Developer Console, inspect the subscription's delivery history, correct the cause, and select Replay. Replay preserves event ID and payload, but signs with a fresh timestamp. Deduplication must therefore key on event ID, not signature or timestamp.

Keep subscription secrets in a secret manager. Credential rotation does not rotate webhook secrets. To replace a subscription secret, create the replacement subscription, accept both secrets during transition, retire the old subscription and reconcile pending deliveries.

Payload handling​

A return envelope has eventId, type, occurredAt and data. The data contains return/original shipment identifiers, tracking number, authorization/logistics state, reason/type and event time. Treat merchantReference as merchant-supplied potentially sensitive data. Do not log the full envelope. There are no recipient capabilities, precise addresses or proof images in this public integration event.

Alkax -> signed HTTPS webhook -> verify raw bytes -> durable inbox/work -> 2xx
|
+-> transient failure -> safe retry

Confirmation contracts and recovery​

The HTTP confirmation API, legacy per-confirmation callbacks, versioned per-confirmation callbacks and generic project subscriptions have distinct schemas:

ChannelFields and status
Authenticated HTTP confirmation responsecamelCase fields, string status; private snapshot and deliveryInstructions; one-time fields null on ordinary reads
Per-confirmation v1 defaulttop-level eventId, type, occurredAt, request; PascalCase nested fields and numeric status
Per-confirmation opt-in v2top-level schemaVersion, eventId, type, occurredAt, request; camelCase request ID/reference/string status/expiry only
Project subscriptiontop-level eventId, type, occurredAt, data; confirmation data only id, numeric status, expiresAt

The signature header's v1= denotes the HMAC scheme; it does not select the JSON schema. Select optional callbackSchemaVersion when creating a new confirmation. Queued event bytes and selected schema remain unchanged on replay. Per-confirmation manual replay retains the lifetime attempt count; it does not promise a fresh eight-attempt budget.

For response-loss recovery, generate and durably encrypt an optional callbackSigningSecret before creating the confirmation. It must be exactly 64 lowercase hexadecimal characters generated from 32 random bytes, with a configured callbackUrl. HMAC uses the UTF-8 secret text. Alkax never echoes a supplied secret. If omitted, Alkax generates a webhookSigningSecret and returns it only in the initial create response. There is no secret rotation or reprovision endpoint.

An identical create retry returns the original request, with null recipientUrl and one-time secret fields, even after the original link deadline. Preserve the exact payload/key and use a new opaque reference for a new attempt. Validate header/body event identity, correlate the current local attempt, persist environment/event ID plus body hash, and reconcile through authenticated canonical GET. Link expiry does not invalidate completed snapshots; consent and disclosure expiry belong to the consuming application.

Project delivery-history and test responses expose only allowlisted delivery metadata. Raw payloads, receiver excerpts and free-text failures are excluded for every project role. Unsafe historical confirmation project events are retired before sending and denied on manual replay; retained historical secrets still require scoped operational remediation. This does not rotate previously exposed capabilities or secrets.

Sandbox confirmation notifications go to a sink rather than real recipient inboxes, and all confirmation operations are scoped to project/environment. Deploy the compatible provider release before using these additions. See the recoverable callback contract for the exact payload fixture and remaining release gates. The private VolXpr hosted UI, durable authorization journey and full lifecycle concurrency work remain separate.