Webhooks

Inbound webhook handlers for Stripe and Clerk events.

Overview

Praxiom AI receives inbound webhooks from Stripe (billing events) and Clerk (user lifecycle events). These endpoints have no user authentication -- they are verified by provider-specific signatures.


Stripe Webhook

POST /api/webhooks/stripe

Handle Stripe webhook events. Verified by Stripe signature (no JWT auth required).

Headers

NameRequiredDescription
stripe-signatureYesStripe webhook signature for payload verification

Handled Event Types

EventAction
checkout.session.completedLinks Stripe customer to workspace; provisions boost packs; grants referral credits
customer.subscription.createdCreates/updates subscription record with plan and period
customer.subscription.updatedUpdates plan, status, and period dates
customer.subscription.deletedMarks subscription as cancelled
invoice.payment_succeededActivates subscription, resets period
invoice.payment_failedSets subscription to past_due status

Idempotency

Webhook events are deduplicated using the ProcessedStripeEvent table. If the same event ID is received twice, the second delivery returns {"status": "already_processed"}. Old events are probabilistically cleaned up after 90 days.

Response (200 OK)

{
  "status": "ok"
}

Error Responses

StatusDescription
400Invalid payload or signature
400Webhook secret not configured

Clerk Webhook

POST /api/webhooks/clerk

Handle Clerk user lifecycle events. Verified by Clerk webhook signature (no JWT auth required).

Handled Event Types

EventAction
user.createdCreates a new user record in the database
user.updatedUpdates user profile (name, email, avatar)

How It Works

  1. Clerk fires a webhook when a user signs up or updates their profile.
  2. The endpoint verifies the Clerk webhook signature.
  3. For user.created, a new User record is created with the Clerk ID, email, and name.
  4. For user.updated, the existing user record is updated with the new profile data.

Response (200 OK)

{
  "status": "ok"
}

Auth Endpoint

GET /api/auth/me

Returns the current authenticated user's profile. This is not a webhook but is included here as part of the auth flow.

Example Request

curl https://api.praxiomai.xyz/api/auth/me \
  -H "Authorization: Bearer $TOKEN"

Response (200 OK)

{
  "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "clerk_id": "user_2abc...",
  "email": "pm@example.com",
  "name": "Jane Doe",
  "avatar_url": "https://img.clerk.com/...",
  "has_product_access": true,
  "is_founder": false,
  "created_at": "2026-03-15T08:00:00Z"
}

Was this helpful?