Server API and OpenAPI
Customer Portal uses Nitro file-based handlers. The assembled application generates its API reference from the handlers present in portal core and every enabled feature layer.
Discover a deployed API
Each running portal exposes:
| Route | Purpose |
|---|---|
/api-docs | Scalar interactive API browser. |
/api-docs/swagger | Swagger UI for the same document. |
/api-docs/openapi.json | Ordered OpenAPI document enriched with Better Auth operations and shared contracts. |
/api-docs/openapi.raw.json | Nitro's unmerged generated document, intended for internal assembly. |
Use the ordered /api-docs/openapi.json document for clients and reviews. It reflects the layers installed in that deployment, so it is more authoritative than a copied endpoint list.
Current product surface
At the documented master revision, the assembled repository contains 111 Nitro API handler files. The count is a review signal rather than an API version: a catch-all handler can contribute multiple OpenAPI operations, and a handler can respond to one method only.
| Owning layer | Handler files | Route families | Primary responsibility |
|---|---|---|---|
core | 3 | /api/auth/**, /api/notifications | Better Auth transport, current permissions, and notification feed |
ui | 0 | — | Neutral presentation only |
authentication | 0 | — | Uses core identity handlers and owns only account-entry UI |
organizations | 10 | /api/organizations/**, /api/profile | Profile, invitations, organization details, and email-provider credentials |
administration | 14 | /api/admin/** | Installation-wide users, organizations, memberships, invitations, and roles |
service-requests | 8 | /api/service-requests/** | Customer and organization-administrator request workflows |
timesheets | 76 | /api/timesheets/** | Entry, timers, setup, approvals, reporting, clients, invoices, PDFs, email, and cross-organization access |
preset | 0 | — | Composition only |
kit | 0 | — | Configuration and migration CLI only |
CI compares these layer and handler counts with the pinned public repository tree. When the product source pin changes, a mismatch requires the inventory and relevant narrative documentation to be reviewed together. For the exact operations, parameters, and response schemas in a deployment, use its generated OpenAPI document.
Authentication and tenancy
Most product handlers are session APIs rather than token-based public APIs. A protected feature operation should:
- require an authenticated session;
- derive the active organization from that session;
- evaluate a typed feature action against the user's organization role;
- scope every tenant-owned query by the returned organization ID;
- validate path, query, and body input before repository access.
import { definePortalRouteMeta, requireFeatureAccess } from '@nuxt-customer-portal/core/server'
import { exampleFeature } from '../../../shared/feature'
import { createExampleSchema } from '../../utils/example-validation'
definePortalRouteMeta({
openAPI: {
tags: ['Example'],
operationId: 'exampleCreate',
summary: 'Create an example record',
description: 'Creates a record in the active organization.'
},
body: createExampleSchema
})
export default defineEventHandler(async (event) => {
const { session, organizationId } = await requireFeatureAccess(
event,
exampleFeature.policy,
'create'
)
const input = createExampleSchema.parse(await readBody(event))
return createExample({
...input,
organizationId,
createdById: session.user.id
})
})
Do not accept organizationId from a normal browser request when the active session already determines the tenant. Administrative cross-organization operations require a separate, explicit system-administrator check.
OpenAPI metadata
Every contributor-owned handler should call definePortalRouteMeta() beside the route with:
- one stable, unique
operationId; - a domain tag such as
TimesheetsorService requests; - an imperative summary;
- a description that states authentication, tenant scope, and the relevant permission;
- request and response schemas when Nitro cannot infer enough detail.
Keep the metadata beside the handler so moving or removing a layer also moves or removes its API description.
Error semantics
Core adapters establish these baseline responses:
| Status | Meaning |
|---|---|
400 | The session has no active organization or validated input is invalid. |
401 | No authenticated user session. |
403 | The user lacks organization membership or the required feature action. |
404 | A scoped record does not exist or is not visible in the active organization. |
409 | The requested transition conflicts with current domain state. |
Feature repositories should avoid revealing whether a record exists in another organization. Validation failures should be useful to the caller without exposing secrets, SQL, or credentials.
Stability
The generated OpenAPI document describes the current deployment; it is not yet a versioned public API guarantee. Pin the Customer Portal commit used by an integration and review the document when upgrading. See Compatibility and releases.