Authdog

Billing

Last updated Aug 8, 2026
View as Markdown

Authdog Billing adds a subscription catalog to your environment: define plans and the features they grant, sell them through Stripe, and gate your application on the plan or feature a signed-in user holds. Subscriptions and entitlements live next to your users, so checking a plan is a local read, not a round trip to Stripe.

Billing runs on the environment's existing Stripe connection and supports one active subscription per subject.

Prerequisites

Connect Stripe first, under Authentication > Add-ons in the Authdog console. Billing reuses that connection — its secret key creates Products and Prices, and your checkout page initializes Stripe.js with its publishable key. There is no separate Billing credential.

Connect a Stripe test account to your development environment and the live account to production. Plans, features, and subscriptions never cross environments.

Model

Object Meaning
Feature A gateable capability, identified by a slug such as advanced_reports.
Plan A purchasable bundle of features with a monthly price and an optional annual price.
Subscription One subject's current plan, its period, and its Stripe status.

A plan targets either a user or an organization (forResource), and slugs are unique within an environment. A feature's slug is what your application checks against, so treat it as frozen once it ships — renaming it revokes access for everyone holding it.

Define plans

In the console, open Billing > Plans for the selected environment:

  1. Add the features you want to gate on.
  2. Create a plan: name, slug, description, and whether it is for a user or an organization.
  3. Set the currency and the monthly amount, in cents. Add an annual amount only if you sell one — a plan can be month-only.
  4. Attach the features the plan grants.
  5. Choose whether the plan is publicly visible, whether it is the default, and its sort order in the pricing table.
  6. Select Sync to Stripe.

Syncing creates or refreshes the plan's Stripe Product and its monthly and annual Prices. Stripe Prices are immutable, so changing an amount mints a new Price and leaves the old one in place — Stripe does not allow deleting a Price that has been used — and existing subscribers stay on the Price they bought until they change plans.

An unsynced plan has no Stripe Price and cannot be checked out.

Client endpoints

Your application calls these on the Authdog identity origin, https://identity.authdog.com, or on your custom domain when one is configured.

Endpoint Auth Purpose
GET /billing/:environmentId/plans None Publicly visible plans, their features, and the Stripe publishable key.
GET /billing/:environmentId/subscription Session The caller's current subscription.
POST /billing/:environmentId/subscriptions Session Start a subscription; returns a client secret for payment confirmation.
POST /billing/:environmentId/subscriptions/cancel Session Cancel the caller's subscription.

The plans endpoint is deliberately unauthenticated — a pricing page has to render before sign-in. It returns only plans marked publicly visible, and the publishable key, never a secret. Pass ?for=org to list organization plans; the default is user.

Creating a subscription takes a planId and an optional planPeriod of month or annual (default month). The response carries a clientSecret your page confirms with Stripe Elements, so card details never touch Authdog.

curl -X POST "https://identity.authdog.com/billing/$ENVIRONMENT_ID/subscriptions" \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "planId": "pln_123", "planPeriod": "annual" }'

Gate your application

The React SDK ships the pieces for a pricing page and for gating:

  • PricingTable renders plans with a month/annual toggle and a per-plan call to action.
  • CheckoutButton starts checkout for a plan and period.
  • Protect renders its children only when the caller holds the given plan or feature.
  • hasBillingAccess exposes the same check as a plain function, for use outside JSX.
import { Protect } from "@authdog/react-elements"

<Protect
  subscription={subscription}
  activeFeatures={features}
  feature="advanced_reports"
  fallback={<UpgradePrompt />}
>
  <AdvancedReports />
</Protect>

Protect is a pure conditional render over state you already fetched — it makes no network call of its own. And like every client-side gate, it decides what the interface shows, not what the API allows: enforce entitlements again on your server before doing the paid work.

Staying in sync with Stripe

Stripe's webhook is the source of truth for subscription state: it updates the stored status, current period end, and cancellation flag, and refreshes the entitlement cache for the plan's features. Feature slugs double as Stripe entitlement lookup keys, so a feature check reads the same cache that backs entitlement claims.

A subscription starts as incomplete and turns active once payment succeeds. Never grant access on the checkout response alone — wait for the subscription to report an active status.

Configure the Stripe webhook for the same account you connected, and verify a full purchase in a test environment before enabling live payments.

Limits

  • One active subscription per subject, per environment.
  • Checkout accepts user plans only. Organization plans can be defined and listed with ?for=org, but a subscription request for one is rejected.
  • Amounts are integer cents in the plan's currency. A plan has a single currency.

Learn more