Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.shipfastai.dev/llms.txt

Use this file to discover all available pages before exploring further.

The Billing API integrates with Stripe to handle subscription checkout, customer portal access, subscription queries, and lifecycle events. All billing endpoints are mounted under /api/billing/. Protected endpoints require a valid Bearer token for the authenticated user.
You must configure STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET in your backend environment variables before these endpoints will work. See your backend .env for the required keys.

POST /api/billing/create-checkout-session

Create a Stripe Checkout session for a subscription. If the authenticated user does not yet have a Stripe customer record, one is created automatically using their name and email. Returns the Stripe-hosted checkout URL to redirect the user to. Headers:
Authorization
string
required
Bearer <access_token>
Query parameters:
price_id
string
required
The Stripe Price ID for the subscription plan (e.g., price_1OqXxxxxxYYYYYYYY). You can find price IDs in the Stripe Dashboard or via the Stripe CLI.
curl --request POST \
  --url "http://localhost:8000/api/billing/create-checkout-session?price_id=price_1OqXxxxxxYYYYYYYY" \
  --header "Authorization: Bearer <access_token>"
Response:
url
string
required
The Stripe-hosted checkout URL. Redirect the user’s browser to this URL to complete payment.
{
  "url": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3d4..."
}
After a successful payment, Stripe redirects to {FRONTEND_URL}/dashboard?success=true. If the user cancels, Stripe redirects to {FRONTEND_URL}/pricing?canceled=true.

POST /api/billing/create-portal-session

Create a Stripe Customer Portal session for the authenticated user. The portal lets the user manage their subscription, update payment methods, view invoices, and cancel. The user must have an existing Stripe customer record (created automatically at checkout). Headers:
Authorization
string
required
Bearer <access_token>
No request body is required. The endpoint uses the stripe_customer_id stored on the authenticated user.
curl --request POST \
  --url http://localhost:8000/api/billing/create-portal-session \
  --header "Authorization: Bearer <access_token>"
Response:
url
string
required
The Stripe-hosted billing portal URL. Redirect the user’s browser to this URL.
{
  "url": "https://billing.stripe.com/session/bps_test_a1b2c3d4..."
}
After the user leaves the portal, Stripe redirects them to {FRONTEND_URL}/dashboard. Returns 400 with {"detail": "No billing account found"} if the user has never completed a checkout session and therefore has no Stripe customer ID.

GET /api/billing/subscription

Return the current subscription status for the authenticated user. Headers:
Authorization
string
required
Bearer <access_token>
curl --request GET \
  --url http://localhost:8000/api/billing/subscription \
  --header "Authorization: Bearer <access_token>"
Response:
status
string
required
Current subscription status (e.g., free, active, cancelled, past_due).
tier
string
required
Current subscription tier (e.g., free, pro).
stripe_customer_id
string
The Stripe customer ID associated with the user, or null if no Stripe record exists.
{
  "status": "active",
  "tier": "pro",
  "stripe_customer_id": "cus_a1b2c3d4e5f6"
}

GET /api/billing/plans

Return the list of available subscription plans with their features. This endpoint does not require authentication.
curl --request GET \
  --url http://localhost:8000/api/billing/plans
Response:
plans
object[]
required
Array of available subscription plans.
{
  "plans": [
    {
      "name": "Free",
      "tier": "free",
      "price": 0,
      "features": [
        "Basic authentication",
        "User management",
        "Community support"
      ]
    },
    {
      "name": "Pro",
      "tier": "pro",
      "price_monthly": "price_1OqXxxxxxMONTHLY",
      "price_yearly": "price_1OqXxxxxxYEARLY",
      "features": [
        "Everything in Free",
        "AI Chat (OpenAI, Anthropic, Gemini)",
        "RAG Pipeline",
        "API Key access",
        "Priority support"
      ]
    }
  ]
}

POST /api/billing/webhook

Stripe webhook endpoint. This endpoint is called directly by Stripe — not by your application. Stripe sends signed events here to notify your backend of subscription changes.
Do not call this endpoint from your application. Register it in the Stripe Dashboard (or via the Stripe CLI) as your webhook URL: https://<your-backend-url>/api/billing/webhook.
Headers required by Stripe:
stripe-signature
string
required
The Stripe-Signature header added automatically by Stripe. The backend verifies this signature against your STRIPE_WEBHOOK_SECRET to confirm the event is authentic.

Handled events

Event typeEffect
checkout.session.completedSets subscription_status to active and determines subscription_tier from the checkout line items using the configured price-to-tier mapping.
customer.subscription.updatedUpdates subscription_status on the matching user to the new Stripe subscription status.
customer.subscription.deletedSets subscription_status to cancelled and subscription_tier to free for the customer.
invoice.payment_failedSets subscription_status to past_due for the customer.
Response (on successful receipt):
{
  "received": true
}
Returns 400 for an invalid payload or a failed signature verification.

Local testing with the Stripe CLI

To test webhooks locally, forward events from Stripe to your running dev server:
stripe listen --forward-to http://localhost:8000/api/billing/webhook
The CLI prints a webhook signing secret that you should set as STRIPE_WEBHOOK_SECRET in your local .env while testing.