Skip to Content
DocumentationDropshippingWebhooks and key rotation

Dropship webhooks and key rotation

Episode AFF 14 · Webhooks, retries and key rotation · 2:05 · KeepKey, step by step

Use signed webhooks to follow order changes, with order lookup as a fallback. An HTTP delivery and a customer-facing storefront update are separate operations; verify and reconcile before changing the store.

Register your endpoint

  1. Open Dropship in your approved account.
  2. Enter a public HTTPS URL on your store’s backend.
  3. Choose Save & generate secret.
  4. Store the signing secret privately before leaving the page; it is shown once. Keep it separate from test and live API tokens.
  5. Configure the receiver to read the original request body and verify its signature before parsing or processing the event.

The delivery uses these headers:

HeaderPurpose
X-KeepKey-Signaturet=<unix timestamp>,v1=<hex HMAC>
X-KeepKey-Webhook-IdDelivery identifier for deduplication
X-KeepKey-EventEvent type; reconcile it with the identified order

Verify before parsing

The signature is HMAC-SHA256 with the signing secret over timestamp + "." + rawBody. Preserve the received body bytes; parsing and reserializing JSON can change the signature input. Reject invalid signatures and timestamps outside the five-minute tolerance.

This Node.js example demonstrates the verification step. Pass the raw body and header received by your framework; it does not register a route or persist delivery IDs:

import { createHmac, timingSafeEqual } from 'node:crypto' export function verifyKeepKeyWebhook(secret, header, rawBody, now = Math.floor(Date.now() / 1000)) { if (!secret || typeof header !== 'string' || !Buffer.isBuffer(rawBody)) return false const parts = Object.fromEntries(header.split(',').map(part => part.trim().split('='))) const timestamp = Number(parts.t) if (!Number.isInteger(timestamp) || timestamp <= 0 || Math.abs(now - timestamp) > 300) return false if (!/^[a-f0-9]{64}$/i.test(parts.v1 ?? '')) return false const expected = createHmac('sha256', secret) .update(`${timestamp}.`) .update(rawBody) .digest() const received = Buffer.from(parts.v1, 'hex') return received.length === expected.length && timingSafeEqual(received, expected) }

After verification, durably record or enqueue the delivery and deduplicate X-KeepKey-Webhook-Id before applying changes. Return success after your receiver has safely accepted the event. Repeated deliveries must not apply a storefront change twice. An authenticated payload still needs to match the intended account and order.

Retain a lookup fallback

Deliveries can be late, repeated or missing. Use GET /orders with the known KeepKey or external order ID to reconcile an unexpected state or recover after downtime. Do not infer shipment from an accepted create request, a successful webhook HTTP response or null tracking fields.

For order creation, retries use the same external ID and identical payload. Handle conflicts and permanent rejections explicitly; use bounded backoff for temporary transport errors.

Rotate credentials deliberately

Rotate API keys replaces the live and test tokens together. The previous tokens stop working, so update the intended backend configurations and verify a harmless authenticated read before relying on live order submission.

Update & regenerate secret changes the webhook signing secret. Update your receiver as part of that change; it is a separate secret from the order API tokens. Review pending deliveries and reconcile through order lookup after a credential change or outage.

Test the receiver, deduplication, lookup fallback and credential replacement before scaling live automation. Your recording or practice request should use fictional customer data and non-payable values; actual customer information belongs only in the authorized fulfillment workflow.

Last updated on