Webhooks
Titus sends webhook events whenever an entity is created, updated, or deleted. These events are the primary way to track key moments like a successful checkout or a business onboarding. For a full list of events, see the webhooks reference.
You can configure webhook endpoints via your platform dashboard. Each platform can create multiple webhook endpoints for different types of events. All webhook endpoints must be configured with https
URLs that accept POST
requests with JSON payloads.
Event ModelCopied!
All events are sent as POST
requests with a consistent structure:
-
id
id of the webhook event itself -
createdAt
when the webhook event was created -
type
is in the format{entity_type}.{action}
. where action iscreated
,updated
,deleted
-
payload
contains the full current state of the entity at the time of the event
{
"type": "checkout.updated",
"payload": {
...entity data
}
}
Authenticating webhooksCopied!
Titus signs all webhook requests so you can verify their authenticity. Each request includes two headers:
-
x-webhook-signature
: HMAC SHA-256 of${timestamp}.${body}
-
x-webhook-timestamp
: Timestamp of when the event was signed
Never use a webhook request body directly without first verifying the signature.
Example webhook endpoint
function generateWebhookSignature(secret: string, bodyString: string, timestamp: number) {
return crypto.createHmac('sha256', secret).update(`${timestamp}.${bodyString}`).digest('hex');
}
export async function POST(request) {
const body = await request.json();
const signature = request.headers.get('x-webhook-signature');
const timestamp = request.headers.get('x-webhook-timestamp');
if (!signature || !timestamp) {
throw new Error('Missing signature or timestamp');
}
const timestampNum = parseInt(timestamp);
if (isNaN(timestampNum)) {
throw new Error('Invalid timestamp');
}
// Check if timestamp is within 5 minutes
const now = Date.now();
if (now - timestampNum > 300_000) {
throw new Error('Timestamp too old');
}
const expectedSignature = generateWebhookSignature('YOUR_WEBHOOK_SECRET', JSON.stringify(body), timestampNum);
if (signature !== expectedSignature) {
throw new Error('Invalid signature');
}
// webhook verified! You can now safely use it to update your systems
}
Replay Protection
We recommend rejecting webhook requests if the timestamp is older than 5 minutes to prevent replay attacks.
Retry Behavior & IdempotencyCopied!
Titus will automatically retry webhook deliveries if:
-
Your server responds with a non-2xx HTTP status code
-
The request times out or fails to connect
Retry Logic
-
Retries use exponential backoff with a maximum number of attempts
-
There is no guarantee of delivery order
-
Your endpoint must be idempotent — handle duplicates gracefully
Best Practices
-
Log webhook attempts and failures for debugging and support
-
Always return a
2xx
status code (e.g.200 OK
) if processing succeeds
Titus considers a webhook delivered once your server responds with a 2xx. All other responses will be retried.
SummaryCopied!
-
Always validate webhook signatures
-
Handle retries and duplicate events safely
-
Use webhooks or API requests as the source of truth — not redirects