Skip to main content

API & webhooks

Premium brands can receive signed JSON webhooks when orders are paid or fulfilled on Drape. Configure the endpoint in the brand portal under Integrations.

There is no public third-party REST catalog API yet. Day-to-day catalog, orders, and analytics stay in the brand portal / app; webhooks cover outbound order events for your own systems (ERP, fulfillment, Slack bots, etc.).

Prerequisites

RequirementDetail
PlanPremium (custom_integrations entitlement)
Portalbrand.drape.to/integrations
HTTPS endpointMust accept POST with JSON body
ResponseReturn 2xx within ~10 seconds

Configure in the portal

  1. Open Integrations.
  2. Under Order webhooks, set your endpoint URL.
  3. Select events (order.paid, order.fulfilled).
  4. Save — copy the signing secret when it is shown (create / rotate only).
  5. Keep the webhook active.

Rotate the secret anytime with Rotate secret, then update your verifier.

Events

EventWhen it fires
order.paidShopper payment succeeds for an order that includes your store’s lines
order.fulfilledYou mark the order shipped / fulfilled in Drape

Default subscription when events are omitted: both of the above.

Delivery

Drape POSTs to your URL with:

HeaderValue
Content-Typeapplication/json
X-Drape-EventEvent name, e.g. order.paid
X-Drape-SignatureHex HMAC-SHA256 of the raw body using your signing secret

Timeout: 10 seconds. Non-2xx or network errors are logged as failed deliveries; retries are not guaranteed yet — treat handlers as idempotent.

Envelope shape

{
"id": "9f3c2a1b-…",
"type": "order.paid",
"created_at": "2026-09-10T13:00:00.000000Z",
"store_id": "c373fbc1-…",
"data": {
"order": {
"id": "…",
"store_id": "…",
"status": "paid",
"total": 189.0
}
}
}

data.order is the serialized order row for your store (UUIDs as strings, datetimes as ISO-8601). Fields may grow over time — ignore unknown keys.

Verify the signature

Compute HMAC-SHA256 over the exact request body bytes with your secret and compare to X-Drape-Signature (hex digest).

Node.js

import crypto from "node:crypto";

function verifyDrapeSignature(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(signatureHeader || "", "utf8");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express: use express.raw({ type: "application/json" }) for this route
app.post("/webhooks/drape", (req, res) => {
const ok = verifyDrapeSignature(
req.body,
req.get("X-Drape-Signature"),
process.env.DRAPE_WEBHOOK_SECRET,
);
if (!ok) return res.status(401).send("invalid signature");
const event = JSON.parse(req.body.toString("utf8"));
// handle event.type …
res.status(200).json({ received: true });
});

Python

import hmac
import hashlib

def verify_drape_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")

Brand portal API (authenticated)

Webhook settings are managed with the brand JWT (same session as the portal):

MethodPathNotes
GET/api/brand/integrations/webhookCurrent config (secret not returned)
PUT/api/brand/integrations/webhookBody: { "url", "events?", "is_active?" } — returns secret on create
POST/api/brand/integrations/webhook/rotate-secretReturns new secret

Base URL: https://api.drape.to. Requires Premium; otherwise 403.