Skip to main content

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

API Reference

POST https://api.credyt.ai/webhooks
{
"url": "https://yourplatform.com/webhooks/credyt",
"topics": ["*"]
}
FieldRequiredDefaultDescription
urlYesHTTPS URL Credyt will POST events to
topicsNo["*"]List of event topics to subscribe to. Use "*" to receive all events
connectNofalseSet true to receive events scoped to connected accounts

Response

{
"id": "whd_01abc123",
"secret": "whsec_••••••",
"created_at": "2026-04-14T10:00:00Z"
}
Save your secret

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

TopicDescription
subscription.activatedFired 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 dispatched
  • v0 — 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();
}
);
Replay protection

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.

POST https://api.credyt.ai/webhooks
{
"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

No endpoint yet?

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.

API Reference

POST https://api.credyt.ai/webhooks/:webhookId/test/:topic
{
"user_id": "test_user_123",
"status": "active"
}

To test all destinations subscribed to a topic (without specifying a destination ID):

API Reference

POST https://api.credyt.ai/webhooks/test/:topic

Both endpoints return 202 Accepted with the dispatched event ID:

{
"id": "evt_01abc123"
}

Managing Destinations

OperationAPI Reference
List destinationsGET /webhooks
Get a destinationGET /webhooks/:webhookId
Update a destinationPATCH /webhooks/:webhookId
Delete a destinationDELETE /webhooks/:webhookId
List webhook eventsGET /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.