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.
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 | true | Allow portal users to check out and return shared devices |
enableSelfService | true | Allow portal users to view organization devices |
enablePasswordReset | true | Allow portal users to reset their password via email |
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 custom domain — when a portal user visits their organization’s custom domain, Breeze serves the matching branding configuration.
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 |
Tickets
Section titled “Tickets”Portal users can submit support tickets, track their status, and add comments to ongoing conversations.
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 admin technicians can add comments to tickets. Portal users only see public comments (isPublic: true). Internal notes added by technicians with isPublic: false are hidden from the portal view.
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.
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 |
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 |
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.