Customer portal
A hosted, token-authenticated portal where your customers manage payment methods, subscriptions, and invoices without signing in.
Overview
The customer portal is a hosted page where your customers self-serve their billing: update payment methods, view and pay invoices, download invoice PDFs, and change or cancel subscriptions — all without an account or password.
Access is controlled by a portal session token. You create a session with your secret key (server-side), then redirect the customer to the returned URL. Every customer-facing request is authenticated by the session token embedded in the URL — never by your secret key. This makes the customer-facing endpoints safe to call from the browser.
There are two distinct API surfaces:
The customer-facing endpoints are documented here for completeness and for teams building a fully custom portal front-end. Most integrations only need to create a session and redirect — PaymentKit’s hosted portal calls these token endpoints for you.
Creating a portal session
Create a session server-side with your secret key. The response contains a billing_portal_url you redirect the customer to, and the token used by every customer-facing endpoint.
API
Dashboard
Request body
Response
The portal token is the final path segment of billing_portal_url (a random, URL-safe string — shown as BPT_SECURE_TOKEN above). It is the {billing_portal_token} used in every customer-facing request below.
Portal links grant access to a customer’s billing data. Send them only through secure channels (email, in-app messages) and keep expires_in_hours as short as your flow allows.
Customer-facing API
All customer-facing endpoints are prefixed with:
No Authorization header is required — the token in the path is the credential. Requests against an expired or invalid token return 401, except the session-status endpoint, which stays accessible for expired tokens so the front-end can render an “expired” state with the merchant’s branding.
Session
Get session status
Lightweight check that works even for expired tokens. Returns expiry state plus merchant branding so the front-end can decide whether to redirect and how to render the page.
Get account
Returns the merchant account (branding, name, and public details) for the session.
Customer
Get customer
Returns the customer tied to the session.
Update customer
Update the customer’s contact and billing details. All fields are optional; only the fields you send are changed.
Payment methods
List payment methods
Returns the customer’s saved payment methods.
Add a payment method
Creates a setup checkout session for securely collecting a new payment method. The response returns a secure_token and the account’s tokenization configuration, which you pass to PaymentKit.js to render the card form.
Delete a payment method
Removes a saved payment method. Returns 204 No Content on success.
Subscriptions
List subscriptions
Get a subscription
Get outstanding payment breakdown
Aggregated totals (subtotal, discount, tax, amount due) across all outstanding invoices on the subscription, so the portal can show a single “amount due” figure before collecting.
Collect payment on a subscription
Attempts payment on all outstanding invoices for the subscription using the given payment method.
The response reports an overall payment_status (paid, partially_paid, or failed) plus a per-invoice breakdown in invoice_results.
Update the subscription payment method
Sets the default payment method used for the subscription’s future charges.
Cancel a subscription
Cancels the subscription. Returns the updated subscription.
Undo a scheduled cancellation
Reverses a pending cancellation (a subscription set to cancel at period end), keeping it active. Returns the updated subscription.
Changing a subscription (preview then apply)
Plan and quantity changes use a change request lifecycle so the customer sees the exact proration before anything is charged: preview to compute the cost, then apply to collect payment and commit the change.
Preview changes
Creates (or reuses) a change-request draft, applies the requested changes to it, and returns a full proration preview alongside a change_request_id to apply later.
The response includes prorated line items, discount and tax breakdowns, total_due_atom (the amount that will be charged), and the change_request_id.
Apply a change request
Applies a previously previewed change request using the charge-first pattern: payment is collected before the change is committed. Returns the updated subscription(s) — a billing-cycle change may split into new subscriptions, which are included in the response.
Notable error responses:
Cancel a change request
Discards a previewed change request without applying it. Returns 204 No Content.
Invoices
List invoices
Returns the customer’s invoices, paginated.
Get an invoice
Returns a single invoice with its line items.
Download the invoice PDF
Returns the PDF URL for a finalized invoice, triggering generation if it isn’t ready yet.
When status is generating, the PDF is still being produced — poll again shortly.
Pay an invoice
Collects payment on a single invoice. The response reports payment_status (paid, failed, no_payment_method, or already_paid) and, on success, the invoice PDF URL.
Security
Portal sessions are designed to be safe to expose in the browser:
- Token-scoped — each session is bound to exactly one customer and one account. The token cannot reach any other customer’s data.
- Time-limited — sessions expire after
expires_in_hours(default 1 hour, max 30 days). Expired tokens return401on every endpoint except session-status. - No secret key in the browser — customer-facing endpoints authenticate solely via the URL token, so your secret key never leaves your server.
- Actor attribution — actions taken in the portal (e.g. a self-service cancel) are attributed to the customer in the event log, not to your integration.
Webhooks
Actions taken in the portal emit the same webhook events as any other change, so you can keep your systems in sync:
See Webhooks for the full event list, setup, and signature verification.
Best practices
Add a “Manage billing” button that generates a fresh portal link on demand rather than reusing an old one.
Set expires_in_hours to the shortest window your flow needs.
Listen for portal-driven changes to sync subscriptions and payment methods.
For plan changes, always preview so the customer sees the exact proration before paying.