Customer Portal
The Customer Portal gives end-users in your managed organizations a self-service interface for common IT tasks. Portal users can view their organization’s devices, submit and track support tickets, check out and return shared assets, and manage their own profile – all without needing access to the full Breeze admin dashboard.
Each organization gets its own portal instance with customizable branding, a dedicated user directory, and granular feature toggles. Portal users authenticate with their own credentials, separate from admin accounts, and sessions are scoped to their organization.
Accessing the Portal
Section titled “Accessing the Portal”The portal is served under the /portal path on your Breeze domain — for example, if your dashboard is at https://breeze.example.com, portal users sign in at https://breeze.example.com/portal/login. No separate hostname or DNS record is required. Share this link (or the per-quote/invoice links Breeze emails, which already point at /portal) with your end-users.
Key Concepts
Section titled “Key Concepts”Portal Feature Toggles
Section titled “Portal Feature Toggles”Each organization can enable or disable portal features independently through the branding configuration.
| Feature | Default | Description |
|---|---|---|
enableTickets |
true |
Allow portal users to submit and track support tickets |
enableAssetCheckout |
false |
Allow portal users to check out and return shared devices (parked: API only, no portal UI yet) |
enableSelfService |
true |
Allow portal users to view organization devices |
enablePasswordReset |
true |
Allow portal users to reset their password via email |
Visibility settings
Section titled “Visibility settings”The service-insight sections — Dashboard, Security, Backups, Reports, and support usage — are controlled separately, per organization, and every one of them starts off. You decide which customers get to see what.
| Toggle | Default | What the customer sees |
|---|---|---|
| Dashboard | Off | A service-health summary when they sign in |
| Security | Off | Security posture, protection, threats, and vulnerabilities |
| Backups | Off | Backup coverage, verification, restore tests, and readiness |
| Reports | Off | The ability to generate and download approved reports |
| Support usage | Off | Month-to-date billable support-time totals, and SLA badges in the ticket list |
To change them:
-
Go to Settings → Organizations and open the organization.
-
Open the Customer Portal tab (under Portal & Branding).
-
In the Visibility panel, turn on the sections this customer should see. Enable all visibility turns on all five at once.
-
Save. The customer’s next page load reflects the change.
You need the Organizations: write permission and a multi-factor confirmation to change these.
Ticket Statuses
Section titled “Ticket Statuses”| Status | Meaning |
|---|---|
new |
Ticket just submitted, not yet triaged |
open |
Ticket acknowledged and being worked on |
pending |
Waiting for additional information from the submitter |
on_hold |
Work paused, awaiting external dependency |
resolved |
Issue fixed, awaiting confirmation |
closed |
Ticket fully closed |
Ticket Priorities
Section titled “Ticket Priorities”| Priority | Description |
|---|---|
low |
Non-urgent issue, can be addressed during normal operations |
normal |
Standard priority (default for new tickets) |
high |
Important issue requiring prompt attention |
urgent |
Critical issue requiring immediate response |
Portal User Statuses
Section titled “Portal User Statuses”| Status | Behavior |
|---|---|
active |
User can log in and access portal features |
| Any other value | Login is blocked with a 403 response |
Authentication
Section titled “Authentication”Portal authentication is separate from the main Breeze admin auth system. Portal users have their own credentials stored in the portal_users table and sessions are managed through either Redis (production) or in-memory storage (development).
Session Management
Section titled “Session Management”Sessions use 48-character tokens generated by nanoid. Each authenticated request slides the session expiry forward, keeping active users logged in.
| Setting | Value |
|---|---|
| Session TTL | 24 hours (sliding) |
| Reset token TTL | 1 hour |
| Cookie name | breeze_portal_session |
| CSRF cookie name | breeze_portal_csrf_token |
| Auth methods | Bearer token, session cookie |
Rate Limits
Section titled “Rate Limits”The portal applies per-IP and per-account rate limiting to authentication endpoints to prevent brute-force attacks.
| Endpoint | Window | Max Attempts | Block Duration |
|---|---|---|---|
| Login | 5 minutes | 10 | 15 minutes |
| Forgot password | 15 minutes | 5 | 30 minutes |
| Reset password | 15 minutes | 10 | 30 minutes |
Branding
Section titled “Branding”Each organization can customize the portal appearance and contact information. Branding is resolved by domain — when a portal user visits a configured custom domain, Breeze serves that organization’s matching branding. Because custom-domain serving is not enabled yet, the shared /portal portal currently shows the default branding at the login screen; configured branding still applies to the organization elsewhere.
Branding Fields
Section titled “Branding Fields”| Field | Type | Description |
|---|---|---|
logoUrl |
URL | Organization logo displayed in the portal header |
faviconUrl |
URL | Browser tab icon |
primaryColor |
CSS color | Main brand color |
secondaryColor |
CSS color | Secondary accent color |
accentColor |
CSS color | Highlight color for interactive elements |
customDomain |
String | Custom domain for the portal (e.g., support.acme.com) |
domainVerified |
Boolean | Whether the custom domain has been verified |
welcomeMessage |
Text | Welcome text displayed on the portal login page |
supportEmail |
Support contact email shown in the portal | |
supportPhone |
Phone | Support phone number shown in the portal |
footerText |
Text | Custom footer text |
customCss |
CSS | Custom CSS injected into the portal pages |
Dashboard
Section titled “Dashboard”When Dashboard visibility is on, signing in lands the customer on a service-health summary instead of a device list: what is being looked after, what needs attention, and what has been done for them recently. It is the answer to “what am I paying you for”, written for a business owner rather than a technician.
Leave it off and portal users land on their devices as before.
Devices
Section titled “Devices”The device list shows what the customer’s own devices are doing, in plain language: operating system, whether the device is protected, when it was last patched, whether the disk is encrypted, when it last backed up, and when the manufacturer warranty ends. Values Breeze cannot establish read Not known rather than being guessed at.
Export CSV downloads the same list for a customer who wants it in a spreadsheet — for an insurance questionnaire, an asset register, or their own auditor.
The device list is governed by the existing Self-service feature toggle, not by the visibility settings.
Security
Section titled “Security”With Security visibility on, the customer gets a posture overview — endpoint protection coverage, outstanding threats, and vulnerability exposure — plus per-device protection detail so they can see which machines are the reason a number is not green.
This is a summary for the customer, not a console. Nothing here lets a portal user change protection settings or dismiss a finding.
Backups
Section titled “Backups”With Backups visibility on, the customer sees backup coverage across their devices, whether recent backups verified, when restores were last tested, and an overall readiness picture. It answers the question a customer only asks after something has gone wrong, before it goes wrong.
Reports
Section titled “Reports”With Reports visibility on, the customer can generate and download reports themselves rather than emailing you for them:
- Generate security summary — the security and compliance posture report
- Generate executive summary — the executive summary report
A finished run offers both PDF and CSV downloads. These are the same report types described in Reports; only the trigger is different.
With Reports off, a customer who navigates to the reports URL is redirected to their proposals — the same as every other section that is off.
Tickets
Section titled “Tickets”Portal users can submit support tickets, track their status, and add comments to ongoing conversations. Tickets submitted through the portal land directly in the technician Tickets queue in the Breeze dashboard, where technicians triage, assign, and respond to them.
Creating a Ticket
Section titled “Creating a Ticket”- Log in to the customer portal.
- Navigate to the Tickets section.
- Click New Ticket and fill in the subject, description, and priority.
- Submit the ticket. A unique ticket number is auto-generated.
Tickets are automatically associated with the submitter’s portal user account and organization. Each ticket receives a unique alphanumeric ticket number for easy reference.
Ticket Comments
Section titled “Ticket Comments”Both portal users and technicians can add comments to tickets. When a technician sends a public reply from the Tickets queue, it appears in the portal user’s ticket conversation. Portal users only see public comments (isPublic: true). Internal notes added by technicians with isPublic: false are never shown in the portal view.
Files a technician attaches to a public reply appear beneath it in the portal conversation — images inline, other files as download links. Attachments on internal notes are never exposed. Portal users cannot upload attachments of their own.
SLA badges and support usage
Section titled “SLA badges and support usage”Turning on Support usage visibility adds two things to the customer’s view: an SLA badge on tickets in the list (the ticket detail always shows one), and a month-to-date total of billable support time. Use it with customers on a block-hours or time-and-materials arrangement who would otherwise find the invoice a surprise; leave it off for customers on a flat-rate agreement, where a running time total invites the wrong conversation.
External Ticket Integration
Section titled “External Ticket Integration”Tickets support linking to external ticketing systems through the externalTicketId and externalTicketUrl fields, enabling integration with tools like ConnectWise, Autotask, or Zendesk.
Invoices & Payments
Section titled “Invoices & Payments”Portal users can view their organization’s issued invoices, download the PDF, and – when you’ve connected Stripe – pay any invoice with an outstanding balance by card online.
A customer does not need a portal account to pay. Every issued invoice also has its own private link that the MSP can email or paste anywhere; it opens the same view, download, and pay page with no sign-in, and can be revoked at any time. See Online Payments.
Paying an Invoice
Section titled “Paying an Invoice”- Log in to the customer portal and open Invoices.
- Select an invoice to see its line items, total, and balance due.
- Click Pay now to open a secure Stripe Checkout page and pay by card.
- Once payment succeeds, the invoice’s balance and status update automatically.
Only issued invoices appear in the portal – drafts are never shown. The Pay now button appears only on invoices with a balance due, and only when the partner has connected Stripe. Payments collected here settle directly into the MSP’s own Stripe account; see Online Payments for how to connect Stripe and send payment links.
Asset Checkout
Section titled “Asset Checkout”The asset checkout system lets portal users borrow shared devices from their organization’s inventory and return them when finished.
Checking Out an Asset
Section titled “Checking Out an Asset”- Navigate to Assets in the portal.
- Browse the list of available devices (devices not currently checked out).
- Select a device and click Check Out.
- Optionally provide an expected return date, condition notes, and checkout notes.
- Confirm the checkout.
Returning an Asset
Section titled “Returning an Asset”- Navigate to Assets in the portal.
- Find your checked-out asset and click Check In.
- Optionally provide return condition notes and check-in notes.
- Confirm the check-in.
Checkout Fields
Section titled “Checkout Fields”| Field | Required | Description |
|---|---|---|
expectedReturnAt |
No | ISO 8601 datetime for expected return |
checkoutNotes |
No | Notes about the checkout (max 2000 chars) |
condition |
No | Condition of the device at checkout (max 100 chars) |
Check-in Fields
Section titled “Check-in Fields”| Field | Required | Description |
|---|---|---|
checkinNotes |
No | Notes about the return (max 2000 chars) |
condition |
No | Condition of the device at return (max 100 chars) |
Profile Management
Section titled “Profile Management”Portal users can view and update their profile, change their password, and manage notification preferences.
Updating Profile
Section titled “Updating Profile”Send a PATCH to the profile endpoint with any combination of:
| Field | Type | Description |
|---|---|---|
name |
String (1-255 chars) | Display name |
receiveNotifications |
Boolean | Whether to receive email notifications |
password |
String (min 8 chars) | New password (replaces current) |
Changing Password
Section titled “Changing Password”The dedicated password change endpoint requires the current password for verification before accepting a new password. After a successful password change, all existing sessions for the user are invalidated.
API Reference
Section titled “API Reference”All portal endpoints are mounted under /api/v1/portal. Public routes (branding, auth) do not require authentication. Protected routes require a valid portal session token.
Authentication
Section titled “Authentication”| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/auth/login |
No | Log in with email and password |
POST |
/auth/forgot-password |
No | Request a password reset email |
POST |
/auth/reset-password |
No | Reset password with a reset token |
POST |
/auth/logout |
Yes | Log out and invalidate the current session |
Branding
Section titled “Branding”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/branding |
No | Get branding for the current domain (resolved from Host header) |
GET |
/branding/:domain |
No | Get branding for a specific domain |
Devices
Section titled “Devices”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/devices |
Yes | List devices in the portal user’s organization |
GET |
/devices/export.csv |
Yes | Download the same list as CSV |
Dashboard
Section titled “Dashboard”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/dashboard |
Yes | Service-health summary. Requires Dashboard visibility |
Security
Section titled “Security”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/security/overview |
Yes | Security posture summary. Requires Security visibility |
GET |
/security/devices |
Yes | Per-device protection detail. Requires Security visibility |
Backups
Section titled “Backups”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/backups/overview |
Yes | Backup coverage and readiness. Requires Backups visibility |
GET |
/backups/devices |
Yes | Per-device backup detail. Requires Backups visibility |
Reports
Section titled “Reports”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/reports/runs |
Yes | List the customer’s report runs. Requires Reports visibility |
POST |
/reports/generate |
Yes | Generate a report. Requires Reports visibility |
GET |
/reports/runs/:id/pdf |
Yes | Download a finished run as PDF |
GET |
/reports/runs/:id/csv |
Yes | Download a finished run as CSV |
Tickets
Section titled “Tickets”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/tickets |
Yes | List tickets submitted by the current portal user |
POST |
/tickets |
Yes | Create a new support ticket |
GET |
/tickets/:id |
Yes | Get ticket details with public comments |
POST |
/tickets/:id/comments |
Yes | Add a comment to a ticket |
GET |
/tickets/usage |
Yes | Month-to-date billable support time. Requires Support usage visibility |
Invoices
Section titled “Invoices”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/invoices |
Yes | List the organization’s issued invoices |
GET |
/invoices/:id |
Yes | Get invoice detail with customer-visible lines |
GET |
/invoices/:id/pdf |
Yes | Download the invoice PDF |
POST |
/invoices/:id/pay |
Yes | Start a Stripe Checkout session to pay the balance |
Assets
Section titled “Assets”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/assets |
Yes | List available devices for checkout |
POST |
/assets/:id/checkout |
Yes | Check out a device |
POST |
/assets/:id/checkin |
Yes | Check in a device |
Profile
Section titled “Profile”| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/profile |
Yes | Get current portal user profile |
PATCH |
/profile |
Yes | Update profile (name, notifications, password) |
POST |
/profile/password |
Yes | Change password (requires current password) |
Caching
Section titled “Caching”Portal API responses use HTTP cache headers for optimal performance.
| Route Type | Cache Scope | Browser Max-Age | Stale-While-Revalidate |
|---|---|---|---|
| Branding | public |
5 minutes | 24 hours |
| Devices, Tickets, Assets | private |
15 seconds | 90 seconds |
| Profile | private |
15 seconds | 60 seconds |
All authenticated endpoints also return ETag headers. Clients that send If-None-Match receive a 304 Not Modified response when the data has not changed, saving bandwidth.
Audit Logging
Section titled “Audit Logging”All mutating portal actions are recorded in the audit log with an actorType of user and actions prefixed with portal.:
| Action | Trigger |
|---|---|
portal.ticket.create |
New ticket submitted |
portal.ticket.comment.create |
Comment added to a ticket |
portal.asset.checkout |
Device checked out |
portal.asset.checkin |
Device checked in |
portal.profile.update |
Profile updated |
portal.profile.password.change |
Password changed |
Troubleshooting
Section titled “Troubleshooting”Portal login returns “Multiple portal accounts found”
Section titled “Portal login returns “Multiple portal accounts found””This error occurs when the same email address exists in multiple organizations and no orgId was provided in the login request. The portal frontend should prompt the user to select their organization or include the orgId field in the login payload.
Custom domain branding returns 404
Section titled “Custom domain branding returns 404”Verify that the domainVerified field is set to true for the organization’s branding record. Branding is only served for verified domains.
CSRF token errors on form submissions
Section titled “CSRF token errors on form submissions”When using cookie-based authentication, ensure your frontend reads the breeze_portal_csrf_token cookie and sends its value in the x-breeze-csrf request header on every POST, PATCH, and DELETE request.
“Service temporarily unavailable” (503) on login
Section titled ““Service temporarily unavailable” (503) on login”In production mode, portal sessions require Redis. If Redis is unreachable, the portal returns 503. Verify your Redis connection is healthy and the PORTAL_STATE_BACKEND environment variable is set correctly.
Rate limited (429) responses
Section titled “Rate limited (429) responses”Rate limits apply per IP address and per account. After being blocked, wait for the duration specified in the Retry-After response header. Rate limit state is automatically cleared on successful login.