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 is created, 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