Webhooks
Credyt can notify your platform in real time when billing events occur. You register one or more webhook destinations — HTTPS endpoints in your infrastructure — and Credyt delivers a signed HTTP POST request each time a matching event fires.
Create a Webhook Destination
{
"url": "https://yourplatform.com/webhooks/credyt",
"topics": ["*"]
}
| Field | Required | Default | Description |
|---|---|---|---|
url | Yes | — | HTTPS URL Credyt will POST events to |
topics | No | ["*"] | List of event topics to subscribe to. Use "*" to receive all events |
connect | No | false | Set true to receive events scoped to connected accounts |
Response
{
"id": "whd_01abc123",
"secret": "whsec_••••••",
"created_at": "2026-04-14T10:00:00Z"
}
The secret is returned at creation time and can also be retrieved via GET /webhooks/:webhookId. Store it securely (e.g. as an environment variable) — it is required to verify webhook signatures.
Available Webhook Events
| Topic | Description |
|---|---|
subscription.activated | Fired when a customer is subscribed to a product and their subscription becomes active |
Event envelope
All webhook payloads share a common envelope:
{
"id": "evt_01abc123",
"type": "subscription.activated",
"created": "2026-04-14T10:00:00Z",
"live_mode": true,
"data": { ... }
}
subscription.activated
{
"id": "evt_01abc123",
"type": "subscription.activated",
"created": "2026-04-14T10:00:00Z",
"live_mode": true,
"data": {
"customer_id": "cust_473cr1y0ghbyc3m1yfbwvn3nxx",
"subscription_id": "sub_01abc123",
"occurred_at": "2026-04-14T10:00:00Z"
}
}
Verifying Webhook Signatures
Every webhook request includes a Credyt-Signature header you should use to confirm the request originated from Credyt and has not been tampered with.
Header format: t=<timestamp>,v0=<signature>
t— Unix timestamp of when the event was dispatchedv0— HMAC-SHA256 hex signature of${timestamp}.${rawBody}using your destination secret
The following Node.js helper verifies the signature:
import * as crypto from "crypto";
/**
* Verify a Credyt webhook signature.
*
* @param secret - The webhook destination secret returned at creation
* @param rawBody - The raw request body as a Uint8Array or Buffer
* @param signatureHeader - The value of the `Credyt-Signature` header
* @returns true if the signature is valid, false otherwise
*/
export function verifyWebhookSignature(
secret: string,
rawBody: Uint8Array,
signatureHeader: string
): boolean {
// Split on the first comma only: "t=<timestamp>,v0=<sig1,sig2>"
const [timestampPart, signaturesPart] = signatureHeader.split(",", 2);
const timestamp = parseInt(timestampPart.replace("t=", ""), 10);
if (isNaN(timestamp)) {
return false;
}
const signatures = signaturesPart.replace("v0=", "").split(",");
// Signed content is: "<timestamp>.<rawBody>"
const signedContent = `${timestamp}.${rawBody}`;
const hmac = crypto.createHmac("sha256", secret);
hmac.update(signedContent);
const expectedSignature = hmac.digest("hex");
return signatures.some((sig) => sig === expectedSignature);
}
Express.js example
import express from "express";
const app = express();
// Use raw body parser to preserve the exact bytes for signature verification
app.post(
"/webhooks/credyt",
express.raw({ type: "application/json" }),
(req, res) => {
const signatureHeader = req.headers["credyt-signature"] as string;
const isValid = verifyWebhookSignature(
process.env.CREDYT_WEBHOOK_SECRET!,
req.body,
signatureHeader
);
if (!isValid) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(req.body.toString());
switch (event.topic) {
case "subscription.activated":
// Handle subscription activation
break;
}
res.status(200).send();
}
);
The t timestamp lets you reject replayed requests. Consider rejecting events where the timestamp is more than a few minutes old.
Connect Webhooks
If your platform uses Credyt's C4P functionality to manage sub-accounts, you can create dedicated webhook destinations for connected-account events by setting connect: true when creating the destination.
{
"url": "https://yourplatform.com/webhooks/credyt/connect",
"topics": ["subscription.activated"],
"connect": true
}
Connected-account events are routed only to destinations with connect: true. Standard destinations (with connect: false) receive platform-level events only.
Testing Webhooks
Tools like webhook.site or Hookdeck Console give you a temporary public URL that logs incoming requests — useful for inspecting the payload shape before you build your own endpoint.
You can send a test event to an existing destination without triggering a real billing event. This is useful for verifying your endpoint handles the payload correctly.
{
"user_id": "test_user_123",
"status": "active"
}
To test all destinations subscribed to a topic (without specifying a destination ID):
POST https://api.credyt.ai/webhooks/test/:topic
Both endpoints return 202 Accepted with the dispatched event ID:
{
"id": "evt_01abc123"
}
Managing Destinations
| Operation | API Reference |
|---|---|
| List destinations | GET /webhooks |
| Get a destination | GET /webhooks/:webhookId |
| Update a destination | PATCH /webhooks/:webhookId |
| Delete a destination | DELETE /webhooks/:webhookId |
| List webhook events | GET /webhooks/events |
Webhook Event Log
The event log (GET /webhooks/events) lets you inspect every delivery attempt, including its status (Success or Failed) and timestamp. Results can be filtered by destination ID, status, and time range and support cursor-based pagination.