Documentation menu

Automation

Deliver signed workspace events to your application.

Register HTTPS endpoints, subscribe to actual emitted events, inspect delivery history, and replay failed attempts.

Manage endpoints

GET/webhooksPro or Business
POST/webhooksWorkspace endpoint limit applies
PATCH/webhooks/:id
POST/webhooks/:id/rotate-secret
DELETE/webhooks/:id
typescript
import { veritas } from "@/lib/veritas";

const created = await veritas<{
  webhook: { id: string };
  secret: string;
}>("/webhooks", {
  method: "POST",
  body: JSON.stringify({
    url: "https://example.com/api/veritas-webhook",
    events: ["payment_link.paid", "verification.failed"],
  }),
});

const webhookId = created.webhook.id;
// Store created.secret in your server secret manager.

const webhooks = await veritas("/webhooks");

await veritas(`/webhooks/${webhookId}`, {
  method: "PATCH",
  body: JSON.stringify({ active: false }),
});

const rotated = await veritas<{ secret: string }>(
  `/webhooks/${webhookId}/rotate-secret`,
  { method: "POST" },
);

await veritas(`/webhooks/${webhookId}`, { method: "DELETE" });

Delivery history

GET/webhooks/:id/deliveries
POST/webhooks/:id/retry/:deliveryId
Queues a replay of a failed delivery.
typescript
import { veritas } from "@/lib/veritas";

const deliveries = await veritas(
  "/webhooks/webhook_id/deliveries?page=1&limit=50",
);

await veritas(
  "/webhooks/webhook_id/retry/failed_delivery_id",
  { method: "POST" },
);

Delivery records move through queued, processing, retrying, succeeded, dead-letter, and cancelled states. Do not assume immediate delivery.

Events

Supported subscriptions are payment_link.paid, product.sold_out, verification.success, verification.failed, and webhook.dead_letter.

Verify signatures

Read X-Veritas-Signature. Its value is sha256=LOWERCASE_HEX_HMAC over the exact raw JSON bytes. Parse JSON only after verification.

typescript
import crypto from "node:crypto";

const webhookSecret = process.env.VERITAS_WEBHOOK_SECRET;
if (!webhookSecret) throw new Error("VERITAS_WEBHOOK_SECRET is required");

export async function POST(request: Request) {
  // Verify the exact bytes received from Veritas before parsing JSON.
  const rawRequestBody = Buffer.from(await request.arrayBuffer());
  const signatureHeader =
    request.headers.get("x-veritas-signature") ?? "";

  const expectedHex = crypto
    .createHmac("sha256", webhookSecret)
    .update(rawRequestBody)
    .digest("hex");
  const expected = `sha256=${expectedHex}`;
  const expectedBytes = Buffer.from(expected, "utf8");
  const receivedBytes = Buffer.from(signatureHeader, "utf8");

  const valid =
    expectedBytes.length === receivedBytes.length &&
    crypto.timingSafeEqual(expectedBytes, receivedBytes);

  if (!valid) return new Response("Invalid signature", { status: 401 });

  const event = JSON.parse(rawRequestBody.toString("utf8"));
  // Handle the event and record its order/delivery ID before side effects.
  console.log(event.event);

  return new Response(null, { status: 204 });
}

Retries and limits

Delivery attempts run immediately, then after 5, 15, and 45 seconds, with a 10-second request timeout. Active endpoints count toward the configured plan limit. Lowering a limit does not delete existing endpoints; it blocks creation or reactivation above the new capacity.