Skip to main content

Webhooks

Webhooks let your application receive real-time notifications when events happen in your store — instead of polling the API for changes. When an order is placed, paid, fulfilled, or updated, TakeTheme sends an HTTP POST request to a URL you control with a JSON description of the event.

Use webhooks to keep external systems in sync with TakeTheme: update an ERP or accounting system when orders are paid, trigger fulfillment in a warehouse platform, notify a Slack channel, or refresh a customer-facing dashboard.

How it works

  1. You register one or more endpoints — HTTPS URLs on your server that will receive events.
  2. For each endpoint you choose which events to subscribe to (for example, order.paid).
  3. When a subscribed event occurs, TakeTheme delivers a signed JSON payload to your endpoint.
  4. Your endpoint verifies the signature, does its work, and responds with a 2xx status code.
  5. If your endpoint is unreachable or returns a non-2xx status, TakeTheme retries with exponential backoff.
  Store event                TakeTheme                    Your server
───────────── ───────────────────────── ───────────────────────
order.paid ───▶ sign + POST payload ───▶ verify signature
process event
mark delivered ◀─── respond 200 OK

Requirements

Plan requirement

Webhooks are available on plans that include webhook access (canAccessWebhooks). If your current plan doesn't include webhooks, endpoint management requests return 403 Forbidden. See your Dashboard billing page to upgrade.

Your receiving endpoint must:

  • Be served over HTTPS — plain http:// URLs are rejected.
  • Be publicly reachable. Internal or private IP addresses (loopback, link-local, private ranges) are blocked to prevent SSRF.
  • Not redirect. Redirects (3xx) are not followed — respond at the endpoint URL directly.
  • Respond within 10 seconds with a 2xx status code.

Available events

TakeTheme currently emits order lifecycle events:

EventSent when…
order.placedA new order is created.
order.paidAn order's payment is captured / marked as paid.
order.fulfilledAn order (or the remaining items) is marked as fulfilled.
order.cancelledAn order is cancelled.
order.returnedAn order is marked as returned.
order.refundedAn order is refunded.
order.updatedAn order is edited or its status is reconciled (debounced — see below).

Each endpoint subscribes to at least one event. See the Events Reference for the full payload of each event.

order.updated is debounced

A single admin action (editing an order, reconciling fulfillment or payment) can emit many internal order.updated signals. TakeTheme collapses a burst into one order.updated delivery per order within a short window (~5 seconds). Lifecycle events (order.paid, order.fulfilled, …) are never debounced — you receive one delivery per occurrence.

Payload structure

Every webhook body is a JSON envelope with the same top-level shape, regardless of event type:

{
"id": "3f1c2b9e-8a4d-4c7e-9b1a-2d6f8e0c1a34",
"type": "order.paid",
"created": 1751371200.123,
"data": {
"object": {
"id": "665f1b2c9a3e4d0012ab34cd",
"status": "open",
"paymentStatus": "paid",
"fulfillmentStatus": "unfulfilled",
"totalPrice": 349.99,
"currency": "EGP",
"customer": { "...": "..." },
"items": [ { "...": "..." } ],
"shippingAddress": { "...": "..." },
"createdAt": "2026-07-01T12:00:00.000Z"
}
}
}
FieldTypeDescription
idstringUnique delivery/event ID (UUID). Matches the X-TakeTheme-Delivery header. Use it to deduplicate.
typestringThe event type, e.g. order.paid.
creatednumberUnix timestamp (seconds, with fractional part) of when the event was generated.
data.objectobjectThe resource the event relates to — for order events, the order object.
created is not the signature timestamp

The created field describes when the event occurred. The timestamp used to verify the signature is carried separately in the X-TakeTheme-Signature header (t=…). Always verify against the header timestamp, not created. See Verifying Signatures.

Quick start

1. Create an endpoint

Register an HTTPS URL and subscribe it to events. This can be done from the Dashboard (Settings → Webhooks) or via the API:

curl -X POST "https://api.taketheme.com/api/v1/store/webhooks/endpoints" \
-H "tt-api-key: tt_xxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/taketheme",
"events": ["order.placed", "order.paid", "order.fulfilled"],
"description": "Production order sync"
}'

The response includes a one-time signing secret (whsec_…). Store it securely — it's shown only once:

{
"id": "665f1b2c9a3e4d0012ab34cd",
"url": "https://example.com/webhooks/taketheme",
"events": ["order.placed", "order.paid", "order.fulfilled"],
"isActive": true,
"secret": "whsec_3a7f...c9d2"
}

2. Receive and verify

Your endpoint should verify the signature before trusting the payload:

import express from "express";
import crypto from "node:crypto";

const app = express();
const SIGNING_SECRET = process.env.TAKETHEME_WEBHOOK_SECRET; // whsec_...

// Capture the RAW body — signature is computed over the exact bytes sent.
app.post(
"/webhooks/taketheme",
express.raw({ type: "application/json" }),
(req, res) => {
const header = req.get("X-TakeTheme-Signature");
const rawBody = req.body; // Buffer

if (!verifySignature(rawBody, header, SIGNING_SECRET)) {
return res.status(400).send("Invalid signature");
}

const event = JSON.parse(rawBody.toString("utf8"));

// Respond fast, then do the heavy lifting asynchronously.
res.status(200).send("ok");

switch (event.type) {
case "order.paid":
// fulfill(event.data.object)
break;
// ...
}
}
);

The full verifySignature implementation (and versions in Python, PHP, Ruby, and Go) is in Verifying Signatures.

Design principles

  • At-least-once delivery. A delivery may occasionally arrive more than once (for example, if your server responds slowly and TakeTheme retries). Make your handler idempotent by deduplicating on the event id / X-TakeTheme-Delivery header.
  • No strict ordering. Events are delivered as they happen, but network retries mean they may arrive out of order. Reconcile using the fields on the payload (e.g. paymentStatus) rather than assuming order.
  • Respond quickly. Acknowledge with 2xx immediately and defer slow work (database writes, third-party calls) to a background job. TakeTheme treats a response slower than 10 seconds as a failure.
  • Always verify. Never act on an unverified payload — anyone who learns your URL could otherwise forge events.

Next steps