Skip to main content
POST
https://searchcompany-main.up.railway.app
/
api
/
billing-portal
curl -X POST https://searchcompany-main.up.railway.app/api/billing-portal \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_CLERK_JWT_TOKEN" \
  -d '{
    "return_url": "https://app.searchcompany.co/dashboard/settings"
  }'
{
  "url": "https://billing.stripe.com/p/session/abc123..."
}
Creates a Stripe Customer Portal session where users can manage their subscription, update payment methods, and view invoices.
Authentication Required: This endpoint requires a Clerk JWT token with organization context. The organization is determined from the org_id claim in the token.

Request Headers

Authorization
string
required
Bearer token with organization context: Bearer {clerk_jwt_token}

Request Body

return_url
string
required
URL to redirect back to after leaving the portal

Response

url
string
The Stripe Customer Portal URL - redirect the user here
curl -X POST https://searchcompany-main.up.railway.app/api/billing-portal \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_CLERK_JWT_TOKEN" \
  -d '{
    "return_url": "https://app.searchcompany.co/dashboard/settings"
  }'
{
  "url": "https://billing.stripe.com/p/session/abc123..."
}

Errors

StatusDescription
401Missing or invalid authentication token
401No organization context in token
400No Stripe customer linked to this organization
404Organization not found
500Stripe API error

Usage

After receiving the URL, redirect the user:
// Get token with organization context
const token = await getToken({ organizationId: organization.id });

const response = await fetch("/api/billing-portal", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${token}`,
  },
  body: JSON.stringify({
    return_url: window.location.href,
  }),
});

const { url } = await response.json();
window.location.href = url;