Skip to content

API Overview

All API endpoints use the /api/v1/ prefix. This prefix is centralized in packages/shared via SERVICE_PREFIXES.api and the apiPath() builder function, so future URI changes only require updating one file.

Authenticated endpoints require a JWT Bearer token in the Authorization header:

Authorization: Bearer <token>

Obtain a token via POST /api/v1/auth/login.

All errors follow a standard format:

{
"error": "Human-readable message",
"code": "MACHINE_CODE",
"details": {}
}
MethodPathDescription
POST/api/v1/auth/registerCreate admin account + tenant
POST/api/v1/auth/loginLogin, returns JWT (or changeToken if force-password-change)
POST/api/v1/auth/change-passwordComplete forced password change (purpose-scoped JWT)
POST/api/v1/auth/accept-inviteAccept team invite, set password
POST/api/v1/auth/contractor-tokenGenerate contractor inspection token (configurable TTL)
MethodPathAuthDescription
GET/api/v1/auth/meJWTCurrent user info + tenant config
MethodPathAuthDescription
GET/api/v1/leadsJWTList leads (paginated, sortable, filterable)
GET/api/v1/leads/:idJWTLead detail with scoring breakdown
POST/api/v1/leadsJWT (admin/member)Create a lead
PATCH/api/v1/leads/:idJWT (admin/member)Update a lead
DELETE/api/v1/leads/:idJWT (admin)Delete a lead
POST/api/v1/leads/:id/convertJWT (admin/member)Convert lead to customer
MethodPathAuthDescription
GET/api/v1/customersJWTList customers (paginated)
GET/api/v1/customers/:idJWTCustomer detail
POST/api/v1/customersJWT (admin/member)Create a customer
PATCH/api/v1/customers/:idJWT (admin/member)Update a customer
MethodPathAuthDescription
GET/api/v1/projectsJWTList projects
GET/api/v1/projects/:idJWTProject detail
POST/api/v1/projectsJWT (admin/member)Create a project
PATCH/api/v1/projects/:idJWT (admin/member)Update a project
MethodPathAuthDescription
GET/api/v1/estimatesJWTList estimates (auto-expires stale quotes)
GET/api/v1/estimates/:idJWTEstimate detail with line items
POST/api/v1/estimatesJWT (admin/member)Create estimate (generates display_id QT-{seq}-{rev})
PATCH/api/v1/estimates/:idJWT (admin/member)Update estimate (content-locked for non-draft)
POST/api/v1/estimates/:id/duplicateJWT (admin/member)Create revision (supersedes original)
POST/api/v1/estimates/:id/cloneJWT (admin/member)Independent copy with new identity
POST/api/v1/estimates/:id/renewJWT (admin/member)Renew expired quote with new expiration
POST/api/v1/estimates/:id/shareJWT (admin/member)Generate share token (30-day default)
POST/api/v1/estimates/:id/convertJWT (admin/member)Convert to project (creates tasks from line items)
POST/api/v1/estimates/:id/archiveJWT (admin/member)Archive estimate
POST/api/v1/estimates/:id/unarchiveJWT (admin/member)Unarchive estimate
GET/api/v1/estimates/:id/pdfJWTHTML rendering (format=html param for raw HTML)
POST/api/v1/estimates/previewJWTRender HTML from builder data (no DB write)
MethodPathAuthDescription
GET/api/v1/estimates/:id/attachmentsJWTList attachments (ordered by sort_order)
POST/api/v1/estimates/:id/attachmentsJWT (admin/member)Upload attachment (multipart/form-data, draft only)
DELETE/api/v1/estimates/:id/attachments/:aidJWT (admin/member)Delete attachment (removes from R2 + DB)
GET/api/v1/estimates/:id/attachments/:aid/downloadJWTDownload attachment (streams from R2)
MethodPathDescription
GET/api/v1/public/quote/:tokenPublic quote HTML view (checks token expiry)
GET/api/v1/public/quote/:token/pdfPublic quote PDF download
GET/api/v1/public/quote/:token/attachments/:aidPublic attachment download
MethodPathAuthDescription
GET/api/v1/catalog/itemsJWTList catalog items
POST/api/v1/catalog/itemsJWT (admin)Create catalog item
PATCH/api/v1/catalog/items/:idJWT (admin)Update catalog item
DELETE/api/v1/catalog/items/:idJWT (admin)Delete catalog item
GET/api/v1/catalog/unit-typesJWTList unit types
POST/api/v1/catalog/unit-typesJWT (admin)Create unit type
PATCH/api/v1/catalog/unit-types/:idJWT (admin)Update unit type
DELETE/api/v1/catalog/unit-types/:idJWT (admin)Delete unit type
MethodPathAuthDescription
GET/api/v1/activitiesJWTList activities (filterable by lead/customer)
GET/api/v1/activities/recentJWTRecent activity feed
POST/api/v1/activitiesJWT (admin/member)Create activity/note
MethodPathAuthDescription
GET/api/v1/dashboard/statsJWTDashboard aggregations
GET/api/v1/dashboard/attentionJWTNeeds-attention items
MethodPathAuthDescription
GET/api/v1/tenantJWTTenant config
PATCH/api/v1/tenantJWT (admin)Update tenant config
GET/api/v1/tenant/configJWTTenant config_json settings
MethodPathAuthDescription
POST/api/v1/assets/intake/uploadJWT (admin/member/contractor)Upload an intake or inspection file to R2. Accepts multipart/form-data with fields: file (image, max 10MB), entityId (lead or customer ID), action (e.g. inspection, intake). Returns { url, id } where url is the R2 key.
GET/api/v1/assets/intake/download?key=...JWT (admin/member/contractor)Download a file from R2 by its storage key. Returns the file with appropriate Content-Type.

R2 key format: intake-photos/{entityId}/{action}/{YYYY-MM-DD}/{filename}

Defaults to unlinked if no entityId is provided and general if no action is provided.

MethodPathAuthDescription
GET/api/v1/searchJWTCross-entity search
POST/api/v1/geocodeJWT (admin/member)On-demand Nominatim geocoding
GET/api/v1/preferencesJWTUser preferences
PUT/api/v1/preferences/:keyJWTSet preference
POST/api/v1/webhooks/intakeSignatureIntake form submission webhook
GET/api/v1/healthNoneHealth check
POST/api/v1/communications/callJWT (admin/member)Log outbound call
POST/api/v1/communications/smsJWT (admin/member)Log outbound SMS
MethodPathAuthDescription
POST/api/v1/inspections/draftJWT (contractor)Save inspection draft
GET/api/v1/inspections/draftJWT (contractor)Get inspection draft
MethodPathDescription
POST/api/v1/admin/generate-leadsGenerate fake leads via intake pipeline
POST/api/v1/admin/generate-customersGenerate fake customers (leads → convert)
POST/api/v1/admin/leads/soft-deleteSoft-delete all leads
POST/api/v1/admin/leads/hard-deleteHard-delete all leads
POST/api/v1/admin/customers/soft-deleteSoft-delete all customers
POST/api/v1/admin/customers/hard-deleteHard-delete all customers
GET/api/v1/admin/system-infoDB size, record counts
GET/api/v1/admin/registration-statusRegistration toggle state
POST/api/v1/admin/create-accountCreate user account
GET/api/v1/admin/usersList tenant users
POST/api/v1/admin/reset-passwordReset user password
POST/api/v1/admin/toggle-force-password-changeToggle force-password-change flag

See the Schemas reference for request/response field details.