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.status_changed", description: "Sync status updates" },
{ event: "product.updated", description: "Sync catalog changes" },
{ event: "app.installed", description: "Provision merchant resources" },
{ event: "app.uninstalled", description: "Clean up merchant data" },
],
// ... 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
All webhook payloads share the same envelope:
{
"id": "evt_01HX4KQMZ7B9XVNR2T6WQFG3P",
"event": "order.created",
"store": "my-store",
"timestamp": "2024-03-01T14:30:00Z",
"data": {
// Event-specific data
}
}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 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
After 4 failed retries, the delivery is abandoned and logged.
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) {
const orderId = payload.data.id;
const deliveryId = payload.id; // 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 13 available events