A Cloudflare-native reference implementation for receiving, verifying, deduplicating, and inspecting HioBuy Webhooks.
Live demo: https://webhooks.demo.hiobuy.com
HioBuy Developer Center: http://developers.hiobuy.com/
HioBuy
→ POST /api/webhooks/hiobuy
→ Verify signature
→ Deduplicate event.id
→ Handle event
→ Cloudflare D1
→ Demo UI
This project demonstrates a complete HioBuy webhook integration flow:
- Receive webhook events from HioBuy
- Preserve the raw request body for signature verification
- Verify the
HioBuy-Signatureheader - Reject stale or invalid webhook requests
- Deduplicate webhook deliveries using
event.id - Dispatch events to application handlers
- Store received events in Cloudflare D1
- Inspect test events through a simple developer dashboard
- Safely handle duplicate deliveries
- Keep production customer data out of the public demo
The project is intentionally small and easy to understand so it can also be used as a starting point for your own HioBuy integration.
- Next.js App Router + TypeScript
- vinext on Cloudflare Workers
- Cloudflare D1 for event storage
- pnpm
- Node.js 20+
The application is designed to run entirely on Cloudflare without requiring a traditional application server.
HioBuy
│
│ HTTPS Webhook
▼
POST /api/webhooks/hiobuy
│
▼
Read Raw Request Body
│
▼
Verify Timestamp
│
▼
Verify HMAC Signature
│
▼
Parse Event Payload
│
▼
Deduplicate event.id
│
┌────────┴────────┐
│ │
Duplicate New Event
│ │
▼ ▼
200 OK Event Handler
│
▼
Cloudflare D1
│
▼
GET /api/events
│
▼
Demo Dashboard
Install dependencies:
pnpm installCreate the local Cloudflare environment file:
cp .dev.vars.example .dev.varsAdd your HioBuy webhook signing secret:
HIOBUY_WEBHOOK_SECRET=whsec_replace_me
Apply the local D1 migrations:
pnpm d1:migrateStart the development server:
pnpm devOpen:
http://localhost:5173
The Recent Events panel will remain empty until a verified webhook has been received and stored in D1.
The repository includes a helper script for sending a correctly signed test webhook to the local receiver.
The test request must use the same HIOBUY_WEBHOOK_SECRET configured for the application.
Example:
HIOBUY_WEBHOOK_SECRET=whsec_replace_me pnpm send:testAfter sending the request, open the demo dashboard and the test event should appear under Recent Events.
You can then inspect:
- Event type
- Event ID
- Received time
- Signature verification result
- Processing status
- HTTP status
- Event payload
The main webhook endpoint is:
POST /api/webhooks/hiobuy
The receiver follows the HioBuy webhook verification protocol.
-
Read the raw request body.
-
Read and parse the
HioBuy-Signatureheader. -
Extract the webhook timestamp and signature.
-
Reject requests outside the allowed timestamp tolerance.
-
Compute the expected HMAC signature using the raw request body.
-
Compare signatures using a timing-safe comparison.
-
Parse the JSON payload only after signature verification succeeds.
-
Validate the webhook event envelope.
-
Deduplicate the event using
event.id. -
Dispatch new events to the appropriate application handler.
-
Store the event and processing result in Cloudflare D1.
-
Return the appropriate HTTP response.
The raw body must not be parsed and re-serialized before signature verification.
HioBuy sends a signature header in the following format:
HioBuy-Signature: t=<unix_seconds>,v1=<hex_hmac>
The signed payload is constructed from:
{timestamp}.{raw_body}
The expected signature is:
HMAC-SHA256(
HIOBUY_WEBHOOK_SECRET,
"{timestamp}.{raw_body}"
)
The implementation also validates the webhook timestamp using a 5-minute tolerance to help prevent replay attacks.
Signature verification lives in:
lib/hiobuy-webhooks.ts
Important:
Always verify the signature against the original raw HTTP request body.
Do not parse the JSON payload before verification.
See the official HioBuy Webhooks documentation for the current protocol specification.
Webhook delivery uses at-least-once delivery semantics.
This means the same event may be delivered more than once.
Applications should therefore never assume that a webhook event will only be received once.
This demo uses:
event.id
as the deduplication key.
Cloudflare D1 also enforces event ID uniqueness at the database level.
Conceptually:
Receive Event
│
▼
Check event.id
│
┌───┴────┐
│ │
Exists New
│ │
▼ ▼
200 OK Process
│
▼
Store
│
▼
200 OK
If an already accepted event is delivered again, it is not processed twice.
The receiver returns a successful response so HioBuy does not continue retrying an event the application has already accepted.
Event dispatch logic lives in:
lib/event-handler.ts
The handlers demonstrate how webhook events can trigger application business logic.
For example:
package.received
↓
Update package status
shipment.dispatched
↓
Update shipment status
shipment.delivered
↓
Mark shipment as delivered
balance.low
↓
Trigger balance warning
The demo handlers are intentionally simple.
In a real application, this is where you would connect HioBuy webhook events to your own:
- order system
- warehouse system
- ERP
- marketplace
- customer notifications
- shipment tracking
- billing logic
The demo includes examples for common procurement, warehouse, consolidation, shipping, and account events.
| Category | Example Events |
|---|---|
| Procurement | procurement.failed, procurement.out_of_stock |
| Warehouse | package.received, package.exception |
| Consolidation | consolidation.completed |
| Shipping | shipment.created, shipment.ready_for_payment, shipment.dispatched, shipment.delivered, shipment.exception |
| Account | balance.low |
This table represents events demonstrated by this repository.
For the complete and current event catalog, see the official HioBuy Webhooks documentation.
Received webhook events are stored in Cloudflare D1.
The database is intentionally simple.
Each event stores information such as:
Event ID
Event Type
Live/Test Mode
App ID
Payload
Received Time
Processing Status
Processed Time
HTTP Status
The event ID is unique and is also used for webhook deduplication.
Database migrations are located in:
migrations/
Apply migrations locally:
pnpm d1:migrateApply migrations to the production D1 database:
pnpm d1:migrate:remoteThe dashboard provides a lightweight webhook inspector.
It allows developers to:
Configure HioBuy Webhook
↓
Send Test Event
↓
Receive Webhook
↓
Verify Signature
↓
Process Event
↓
Inspect Event
The dashboard displays:
- Recent Events
- Event ID
- Event Type
- Test/Live Mode
- Received Time
- HTTP Status
- Processing Status
- JSON Payload
- Fulfillment Timeline
- Developer View
The goal is to make the webhook lifecycle visible while keeping the underlying implementation easy to understand.
For fulfillment-related events, the UI demonstrates how webhook events can update an application's order or shipment state.
Example:
Order Created
✓
│
Domestic Shipment
✓
│
Package Received
●
│
Consolidation
○
│
International Shipment
○
│
Dispatched
○
│
Delivered
○
The timeline is an educational representation of how an application can react to webhook events.
It should not be interpreted as a complete historical record unless the corresponding data actually exists.
The public demo is intentionally restricted to safe data.
GET /api/events
only returns webhook events where:
livemode: false
Production events are never displayed through the public event inspector.
Sensitive payload fields are also sanitized before event data is returned to the browser.
The application must never expose:
HIOBUY_WEBHOOK_SECRET- API keys
- Authorization headers
- Cloudflare credentials
- internal secrets
- private customer information
Webhook signature verification always happens server-side.
The signing secret is never sent to browser JavaScript.
Local development uses:
.dev.vars
Example:
HIOBUY_WEBHOOK_SECRET=whsec_replace_me
Never commit .dev.vars or real webhook secrets to Git.
The repository contains:
.dev.vars.example
for reference.
For production, store the signing secret using Cloudflare Workers Secrets.
pnpm wrangler d1 create hiobuy-developer-webhooks-demoCloudflare will return a database ID.
Add the returned database_id to:
wrangler.jsonc
pnpm d1:migrate:remotepnpm wrangler secret put HIOBUY_WEBHOOK_SECRETEnter the HioBuy webhook signing secret when prompted.
Do not place the production secret directly in wrangler.jsonc.
pnpm buildpnpm deployOptional production domain:
webhooks.demo.hiobuy.com
After deployment, configure the domain in Cloudflare and use the resulting webhook endpoint in HioBuy Developer Portal:
https://webhooks.demo.hiobuy.com/api/webhooks/hiobuy
| Script | Purpose |
|---|---|
pnpm dev |
Start the vinext development server |
pnpm build |
Build the production Cloudflare Worker |
pnpm start |
Run the built Worker locally |
pnpm deploy |
Deploy to Cloudflare Workers |
pnpm cf-typegen |
Generate Cloudflare Worker binding types |
pnpm d1:migrate |
Apply D1 migrations locally |
pnpm d1:migrate:remote |
Apply D1 migrations to production |
pnpm send:test |
Send a signed local test webhook |
app/
page.tsx
api/
webhooks/
hiobuy/
route.ts
events/
route.ts
lib/
hiobuy-webhooks.ts
event-handler.ts
event-store.ts
sanitize-event.ts
components/
WebhookEndpoint.tsx
RecentEvents.tsx
EventDetails.tsx
PayloadViewer.tsx
FulfillmentTimeline.tsx
IntegrationGuide.tsx
SignatureExample.tsx
migrations/
0001_create_webhook_events.sql
scripts/
send-test-webhook.ts
.dev.vars.example
wrangler.jsonc
package.json
README.md
The exact structure may evolve, but the project intentionally keeps webhook verification, event handling, storage, and UI concerns separated.
When implementing webhooks in your own application:
- Always use HTTPS.
- Always verify webhook signatures.
- Verify signatures against the original raw request body.
- Validate the webhook timestamp.
- Use
event.idfor deduplication. - Expect duplicate deliveries.
- Return successful responses only after the event has been safely accepted.
- Keep webhook secrets server-side.
- Never expose production webhook payloads through a public debugging interface.
- Treat webhook payloads as untrusted input even after signature verification.
- HioBuy Developer Center
- HioBuy Webhooks
- HioBuy Developer Documentation
- Next.js on Cloudflare Workers
- vinext
MIT
Built as part of the HioBuy Developer Center examples.