Skip to main content

Overview

Webhooks allow you to receive real-time notifications when specific events occur in the Zochil API. Instead of polling our API for changes, webhooks push data to your application as events happen.

How Webhooks Work

  1. Configure: Set up webhook endpoints in your application
  2. Subscribe: Register your webhook URL for specific events
  3. Receive: Get instant notifications when events occur
  4. Process: Handle the webhook payload in your application

Webhook Events

User Events

EventDescription
user.createdNew user registration
user.updatedUser profile changes
user.deletedUser account deletion
user.verifiedEmail verification completed

Order Events

EventDescription
order.createdNew order placed
order.updatedOrder status changed
order.cancelledOrder cancelled
order.completedOrder fulfilled
order.payment_succeededPayment processed successfully
order.payment_failedPayment processing failed

Product Events

EventDescription
product.createdNew product added
product.updatedProduct information changed
product.deletedProduct removed
inventory.updatedProduct inventory changed

Cart Events

EventDescription
cart.item_addedItem added to cart
cart.item_removedItem removed from cart
cart.abandonedCart abandoned (no activity for 24h)

Setting Up Webhooks

1. Create a Webhook Endpoint

Create an HTTP endpoint in your application to receive webhook notifications:
const express = require("express");
const crypto = require("crypto");
const app = express();

app.use(express.json());

app.post("/webhooks/zochil", (req, res) => {
  const signature = req.get("X-Zochil-Signature");
  const payload = JSON.stringify(req.body);

  // Verify webhook signature (recommended)
  if (!verifySignature(payload, signature)) {
    return res.status(401).send("Unauthorized");
  }

  const { event, data } = req.body;

  // Handle the event
  switch (event) {
    case "user.created":
      console.log("New user:", data.user);
      // Send welcome email, create user profile, etc.
      break;

    case "order.created":
      console.log("New order:", data.order);
      // Update inventory, send confirmation, etc.
      break;

    default:
      console.log("Unhandled event:", event);
  }

  res.status(200).send("OK");
});

function verifySignature(payload, signature) {
  const webhookSecret = process.env.ZOCHIL_WEBHOOK_SECRET;
  const expectedSignature = crypto
    .createHmac("sha256", webhookSecret)
    .update(payload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(`sha256=${expectedSignature}`)
  );
}

app.listen(3000);

2. Register Your Webhook

Register your webhook endpoint using the Admin API:
curl -X POST "https://api.zochil.io/admin/webhooks" \
  -H "access-token: YOUR_ACCESS_TOKEN" \
  -H "device-id: YOUR_DEVICE_ID" \
  -H "merchant-id: YOUR_MERCHANT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/zochil",
    "events": ["user.created", "order.created", "order.updated"],
    "active": true,
    "secret": "your-webhook-secret-key"
  }'

Webhook Payload Structure

All webhook payloads follow this structure:
{
  "id": "evt_1234567890abcdef",
  "event": "user.created",
  "created": 1640995200,
  "data": {
    "user": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "email": "[email protected]",
      "name": "John Doe",
      "created_at": "2023-01-01T00:00:00Z"
    }
  },
  "api_version": "v1"
}

Payload Fields

Example Event Payloads

User Created Event

{
  "id": "evt_user_created_123",
  "event": "user.created",
  "created": 1640995200,
  "data": {
    "user": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "email": "[email protected]",
      "name": "John Doe",
      "phone": "+1234567890",
      "is_verified": false,
      "created_at": "2023-01-01T00:00:00Z"
    }
  },
  "api_version": "v1"
}

Order Created Event

{
  "id": "evt_order_created_456",
  "event": "order.created",
  "created": 1640995200,
  "data": {
    "order": {
      "id": "order_123e4567e89b12d3",
      "order_number": "ORD-2023-001",
      "user_id": "123e4567-e89b-12d3-a456-426614174000",
      "status": "pending",
      "total": 299.99,
      "currency": "USD",
      "items": [
        {
          "product_id": "prod_123",
          "quantity": 2,
          "price": 149.99
        }
      ],
      "created_at": "2023-01-01T00:00:00Z"
    }
  },
  "api_version": "v1"
}

