# This is typically called by Stripe, not manually
curl -X POST "https://searchcompany-main.up.railway.app/webhooks/stripe" \
-H "Content-Type: application/json" \
-H "stripe-signature: t=1234567890,v1=abc123..." \
-d '{
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_test_abc123",
"customer": "cus_xyz789",
"customer_email": "user@example.com",
"subscription": "sub_def456",
"client_reference_id": "nike"
}
}
}'
{
"status": "success"
}
π΅ POST - Stripe Webhook
Stripe Webhook
Handle Stripe webhook events to sync subscription status with Clerk
POST
/
webhooks
/
stripe
# This is typically called by Stripe, not manually
curl -X POST "https://searchcompany-main.up.railway.app/webhooks/stripe" \
-H "Content-Type: application/json" \
-H "stripe-signature: t=1234567890,v1=abc123..." \
-d '{
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_test_abc123",
"customer": "cus_xyz789",
"customer_email": "user@example.com",
"subscription": "sub_def456",
"client_reference_id": "nike"
}
}
}'
{
"status": "success"
}
This endpoint receives webhook events from Stripe and updates the corresponding Clerk organization and user metadata to keep subscription status in sync.
Triggered when a customer completes a checkout session.
Actions:
Triggered when a subscription is updated (e.g., plan change, renewal).
Actions:
Triggered when a subscription is cancelled or expires.
Actions:
No authentication required - This is a public webhook endpoint. This
endpoint is called by Stripe, not by your application directly. You must
configure this URL in your Stripe Dashboard under Developers β Webhooks.
Headers
string
required
Stripe webhook signature for verifying the request authenticity
Request Body
The request body is a raw Stripe webhook event payload. The endpoint handles the following event types:checkout.session.completed
Triggered when a customer completes a checkout session.
Actions:
- Links Stripe customer ID to Clerk organization via
client_reference_id - Stores
stripeCustomerId,stripeSubscriptionId, andsubscriptionStatusin organization metadata - Updates user metadata for backwards compatibility
- Clears subscription cache for immediate status update
customer.subscription.updated
Triggered when a subscription is updated (e.g., plan change, renewal).
Actions:
- Updates
subscriptionStatusin Clerk user metadata - Sets status to
"active"foractiveortrialingsubscriptions - Sets status to
"inactive"for other states
customer.subscription.deleted
Triggered when a subscription is cancelled or expires.
Actions:
- Updates
subscriptionStatusto"cancelled"in Clerk user metadata
Response
string
"success" when the webhook was processed successfully# This is typically called by Stripe, not manually
curl -X POST "https://searchcompany-main.up.railway.app/webhooks/stripe" \
-H "Content-Type: application/json" \
-H "stripe-signature: t=1234567890,v1=abc123..." \
-d '{
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_test_abc123",
"customer": "cus_xyz789",
"customer_email": "user@example.com",
"subscription": "sub_def456",
"client_reference_id": "nike"
}
}
}'
{
"status": "success"
}
Errors
| Status | Description |
|---|---|
| 400 | Invalid payload or invalid Stripe signature |
Stripe Dashboard Configuration
To configure this webhook in Stripe:- Go to Stripe Dashboard β Developers β Webhooks
- Click βAdd endpointβ
- Enter the endpoint URL:
https://searchcompany-main.up.railway.app/webhooks/stripe - Select events to listen for:
checkout.session.completedcustomer.subscription.updatedcustomer.subscription.deleted
- Copy the signing secret and set it as
STRIPE_WEBHOOK_SECRETenvironment variable
Environment Variables
| Variable | Description |
|---|---|
STRIPE_API_KEY | Stripe API secret key |
STRIPE_WEBHOOK_SECRET | Webhook signing secret from Stripe Dashboard |
CLERK_SECRET_KEY | Clerk secret key for updating user/org metadata |