Customer portal
The customer portal is a Paylera-hosted web experience for end users to manage their own subscription, payment methods, and invoices. You generate a session URL; we host the rest. No additional UI work.
What it does
- Subscription — view current plan, upgrade / downgrade, cancel, reactivate.
- Payment methods — add, remove, set default.
- Invoices — list, download PDFs, retry failed payments.
- Tax IDs — add or remove tax registration numbers.
- Billing address — update billing address.
What it does not do:
- Show usage or current period totals (build that in your app — use
GET /v1/subscriptions/{id}/usage). - Sign the customer in. You authenticate them; we trust your handoff.
Configuration
In Settings → Customer portal, enable the modules you want exposed (you can hide cancellation, hide downgrades, hide tax IDs, etc.). All edits go through validated flows; no manual intervention needed.
Generating a session
POST /v1/portal/sessionsIdempotency-Key: <uuid>{ "customer_id": "cus_…", "return_url": "https://yourapp.example/account/billing"}Response:
{ "id": "ps_…", "url": "https://portal.paylera.io/p/ps_…", "expires_at": "2026-05-06T13:34:00Z"}Redirect the customer to url. The link is single-use and expires in
60 minutes.
When the customer is done (closes the tab, hits back to app), they
land on return_url.
Locking access
The portal session is tied to a single customer_id. The customer
cannot navigate to other customers’ data. The session URL is a
bearer-style token — anyone with it can act on that customer until the
TTL expires. Keep it server-side; don’t email it raw.
For long-lived deep links from your app’s UI, generate the URL on demand at click-time, not at page-render time.
Locale and branding
The portal inherits your tenant’s branding (logo, accent colour). Locale
is auto-detected from Accept-Language; override with locale: "fr"
on session create.
Restricting actions
Per-session overrides for one-off flows:
{ "customer_id": "cus_…", "return_url": "https://yourapp.example/account/billing", "configuration": { "subscription_cancel_enabled": false, "subscription_update_enabled": false, "payment_method_update_enabled": true }}Useful for “update card” emails after a payment failed — you don’t want the customer accidentally cancelling instead of updating.
What you’ll get back
Every action the customer takes emits a webhook from your tenant’s configured endpoints. Listen for:
| Event | When |
|---|---|
subscription.updated | Plan changed, quantity changed. |
subscription.canceled | Customer cancelled. |
subscription.reactivated | Customer un-cancelled before period end. |
customer.payment_method_attached | Customer added a method. |
customer.payment_method_detached | Customer removed a method. |
customer.updated | Address or tax ID changed. |
portal.session.completed | Customer closed the portal. |
portal.session.completed carries a summary of actions taken — use it
to refresh local caches in one shot.
Embedding
The portal isn’t designed to be embedded in an iframe (we ship X-Frame headers). If you need fully embedded UX, build the surface in your app using the REST endpoints — they’re the same ones the portal uses.
Common pitfalls
- Session leaked into a URL log: portal URLs are bearer credentials for the duration of the TTL. Don’t include them in 302 redirects visible in client-side logs.
- Generating sessions in advance: TTL is 60 minutes. Generate at click time, not signup time.
- Customer expects to update something the portal doesn’t expose: expand the configuration in Settings → Customer portal.