Developers · Event Delivery
Receive signed lifecycle events, then verify the receipt
A webhook tells you something changed. Your server still fetches the public receipt and evaluates it before any partner action.
Contract
Schema version 2026-09-18.
Production payload types: receipt.issued, receipt.revoked. Stored as partner.receipt.issued and partner.receipt.revoked.
receipt.expired, receipt.expiring, receipt.invalidated, decision.denied, and integration.health_changed require an expanded outbox CHECK. Until that DEMO-first migration is applied, skip code is event_type_not_supported. Production 067 compatibility remains receipt.issued, receipt.revoked, and TEST EVENT.
A webhook event is not authorization. Fetch GET /api/receipts/{id}/public and verify the signed receipt on your server before granting access.
Delivery is best effort. It is not guaranteed.
- HTTPS only. HTTP is rejected.
- No query strings, fragments, or userinfo in the URL.
- No localhost, link-local, or private IP destinations.
- Hostname must resolve to a public address at save time and at delivery time.
- Signing secret is shown once. Store it in your server secret manager.
- Webhook events are notifications. Fetch and verify the signed public receipt before granting access.
- Delivery is best effort with bounded retries. It is not guaranteed.
Next.js webhook receiver
// app/api/abraxas/webhooks/route.ts
import { NextRequest, NextResponse } from "next/server";
import { AbraxasPartnerKit, permitProtocolAction } from "@/lib/partner/integrationKit";
import { verifyPartnerWebhookEvent } from "@/lib/partner/eventDelivery";
const kit = new AbraxasPartnerKit({
partnerId: "your-partner-id",
policyId: "your-policy-v1",
environment: "sandbox",
baseUrl: process.env.ABRAXAS_BASE_URL,
});
const seenEventIds = new Set<string>();
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const verified = verifyPartnerWebhookEvent({
secret: process.env.ABRAXAS_WEBHOOK_SECRET ?? "",
timestamp: req.headers.get("x-abraxas-webhook-timestamp") ?? "",
rawBody,
signatureHeader: req.headers.get("x-abraxas-webhook-signature") ?? "",
expectedPartnerId: kit.options.partnerId,
seenEventIds,
});
if (!verified.ok) {
return NextResponse.json({ accepted: false, error: verified.error }, { status: 400 });
}
if (verified.duplicate) {
return NextResponse.json({ accepted: true, duplicate: true });
}
const receiptId = verified.payload.receipt_id;
if (!receiptId) {
return NextResponse.json({ accepted: true, grant: false, reason: "no_receipt" });
}
const result = await kit.verifyReceiptId(receiptId);
if (!permitProtocolAction(result)) {
return NextResponse.json({ accepted: true, grant: false, outcome: result.outcome });
}
return NextResponse.json({ accepted: true, grant: true, outcome: "permitted" });
}
Express webhook receiver
// express webhook receiver
import express from "express";
import { AbraxasPartnerKit, permitProtocolAction } from "@/lib/partner/integrationKit";
import { verifyPartnerWebhookEvent } from "@/lib/partner/eventDelivery";
const kit = new AbraxasPartnerKit({
partnerId: "your-partner-id",
policyId: "your-policy-v1",
environment: "sandbox",
});
const seenEventIds = new Set();
app.post("/webhooks/abraxas", express.raw({ type: "application/json" }), async (req, res) => {
const rawBody = Buffer.isBuffer(req.body) ? req.body.toString("utf8") : String(req.body ?? "");
const verified = verifyPartnerWebhookEvent({
secret: process.env.ABRAXAS_WEBHOOK_SECRET,
timestamp: String(req.headers["x-abraxas-webhook-timestamp"] ?? ""),
rawBody,
signatureHeader: String(req.headers["x-abraxas-webhook-signature"] ?? ""),
expectedPartnerId: kit.options.partnerId,
seenEventIds,
});
if (!verified.ok) return res.status(400).json({ accepted: false, error: verified.error });
if (verified.duplicate) return res.json({ accepted: true, duplicate: true });
if (!verified.payload.receipt_id) return res.json({ accepted: true, grant: false });
const result = await kit.verifyReceiptId(verified.payload.receipt_id);
return res.json({
accepted: true,
grant: permitProtocolAction(result),
outcome: result.outcome,
});
});
Launchpad: Partner Launchpad · Kit: Integration Kit