Logocowtic
Public API

Webhooks

HMAC-SHA256 signatures, delivery headers, and retry behavior

Register HTTPS endpoints with POST /webhooks (webhooks:write). Choose the events you want and store the signing secret securely — it is shown only at create or rotate.

Event types

EventWhen
order.completedOrder finished successfully
order.cancelledOrder cancelled
ticket.issuedTicket issued
ticket.checked_inTicket checked in

Delivery headers

Each delivery is a POST with Content-Type: application/json and:

HeaderPurpose
X-Cowtic-SignatureHex HMAC-SHA256 of {timestamp}.{rawBody}
X-Cowtic-TimestampUnix timestamp (seconds) used in the signature
X-Cowtic-Event-IdStable event id (idempotency for your receiver)
X-Cowtic-Event-TypePublic event type (for example order.completed)
User-AgentCowtic-Webhooks/1.0

Payload envelope

{
  "id": "evt_…",
  "type": "order.completed",
  "createdAt": "2026-07-20T12:00:00.000Z",
  "data": {
    "orderId": "…"
  }
}

Verify signatures

import { createHmac, timingSafeEqual } from "node:crypto";

function verifyCowticSignature(params: {
  secret: string;
  timestamp: string;
  rawBody: string;
  signature: string;
}): boolean {
  const expected = createHmac("sha256", params.secret)
    .update(`${params.timestamp}.${params.rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(params.signature, "utf8");
  if (a.length !== b.length) return false;
  return timingSafeEqual(a, b);
}

Use the raw body

Compute the HMAC over the exact request body bytes (string) you received — do not re-serialize JSON before verifying.

Reject deliveries with timestamps that are too old (for example older than five minutes) to reduce replay risk. Deduplicate with X-Cowtic-Event-Id.

Retries

Failed deliveries (non-2xx or network errors) are retried with exponential backoff:

AttemptDelay before next try
11 minute
22 minutes
35 minutes
410 minutes
520 minutes
640 minutes
780 minutes
8160 minutes

After 8 failed attempts the outbox item is marked dead. Inspect history with GET /webhooks/{webhookId}/deliveries.

Secrets

  • Rotate with POST /webhooks/{webhookId}/rotate-secret.
  • Update your receiver before or immediately after rotation.
  • Respond quickly with 2xx once the event is accepted (process asynchronously if needed).

On this page