Inventory Updated Event

{
  "id": "evt_inventory_updated_789",
  "event": "inventory.updated",
  "created": 1640995200,
  "data": {
    "product": {
      "id": "prod_123e4567e89b12d3",
      "sku": "PWH-001",
      "name": "Premium Wireless Headphones"
    },
    "inventory": {
      "previous_quantity": 100,
      "current_quantity": 95,
      "reserved": 5,
      "available": 90
    }
  },
  "api_version": "v1"
}

Security

Signature Verification

All webhooks include an X-Zochil-Signature header for verification:
const crypto = require("crypto");

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(payload, "utf8")
    .digest("hex");

  const receivedSignature = signature.replace("sha256=", "");

  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(receivedSignature)
  );
}

IP Whitelisting

Webhook requests come from these IP ranges:
198.51.100.0/24
203.0.113.0/24

Handling Failures

Retry Logic

If your webhook endpoint returns a non-2xx status code, we’ll retry:
  • Immediate retry: After 1 second
  • Exponential backoff: 5s, 25s, 125s, 300s
  • Maximum attempts: 5 retries
  • Timeout: 30 seconds per attempt

Failed Webhook Dashboard

Monitor failed webhooks in your admin dashboard:
GET /admin/webhooks/{webhook_id}/deliveries

Best Practices

1. Idempotency

Handle duplicate webhook deliveries gracefully:
const processedEvents = new Set();

app.post("/webhooks/zochil", (req, res) => {
  const eventId = req.body.id;

  // Check if we've already processed this event
  if (processedEvents.has(eventId)) {
    return res.status(200).send("Already processed");
  }

  // Process the event
  handleEvent(req.body);

  // Mark as processed
  processedEvents.add(eventId);

  res.status(200).send("OK");
});

2. Async Processing

Process webhooks asynchronously to respond quickly:
const Queue = require("bull");
const webhookQueue = new Queue("webhook processing");

app.post("/webhooks/zochil", (req, res) => {
  // Add to queue for async processing
  webhookQueue.add("process-webhook", req.body);

  // Respond immediately
  res.status(200).send("OK");
});

webhookQueue.process("process-webhook", (job) => {
  const { event, data } = job.data;
  // Process the webhook data
  return handleWebhookEvent(event, data);
});

3. Error Handling

Log errors and return appropriate status codes:
app.post("/webhooks/zochil", async (req, res) => {
  try {
    await processWebhook(req.body);
    res.status(200).send("OK");
  } catch (error) {
    console.error("Webhook processing failed:", error);

    // Return 5xx for temporary failures (will be retried)
    if (error.temporary) {
      return res.status(503).send("Temporary failure");
    }

    // Return 4xx for permanent failures (won't be retried)
    res.status(400).send("Invalid webhook data");
  }
});

Testing Webhooks

Local Development

Use tools like ngrok to expose your local server:
# Install ngrok
npm install -g ngrok

# Expose your local server
ngrok http 3000

# Use the HTTPS URL for webhook registration
# https://abc123.ngrok.io/webhooks/zochil

Webhook Testing Tool

Create a simple webhook tester:
const express = require("express");
const app = express();

app.use(express.json());

app.post("/test-webhook", (req, res) => {
  console.log("Received webhook:");
  console.log("Headers:", req.headers);
  console.log("Body:", JSON.stringify(req.body, null, 2));

  res.status(200).send("Webhook received");
});

app.listen(3000, () => {
  console.log("Webhook tester running on port 3000");
});

Managing Webhooks

List Webhooks

GET /admin/webhooks

Update Webhook

PATCH /admin/webhooks/{webhook_id}

Delete Webhook

DELETE /admin/webhooks/{webhook_id}

Test Webhook

Trigger a test webhook delivery:
POST /admin/webhooks/{webhook_id}/test

Next Steps