Founder & Lead Engineer, RAITHub
An ISP self-service portal lets subscribers manage their account, change plan, check usage, pay bills, report faults and see outages without calling support. For most ISPs and small telecom providers, the portal bundled with their billing platform covers the basics. Build a custom one when that portal cannot model your plans: Stripe's hosted portal cannot update usage-based subscriptions and offers at most 10 products to switch between.
This guide is for ISPs, wireless ISPs, small MVNOs and VoIP providers anywhere, and for the engineers building for them. RAITHub has not shipped a telecom portal, so this is engineering guidance, drawn from customer and staff portals RAITHub has built for other industries and from vendor documentation cited inline. It does not cover OSS/BSS integration or TM Forum API conformance, which RAITHub has not delivered.
What features should an ISP customer portal have?
Seven, in roughly the order subscribers use them. The hard part is rarely the screen. It is the system behind it that the portal has to read from or change.
| Feature | What the customer does | Source of truth behind it | The hard part |
|---|---|---|---|
| Account | Update contact details, authorised users, notification preferences | CRM or billing platform | Verifying identity before sensitive changes |
| Plan changes | Upgrade, downgrade, add a static IP or extra bundle | Billing platform plus network provisioning | Keeping the bill and the network in step (see the state machine below) |
| Usage view | See data or minutes used against the allowance | Network accounting records or rated CDRs | Usage arrives late; label it with its as-of time |
| Bill pay | View and download invoices, pay, save a payment method, set up autopay | Billing platform and payment provider | Webhooks and reconnection after a late payment |
| Fault tickets | Report a fault, run basic checks, track the ticket | Helpdesk or ticketing system | Collecting useful diagnostics before a human sees it |
| Outage status | See whether a known outage affects their service | Network operations centre (NOC) incident records | Mapping an incident to the customers it actually affects |
| Staff console | Support, billing and NOC staff act on customer accounts | All of the above | Role-based access and an audit trail |
Many of the pieces also appear in tenant and resident portals. The tenant portal features guide covers the common ones (payments, requests, notifications) in more depth.
Can I just use my billing platform's portal or Stripe's?
Often, yes, and you should check first. ISP platforms ship portals: Splynx lists a "Customer portal" and ticketing on its product page, and Sonar's pricing page lists a self-service portal and ticketing among its included features. If your plans fit the platform, its portal usually costs the least to run, because it is already in the licence.
If you bill through Stripe, the Stripe customer portal documentation says customers can update billing details and payment methods, update or cancel subscriptions, and "pay, download, and view current and past invoices". Its limits matter for telecom:
- Subscriptions with multiple products or usage-based billing can be cancelled in the portal, but not updated. A broadband plan with a metered add-on falls into that group.
- Plan switching offers a maximum of 10 products.
- Sessions are short: a new session expires after 5 minutes if unused, and within 1 hour of the last activity.
- The portal cannot be displayed inside an iframe, so it is a hand-off, not a page in your own portal.
A common middle path is a custom portal for everything telecom-specific (usage, faults, outages, plan changes) with a deep link to Stripe's portal for card updates and invoice downloads. The billing wiring is covered in how to add Stripe billing to a SaaS.
When is a custom ISP portal worth building?
- Your data lives in several systems. Billing in one product, tickets in another, network status in a third. A thin portal over all three gives customers one login.
- Your plans do not fit the vendor's portal. Bundles, metered add-ons, business accounts with many sites, or resellers with their own customers.
- You run a custom billing core. Then there is no vendor portal to use.
- Support volume is the cost. If most calls are "is there an outage?" and "why is my bill higher?", a portal that answers both is the feature that pays for itself. Measure your own call reasons before assuming it.
How should plan changes work between billing and the network?
As an explicit state machine, stored in the database. A plan change touches two systems that fail independently: billing (was the upgrade paid for?) and provisioning (did the new speed profile reach the customer's connection?). If the portal just calls both in sequence, sooner or later a customer pays for 500 Mbps and still gets 100, or gets 500 without paying.
A minimal model: upgrades take payment first, then provision immediately; downgrades wait for the end of the billing cycle. Every transition that is not listed is rejected.
export type PlanChangeState =
| 'requested' | 'awaiting_payment' | 'scheduled'
| 'provisioning' | 'active' | 'failed' | 'cancelled'
export type PlanChangeEvent =
| 'UPGRADE_CONFIRMED' | 'DOWNGRADE_CONFIRMED'
| 'PAYMENT_SUCCEEDED' | 'PAYMENT_FAILED'
| 'CYCLE_ENDED'
| 'NETWORK_APPLIED' | 'NETWORK_REJECTED'
| 'CUSTOMER_CANCELLED'
const transitions: Record<PlanChangeState, Partial<Record<PlanChangeEvent, PlanChangeState>>> = {
requested: {
UPGRADE_CONFIRMED: 'awaiting_payment',
DOWNGRADE_CONFIRMED: 'scheduled',
CUSTOMER_CANCELLED: 'cancelled',
},
awaiting_payment: { PAYMENT_SUCCEEDED: 'provisioning', PAYMENT_FAILED: 'failed' },
scheduled: { CYCLE_ENDED: 'provisioning', CUSTOMER_CANCELLED: 'cancelled' },
provisioning: { NETWORK_APPLIED: 'active', NETWORK_REJECTED: 'failed' },
active: {},
failed: {},
cancelled: {},
}
export function nextState(from: PlanChangeState, event: PlanChangeEvent): PlanChangeState {
const to = transitions[from][event]
if (!to) throw new Error('Illegal plan change transition: ' + from + ' on ' + event)
return to
}
Persist each transition with a guard on the current state, so a payment webhook and a retry job that arrive together cannot both move the same change:
-- Returns one row if this caller won the transition, zero if another did.
UPDATE plan_changes
SET state = $2, updated_at = now()
WHERE id = $1 AND state = $3
RETURNING id;
Three details that come up in practice for any system like this:
- Failed provisioning needs a human. A change stuck in
failedafter a successful payment must raise a ticket and, depending on your policy, refund or credit the difference. Do not leave it for the customer to notice. - One change at a time. Reject a new request while another is in
awaiting_payment,scheduledorprovisioning, or the second change overwrites the first on the network. - Webhooks are the payment signal. Move to
provisioningon the payment provider's webhook, not on the browser redirect. If they go missing, see Stripe webhook not firing.
How do you show outage status customers can trust?
By tying incidents to the network elements they affect, and customers to the elements that serve them. A national status page that says "some customers may be affected" does not stop anyone calling. A portal banner that says "an outage affects your area, engineers identified the cause at 14:10" does.
You do not need to invent the vocabulary. Atlassian's Statuspage uses five component statuses: operational, under maintenance, degraded performance, partial outage and major outage. It uses four incident statuses: investigating, identified, monitoring and resolved. Customers already recognise these words from other services.
The data model is small: an incident links to one or more network elements (a POP, a tower, a street cabinet), and each service links to the element that serves it. When a customer opens the fault form, check for an open incident on their element first and show it, with the latest update and its time. Only when there is none, run the diagnostics and open a ticket. That ordering is what turns the outage page into fewer duplicate tickets.
Two rules for the NOC side: updates are written by people, not generated, and a resolved incident stays visible for a while so customers who were offline can see what happened.
How should RBAC separate staff from customers?
Treat them as two different kinds of user, not two roles in one list. Customers act on their own account only. Staff act on many accounts, within the limits of their role. RAITHub's RBAC design guide covers the permission model; for an ISP it usually looks like this:
| User | Can | Cannot |
|---|---|---|
| Account holder | Everything on their own account, add authorised users | See any other account |
| Authorised user | View usage, report faults, pay bills | Change plan, change payment method, close the account |
| Support agent | View accounts, run diagnostics, raise and update tickets | Issue credits above a set limit, change prices |
| Billing staff | Issue credits and refunds within a limit, adjust invoices | Change network settings |
| NOC engineer | Create and update incidents, change service profiles | See payment details |
| Admin | Manage staff roles and limits | Act without an audit log entry |
Enforce the customer boundary in the database as well as in the app. With PostgreSQL row-level security, a query that forgets its WHERE clause still returns only the caller's rows:
ALTER TABLE tickets ENABLE ROW LEVEL SECURITY;
-- The app sets app.account_id per request for customer sessions.
CREATE POLICY customer_own_tickets ON tickets
FOR SELECT
USING (account_id = current_setting('app.account_id', true)::bigint);
Staff queries run under a separate database role with their own policies. The full pattern, including the pitfalls, is in the PostgreSQL row-level security guide. Log every staff action on a customer account; disputes about credits and disconnections are settled from that log.
Ask for step-up verification, such as a one-time code, before the actions an attacker would want: changing the payment method, adding an authorised user, or, for mobile providers, anything touching number porting or a SIM. Rules on contract changes and outage compensation vary by country. This is general information; confirm your obligations with your adviser and your regulator, such as Ofcom in the UK.
Why RAITHub for this
RAITHub has not built a telecom portal, and does not claim to. What transfers directly:
- Staff roles at scale. Sundor Skin, a B2B wholesale platform RAITHub built, has 12 staff roles, 88 permission codes and PostgreSQL row-level security, which is the staff-versus-customer split above.
- Recurring payments in a portal. PropDesk, a property management platform, collects recurring rent through Stripe, runs 5 scheduled jobs and has 1,024 automated tests.
- Portals that face the public. This site runs rate limiting without Redis, the kind of protection a login or fault-report form needs.
A portal over existing billing and ticketing systems suits a fixed written quote after the free 15-minute technical audit. See the SaaS development service and RAITHub's work.
When you don't need us
- Your billing platform's portal fits your plans. Use it, and brand it.
- You bill simple card subscriptions through Stripe. The hosted customer portal covers payment methods, invoices and cancellation.
- You need BSS replacement or TM Forum conformance. Choose a vendor with proven delivery in that area.
Planning a portal over your billing, ticketing and network systems? Tell RAITHub which systems you run today, and book the free audit.
Last reviewed: 1 October 2026. Vendor features and Stripe limits from each vendor's pages on that date.
Frequently asked questions
What is an ISP customer self-service portal?
A website where broadband or telecom subscribers manage their own account: update details, change plan, check usage, view and pay bills, report faults and see outage status, without calling support.
Can the Stripe customer portal handle ISP plan changes?
For simple single-product plans, yes. Stripe's documentation says subscriptions with multiple products or usage-based billing can be cancelled in the portal but not updated, and plan switching is limited to 10 products.
Why use a state machine for plan changes?
Because billing and network provisioning can fail separately. A state machine with guarded transitions makes sure a customer is never charged without the new plan being applied, or given a plan without paying, and that races between webhooks and jobs are harmless.
How do I show outages only to affected customers?
Link each incident to the network elements it affects, such as a POP, tower or cabinet, and link each customer's service to the element that serves it. The portal then shows an incident only to customers on those elements.
What is the difference between staff and customer access in an ISP portal?
Customers act only on their own account, and the database should enforce that with row-level security. Staff work across accounts but within role limits, such as a credit ceiling for support agents, and every staff action is logged.
Should an ISP build a portal or use the one in its billing software?
Use the bundled portal if your plans fit it. Build custom when data lives in several systems, your plans do not fit the vendor's model, or you run a custom billing core with no portal of its own.
Related posts
Ready to discuss your project?
Book a free 15-minute technical audit with our engineering team.