API Endpoints (Overview)
This page lists available endpoint groups, routing rules, auth model, and practical request examples.
Authentication
- API access uses API keys generated in Configuration → API Keys (UI).
- Tokens are shown once; store securely.
- Header format:
Authorization: ApiKey <token>for API key calls
- API keys sent with
Bearerare rejected.
Tenant routing (important)
The backend resolves the tenant/workspace from the request Host header:
- the tenant is inferred from the subdomain (the first
.-separated segment)
Recommended production pattern:
https://<tenant-subdomain>.invymate.com/api/v1/...
Local/test hosts depend on your deployment setup, but tenant is always host/subdomain-derived.
The public tenant bootstrap response used by the auth shell includes:
existstenantIdssoProviderssoConfiguredssoLoginEnabledssoOnlyEnabledssoAccess
The sign-in screen always keeps Google login available for non-SSO workspaces, and shows both Google and SSO only when the workspace owner has enabled the SSO-only gate.
ssoProvider reflects the configured workspace SSO protocol, such as oidc for Auth0-style OpenID Connect setups or saml for metadata-driven SAML setups.
ssoConfigured means an SSO config record exists.
ssoLoginEnabled means the workspace owner has enabled the SSO-only gate, so the login screen should expose SSO.
When ssoOnlyEnabled is true:
- the password sign-in screen hides the password form by default
- the dedicated SSO entrypoint is
/auth/sso-login, which calls the backend/api/v1/auth/sso-loginredirect helper and sends the user to the workspace's configured SSO provider - the backend SSO route is feature-gated and returns
403when the workspace plan does not include SSO - workspace owners can still use the password sign-in path as break-glass recovery from the SSO page
- SSO configuration lives in the dedicated
Configuration → SSOnode - only the owner can open the Configuration area and maintain the SSO draft
- SSO is available whenever the workspace subscription includes the SSO feature flag
- the service-provider callback is fixed by the selected protocol:
- SAML uses
/api/v1/auth/saml/callback - OIDC uses
/api/v1/auth/oidc/callback
- SAML uses
- the callback host must be the tenant subdomain, for example
https://<tenant-subdomain>.invymate.com/api/v1/auth/oidc/callback - SAML login uses the IdP metadata URL to discover the provider's sign-in endpoint and signing certificate
- the service-provider metadata is available at
/api/v1/auth/saml/metadatafor IdP setup
SSO onboarding sequence:
- the workspace admin first assigns the user to the application or group inside the SSO provider
- the Invymate owner then invites that same email address in Invymate
- SSO sign-in matches by email, so the identity provider email must match the invited Invymate user email
- if the user is still in the invite state and has not been created in
usersyet, the first OIDC or SAML login consumes the invite and creates the user record - after that first login, the user can authenticate through the SSO button on the login page
Tenant SAML config
SSO draft configuration is exposed as a separate tenant subresource.
GET /tenants/current/saml-configPATCH /tenants/current/saml-config
Access pattern:
- owners can read the current draft with
GET - owners can update the draft with
PATCH - the owner saves the draft directly from the UI
- the UI shows help tooltips next to each SSO field so owners can see which IdP value belongs in each input
- the UI lets owners choose between SAML and OIDC and shows the protocol-specific fields
- the callback URL shown in the UI is the fixed backend endpoint for the chosen protocol, so copy it into the IdP instead of editing it manually
The request body for PATCH is the selected protocol config itself. You do not include a tenant ID in the body:
provider:samloroidc
SAML fields:
entityId: the SAML issuer/entity identifieracsUrl: the assertion consumer service URL. This is the fixed backend callback endpoint and is not tenant-editable.idpMetadataUrl: optional IdP metadata URLidpCertificate: optional IdP certificate textssoOnlyEnabled: optional toggle for SSO-only access, editable by workspace owners in Configuration → SSOssoAccess: true when the workspace plan includes SSOssoLoginEnabled: true when the workspace owner has enabled the SSO-only gate, so the login screen should expose SSO
OIDC fields:
issuerUrl: the OIDC issuer URLdiscoveryUrl: optional OIDC discovery document URLclientId: the OIDC client IDclientSecret: the OIDC client secretredirectUri: the fixed backend callback endpoint for OIDCscopes: optional space-separated scopes, defaulting toopenid profile emailssoOnlyEnabled: optional toggle for SSO-only access, editable by workspace owners in Configuration → SSO
Validation rules:
provideris requiredentityIdandacsUrlare required for SAML- at least one of
idpMetadataUrloridpCertificatemust be present acsUrlandidpMetadataUrlmust be valid URLs when presentissuerUrl,discoveryUrl, andredirectUrimust be valid URLs when presentclientIdandclientSecretare required for OIDC
Response fields include the current workspace SSO draft used by the UI.
Common patterns
- Pagination/filtering/sorting vary by endpoint; see per-endpoint sections below.
- Standard error responses include
messageand optionaldetails. - Webhook management routes are JWT-only even when API Access is enabled.
- Internally, asset command endpoints are routed through an inventory-item facade so create/update/checkout/check-in/status/image workflows share one application entry point.
- Architecture note: inventory item keeps its public domain barrel focused on facade/controllers/validation/types. Asset list filtering, detail, assignment history, audit, status-event, timeline, health, import fields, quick-add logging, and bulk import/export scheduling now run through the inventory-item facade, with focused unit coverage for facade orchestration and list filtering/enrichment. Session endpoints now use the same facade boundary for list/get/create/update/export/include/exclude and other controller orchestration, with focused facade coverage for those read-after-write and export paths plus narrow controller coverage around export/include/exclude HTTP responses and error forwarding. Session unscan cleanup, include/exclude membership edits, completion-side scan application, update guards, start-session snapshot setup, and the update workflow itself now live in focused application modules. The public
sessionbarrel is narrowed to facade/types while routes import controllers and validation explicitly. Maintenance endpoints now follow the same pattern through aMaintenanceFacadefor schedule/log controller orchestration, whilecreateLogbranching,deleteLogschedule recalculation/health refresh,deleteSchedule,updateLog, and schedule create/update due-date wiring now live in focused application modules and routes import maintenance controllers and validation explicitly while workers/asset-attention logic use internal reader paths plus a shared due-date helper. Location endpoints now follow the same pattern through aLocationFacadefor controller orchestration, with create/update/restore reloads behind the facade, routes importing location controllers and validation explicitly, and tenant/session/trash/worker internals using explicit reader/service paths. Person endpoints now follow the same pattern through aPersonFacadefor controller orchestration, with create/update/restore reloads behind the facade, routes importing person controllers and validation explicitly, and auth/tenant/session/search/trash/worker internals using explicit reader/service/type paths. Category endpoints now follow the same pattern through aCategoryFacadefor controller orchestration, with create/update/restore reloads behind the facade, routes importing category controllers and validation explicitly, and tenant/trash/worker/inventory internals using explicit service paths. Notification endpoints now keep the same boundary with aNotificationFacade, an index narrowed to facade/types, explicit route imports for controllers and validation, focused facade coverage around read/write delegation, a small application helper for non-failing realtime event emission, and owner-targeted realtime events that use post-update payloads. Custom-field endpoints now follow the same pattern through aCustomFieldFacadefor controller orchestration, with create/update/restore reloads behind the facade, routes importing custom-field controllers and validation explicitly, domain bootstrap using explicit model/schema imports, and dedicated application modules for both create and update workflows so reserved-name checks, duplicate detection, entity-change guards, rename propagation, and unique-index queue orchestration sit outside the service class. Session-schedule endpoints now follow the same pattern through aSessionScheduleFacadefor controller orchestration, with create/update reloads and trigger input shaping behind the facade, routes importing session-schedule controllers and validation explicitly, bootstrap/worker code using explicit model/service imports, and dedicated application workflows for manual trigger idempotency and ahead-of-time session generation. Layout endpoints now follow the same pattern through aLayoutFacadefor controller orchestration, with update reloads behind the facade, routes importing layout controllers and validation explicitly, tenant initialization/hotfix paths using explicit layout service/type imports, and a dedicated application helper for composing default and custom layout fields outsideLayoutReader. User endpoints now follow the same pattern through aUserFacadefor controller orchestration, with request-user union shaping behind the facade, routes importing user controllers and validation explicitly, auth/tenant/middleware/hotfix code using explicit user reader/service/model/type imports instead of the broad public barrel, and an application workflow for user updates so admin-limit checks, usage accounting, and API-key revocation stay outsideUserService. Auth routes now also import controllers and validation explicitly, the publicauthbarrel is intentionally empty, password sign-in, invited sign-up, email-verification account activation, Google sign-in, and Google invite acceptance each sit in dedicated application workflows so invitation lookup, role shaping, provider checks, password verification, pending-user cleanup, tenant trial activation, and Google-account validation are out of the controller, and auth helpers now own Google auth URL generation, callback token/profile exchange, tenant redirect URL shaping, and cold-start regression coverage that public auth/tenant/invited-member route imports stay free of Mongo, request-context, and mail-client side effects. Audit internals now also stay off the public barrel, withaudit/index.tsnarrowed to shared types only and plugins, bootstrap, stats, and inventory timeline code importing audit models and schemas explicitly. Invited-member endpoints now follow the same pattern through anInvitedMemberFacade, with invite/resend email delivery context loading in a dedicated application helper, routes importing invited-member controllers and validation explicitly, and auth/user/bootstrap internals using explicit invited-member service/model/schema/type paths instead of the broad public barrel. Feedback endpoints now keep the same boundary withFeedbackFacade, an index narrowed to facade/types, explicit route imports for controllers and validation, and focused facade coverage around submission email/storage orchestration. Tenant endpoints now start following the same pattern through aTenantFacadefor current-tenant lifecycle checks and signup read-after-write flows, with routes importing tenant controllers and validation explicitly,tenant/index.tsnarrowed to facade/types, a dedicated application helper handling expired-tenant suspension checks outside the controller, middleware/auth/paddle/inventory/token/user internals using explicit tenant service/model/schema/type paths instead of the broad public barrel, regression coverage that importing tenant controllers before Mongo connects does not touch the shared connection, and lazy mail-client construction so controller or service imports do not requireRESEND_API_KEYat startup. Pending-user internals now also stay off the public barrel, withpendingUser/index.tsnarrowed to shared types only and auth, tenant, bootstrap, and hotfix code importing pending-user models, schemas, and services explicitly. Password-request internals now follow the same rule, withpasswordRequest/index.tsnarrowed to shared types only and bootstrap plus user-service code importing password-request models and schemas explicitly. Subscription internals now follow the same rule, withsubscription/index.tsnarrowed to shared types only and admin, middleware, and domain-bootstrap code importing subscription models, schemas, and entitlements from explicit internal paths. Scan-event endpoints now keep the same boundary withScanEventFacade, an index narrowed to facade/types, explicit route imports for controllers, focused facade coverage around item-history loading, and internal scan-event DTO helpers kept on explicitscanEvent/libimports rather than re-exported publicly. Search endpoints now keep the same boundary withSearchFacade, an index narrowed to the facade only, explicit route imports for controllers, and focused facade coverage around query sanitization and tenant-search delegation. Object endpoints now keep the same boundary withObjectFacade, an index narrowed to the facade only, explicit route imports for controllers and validation, and focused facade coverage around presigned upload/view URL delegation. Stats endpoints now keep the same boundary withStatsFacadefor KPI, lifecycle, maintenance, depreciation, replacement-loss, overview, session analytics, session-data, recent-activity, analytics-report, and report-data orchestration, an index narrowed to facade/types, explicit route imports for controllers, focused helper coverage around KPI aggregation, lifecycle shaping, maintenance shaping, depreciation shaping, overview shaping, session-analytics shaping, session summary shaping, and recent-activity feed shaping, plus focused facade coverage around replacement-loss and report delegation while existing query builders continue to power the detailed report payloads. Paddle checkout endpoints now also follow the same pattern through aPaddleFacade, with routes importing paddle controllers and validation explicitly, checkout token/data orchestration behind the facade, shared plan/tenant/user checkout context loading in a focused application helper, and subscription model access moved off the broadsubscriptionbarrel. Trash endpoints now also keep the same boundary with aTrashFacade, an index narrowed to facade/types, explicit route imports for controllers and validation, focused facade coverage around deleted-item reads plus restore/permanent-delete delegation, and cold-start regression coverage so importing trash controllers does not require request context before middleware initializes it.
Contract docs
Use these docs together:
- API Access: key creation and auth setup
- API Endpoints (Overview): endpoint groups, auth model, and query patterns (this page)
- Webhooks: delivery model and webhook signatures
Endpoints available via API key
All endpoints below are under the /api/v1 prefix and accept API key auth unless noted.
Categories
Base: /categories
GET /categoriesGET /categories/defaultPOST /categoriesPATCH /categories/:categoryIdDELETE /categories/:categoryIdPOST /categories/:categoryId/restore
Locations
Base: /locations
GET /locationsGET /locations/defaultPOST /locationsPATCH /locations/:locationIdDELETE /locations/:locationIdPOST /locations/:locationId/restore
Assets (Inventory items)
Base: /inventory-items
GET /inventory-itemsPOST /inventory-itemsGET /inventory-items/:inventoryItemIdGET /inventory-items/unique-tag/:uniqueTagPATCH /inventory-items/:inventoryItemIdDELETE /inventory-items/:inventoryItemIdPOST /inventory-items/:inventoryItemId/restoreGET /inventory-items/:inventoryItemId/assignment-historyGET /inventory-items/:inventoryItemId/auditGET /inventory-items/:inventoryItemId/allowed-status-transitionsGET /inventory-items/:inventoryItemId/status-eventsGET /inventory-items/:inventoryItemId/timelineGET /inventory-items/:inventoryItemId/healthPOST /inventory-items/health/batchPOST /inventory-items/:inventoryItemId/statusPOST /inventory-items/:inventoryItemId/checkoutPOST /inventory-items/:inventoryItemId/checkinPOST /inventory-items/quick-add/events(JWT auth)
GET /inventory-items list view now includes per-item health:
health.score(0..100)health.badge(HEALTHY|ATTENTION|AT_RISK)health.reasons[]
GET /inventory-items supports health list controls:
healthBadge(HEALTHY|ATTENTION|AT_RISK)healthSort(risk_first|healthy_first) — used by the Assets table Health column sort
GET /inventory-items/:inventoryItemId/timeline query params:
limit(default50, max100)cursor(opaque pagination cursor from previous response)types(optional comma-separated list):CREATEDASSIGNEDUNASSIGNEDMOVEDMAINTENANCE_COMPLETEDVERIFIEDSTATUS_CHANGEDRETIRED
GET /inventory-items/:inventoryItemId/health returns:
score(0..100)badge(HEALTHY|ATTENTION|AT_RISK)reasons[]with reason codes and optional links
Health scoring is tenant-aware: workspace sensitivity presets can make the same signals read as more conservative or more strict, while severe states like lost still hard-fail. Reasons can include maintenance, verification, lifecycle age, and warranty signals when that data exists.
POST /inventory-items/health/batch accepts:
ids[](asset ids, 1..200)
Returns:
items[]withid,score,badge,reasons
POST /inventory-items/:inventoryItemId/status accepts:
toStatusreason(required forretired)meta(optional context, e.g. reservation details)
Example:
{
"toStatus": "reserved",
"reason": "Reserved for onboarding",
"meta": {
"reservedUntil": "2026-03-01T09:00:00.000Z",
"reservationReason": "New hire starts Monday"
}
}
For maintenance, optional meta keys include:
- no special keys are required; completion is tracked via
POST /maintenance/logs
POST /inventory-items/:inventoryItemId/checkout accepts:
personId(required)dueBackAt(optional ISO date/time)
POST /inventory-items/:inventoryItemId/checkin:
- clears assignee and records check-in timestamps/history
- keeps location unchanged
POST /inventory-items/quick-add/events accepts:
event(quick_add_opened|asset_created_quick|asset_assigned|advanced_opened)flowId- optional
itemId - optional
meta
Ad-hoc maintenance completion example:
{
"itemId": "<inventory-item-id>",
"type": "repair",
"completedDate": "2026-02-22T10:00:00.000Z",
"notes": "Fan replaced and thermal test passed"
}
People
Base: /people
GET /peoplePOST /peopleGET /people/:personIdPATCH /people/:personIdDELETE /people/:personIdPOST /people/:personId/restoreGET /people/:personId/assignment-history
Inventory sessions (audits)
Base: /sessions
GET /sessionsGET /sessions/activeGET /sessions/:sessionIdPOST /sessionsPATCH /sessions/:sessionIdDELETE /sessions/:sessionIdGET /sessions/:id/itemsGET /sessions/:id/items/:itemIdPOST /sessions/:id/items/:itemId/includePOST /sessions/:id/items/:itemId/excludeGET /sessions/:id/missingGET /sessions/:id/reportPOST /sessions/:id/scanPOST /sessions/:id/unscanGET /sessions/:id/mobile
POST /sessions/:id/scan is idempotent and returns:
acceptedalready_scannednot_in_sessionwrong_location(for location-scoped sessions unlessallowMarkAnywayis true)
Mobile/client scan payload accepts either:
itemId, orcode(QR value such as an asset URL)
Optional scan flags:
allowMarkAnyway(for location mismatch override)allowRescan(explicitly re-write an already scanned item event)
GET /sessions/:id/mobile returns a fast mobile payload:
- session identity/status/location
- progress counts (
scanned,total) - recent scans list for scan-loop UI
Stats / analytics
Base: /stats/analytics
GET /stats/analytics/overviewGET /stats/analytics/sessionsGET /stats/analytics/lifecycleGET /stats/analytics/maintenanceGET /stats/analytics/depreciationGET /stats/analytics/replacement-lossGET /stats/analytics/reports
GET /stats/analytics/replacement-loss provides:
- age buckets + age-by-category + oldest assets
- warranty expiry counts/lists (30/60/90 days)
- long-assigned assets and threshold counts
- missing-rate trends (location/category) + repeatedly unscanned assets
Custom fields
Base: /custom-fields
GET /custom-fieldsPOST /custom-fieldsPATCH /custom-fields/:customFieldIdDELETE /custom-fields/:customFieldIdPOST /custom-fields/:customFieldId/restore
Custom-field definitions support dropdown options in addition to text, number, date, and checkbox types. Dropdown option lists are trimmed on save, cannot contain blanks, cannot contain duplicates case-insensitively, and must include at least one option.
Feedback
Base: /feedback (JWT auth)
POST /feedback
POST /feedback accepts a feedback title/message and optional image attachments. Attachments are limited to PNG, JPEG/JPG, or GIF files up to 5 MB each.
Objects
Base: /objects (JWT auth)
POST /objectsGET /objects/:objectKey/view-url
Object routes issue presigned upload and view URLs for tenant-scoped object keys. They are used by attachment/image workflows and do not accept API key authentication.
Search
Base: /search (JWT auth)
GET /search?q=<query>
Search returns grouped quick-search results across assets, people, and inventory sessions. It is intended for authenticated app search flows and does not accept API key authentication.
Scan events
Base: /scan-events (JWT auth)
GET /scan-events/items/:itemId/history
Scan event history returns paginated scan records for one asset, including session context, condition, comments, photos, location, and scanner details.
Notifications
Base: /notifications (JWT auth)
GET /notificationsGET /notifications/unread-countPATCH /notifications/read-allPATCH /notifications/:notificationId
Notification routes are authenticated app endpoints for reading and updating in-app notification state. They do not accept API key authentication.
Maintenance
Base: /maintenance
Schedules:
POST /maintenance/schedulesGET /maintenance/schedules/due/allGET /maintenance/schedules/item/:itemIdGET /maintenance/schedules/:idPATCH /maintenance/schedules/:idDELETE /maintenance/schedules/:id
GET /maintenance/schedules/due/all query params:
lookaheadDays(1..60, default7)- Alias also accepted:
reminderDays
Reminder classification:
overdue:nextDueDate < nowdue today:nextDueDateis todayupcoming:nextDueDate > nowand within schedulereminderDays
Logs:
POST /maintenance/logsGET /maintenance/logs/item/:itemIdGET /maintenance/logs/schedule/:scheduleIdGET /maintenance/logs/:idPATCH /maintenance/logs/:idDELETE /maintenance/logs/:id
Webhooks (management API)
Base: /webhooks
Auth:
- Requires JWT auth (
Authorization: Bearer <jwt>) - API key auth is not supported on webhook management routes
- Pro feature gate applies
Endpoints:
GET /webhooks/endpointsPOST /webhooks/endpointsPATCH /webhooks/endpoints/:idDELETE /webhooks/endpoints/:idPOST /webhooks/endpoints/:id/verifyGET /webhooks/healthGET /webhooks/deliveriesGET /webhooks/deliveries/:idPOST /webhooks/deliveries/:id/replayPOST /webhooks/events/:id/replay
GET /webhooks/deliveries query params:
endpointId(optional)status(optional)eventType(optional)dateFrom(optional ISO date/time)dateTo(optional ISO date/time)limit(1..100, optional)
GET /webhooks/health query params:
windowMinutes(5..1440, optional, default60)
Trash (restore / permanent delete)
Base: /trash
GET /trashPOST /trash/restorePOST /trash/permanent-delete
Admin diagnostics (JWT admin cookie)
The admin API includes a TT10 report endpoint for quick-add onboarding diagnostics:
GET /admin/tenants/:tenantId/tt10-report?from=<ISO>&to=<ISO>
Response includes per-user entries with:
- first created time
- tenth assigned time
- computed
tt10Minutes meetsTarget(tt10Minutes < 10)