Webhooks Overview
Understand how Whatalo delivers real-time event notifications to your plugin and how to declare webhook subscriptions in your manifest.
Webhooks notify your plugin of events as they happen in a merchant's store. When a new order is placed, a product is updated, or a customer registers, Whatalo sends an HTTP POST request to your webhookUrl with a structured JSON payload.
How It Works
- Declare events in your
whatalo.app.tsmanifest - Set your
webhookUrl— Whatalo sends all events here - Verify signatures on every incoming request
- Process the event payload and respond with
200 OKwithin 15 seconds
Manifest Declaration
// whatalo.app.ts
import { defineApp } from "@whatalo/plugin-sdk";
export default defineApp({
name: "My Plugin",
pluginId: "my-plugin",
webhookUrl: "https://my-plugin.com/api/webhooks",
webhooks: [
{ event: "order.created", description: "Track new orders for fulfilment" },
{ event: "order.updated", description: "Sync order changes" },
{ event: "product.updated", description: "Sync catalog changes" },
{ event: "checkout.completed", description: "React to completed checkouts" },
],
// ... rest of manifest
});Only declare the events your plugin actually handles. Declaring unused events creates unnecessary load and confuses merchants reviewing your plugin's permissions.
HTTP Delivery Details
Every webhook delivery is a POST request with these characteristics:
| Property | Value |
|---|---|
| Method | POST |
| Content-Type | application/json |
| User-Agent | Whatalo-Webhooks/1.0 |
| Timeout | 15 seconds |
Request Headers
| Header | Description |
|---|---|
X-Webhook-Id | Unique delivery identifier — use for idempotency checks |
X-Webhook-Event | Event type (e.g., order.created) |
X-Webhook-Signature | HMAC-SHA256 signature for verification |
X-Webhook-Timestamp | Unix timestamp of when the signature was generated |
Payload Structure
Webhook payload bodies contain event-specific data. The event name is delivered in X-Webhook-Event, not in the JSON body:
{
"event_id": "ord_abc123",
"occurred_at": "2026-03-01T14:30:00.000Z",
"order": {
"id": "ord_abc123",
"status": "pending"
},
"store": {
"id": "sto_abc123",
"name": "My Store",
"timezone": "America/Santo_Domingo"
}
}Response Requirements
Your endpoint must respond with HTTP 200 OK within 15 seconds. Any other status code or a timeout is treated as a delivery failure.
Whatalo retries failed deliveries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 second |
| 2nd retry | 2 seconds |
Failed deliveries are attempted up to 3 times total. The configured delay list is 1s, 2s, 4s, but only the 1s and 2s delays occur between the 3 attempts.
Idempotency
Because deliveries can be retried, your handler must be idempotent — processing the same event twice must not produce duplicate side effects.
async function handleOrderCreated(payload: WebhookPayload, context: WebhookHandlerContext) {
const orderId = payload.order.id;
const deliveryId = context.deliveryId; // X-Webhook-Id value
// Check if this delivery has already been processed
const existing = await db.processedEvents.findUnique({
where: { deliveryId },
});
if (existing) {
return; // Already processed — skip
}
// Process the order
await fulfilOrder(orderId);
// Record that this delivery was processed
await db.processedEvents.create({ data: { deliveryId } });
}Next Steps
- Handling Webhooks — SDK handlers for Next.js, Hono, and Express
- Verification & Security — Signature verification and replay protection
- Event Reference — All public webhook events