ObjectStackObjectStack

Discovery schema — API Protocol reference

Discovery schemas of the ObjectStack API Protocol: ApiRoutes, AuthFamilies and 10 more — each property with its type, default and a TypeScript example.

Source: packages/spec/src/api/discovery.zod.ts

TypeScript Usage

import { ApiRoutesSchema, AuthFamiliesSchema, CapabilityDescriptorSchema, DiscoverySchema, DiscoveryEnvironmentSchema, EnvironmentTypeSchema, RouteHealthEntrySchema, RouteHealthReportSchema, ServiceInfoSchema, ServiceSelfInfoSchema, ServiceStatus, WellKnownCapabilitiesSchema } from '@objectstack/spec/api';
import type { ApiRoutes, AuthFamilies, CapabilityDescriptor, DiscoveryEnvironment, EnvironmentType, RouteHealthEntry, RouteHealthReport, ServiceInfo, ServiceSelfInfo, ServiceStatus, WellKnownCapabilities } from '@objectstack/spec/api';

// Validate data
const result = ApiRoutesSchema.parse(data);

ApiRoutes

Properties

PropertyTypeRequiredDescription
datastring✅e.g. /api/v1/data
metadatastring✅e.g. /api/v1/meta
discoverystringoptionale.g. /api/v1/discovery
uistringoptionale.g. /api/v1/ui
authstringoptionale.g. /api/v1/auth
automationstringoptionale.g. /api/v1/automation
storagestringoptionale.g. /api/v1/storage
analyticsstringoptionale.g. /api/v1/analytics
packagesstringoptionale.g. /api/v1/packages
datasourcesstringoptionale.g. /api/v1/datasources — base for the datasources/:name/external/* federation-admin family; absent when no host mounts it
emailstringoptionale.g. /api/v1/email — base for the email/send endpoint; absent when no host mounts it
approvalsstringoptionale.g. /api/v1/approvals
realtimestringoptionale.g. /api/v1/realtime — present only when a realtime transport is actually mounted
notificationsstringoptionale.g. /api/v1/notifications
aistringoptionale.g. /api/v1/ai
i18nstringoptionale.g. /api/v1/i18n
mcpstringoptionale.g. /api/v1/mcp — always the unscoped base; absent when MCP is disabled or unserveable

AuthFamilies

Which optional better-auth route families are mounted under routes.auth

Properties

PropertyTypeRequiredDescription
adminboolean✅Whether the better-auth admin family ({routes.auth}/admin/*: list-users, set-role, update-user, ban-user, …) is mounted. Same value as features.admin on GET {routes.auth}/config, read from the same source; false means those routes answer a plain 404 on this deployment.

CapabilityDescriptor

Properties

PropertyTypeRequiredDescription
enabledboolean✅Whether this capability is available
featuresRecord<string, boolean>optionalSub-feature flags within this capability
descriptionstringoptionalHuman-readable capability description

Discovery

Properties

PropertyTypeRequiredDescription
namestring✅
versionstring✅
environmentEnum<'production' | 'sandbox' | 'development'>✅Deployment posture a discovery response advertises. Deliberately three coarse buckets — a client reads this to answer "am I talking to production?", not to identify a specific environment (that is sys_environment / EnvironmentTypeSchema, a richer 7-member taxonomy).
routes{ data: string; metadata: string; discovery?: string; ui?: string; … }✅
locale{ default: string; supported: string[]; timezone: string }✅
servicesRecord<string, { enabled: boolean; status: Enum<'available' | 'registered' | 'unavailable' | 'degraded' | 'stub'>; handlerReady?: boolean; route?: string; … }>✅Per-service availability map keyed by CoreServiceName
capabilities{ comments: object; automation: object; cron: object; search: object; … }✅Hierarchical capability descriptors — the full WellKnownCapabilities vocabulary, every key present
schemaDiscovery{ openapi?: string; jsonSchema?: string }optionalSchema discovery endpoints for API toolchain integration
scoping{ enabled: boolean; resolution: Enum<'required' | 'optional' | 'auto'>; scoped: boolean; environmentId?: string }optionalEnvironment-scoping posture, added by the REST discovery endpoint
authFamilies{ admin: boolean }optionalWhich optional better-auth route families are mounted under routes.auth — the same source as GET {routes.auth}/config features; absent when no auth service answers
metadataRecord<string, any>optionalCustom metadata key-value pairs for extensibility

Nested Shape: Discovery.routes

PropertyTypeRequiredDescription
datastring✅e.g. /api/v1/data
metadatastring✅e.g. /api/v1/meta
discoverystringoptionale.g. /api/v1/discovery
uistringoptionale.g. /api/v1/ui
authstringoptionale.g. /api/v1/auth
automationstringoptionale.g. /api/v1/automation
storagestringoptionale.g. /api/v1/storage
analyticsstringoptionale.g. /api/v1/analytics
packagesstringoptionale.g. /api/v1/packages
datasourcesstringoptionale.g. /api/v1/datasources — base for the datasources/:name/external/* federation-admin family; absent when no host mounts it
emailstringoptionale.g. /api/v1/email — base for the email/send endpoint; absent when no host mounts it
approvalsstringoptionale.g. /api/v1/approvals
realtimestringoptionale.g. /api/v1/realtime — present only when a realtime transport is actually mounted
notificationsstringoptionale.g. /api/v1/notifications
aistringoptionale.g. /api/v1/ai
i18nstringoptionale.g. /api/v1/i18n
mcpstringoptionale.g. /api/v1/mcp — always the unscoped base; absent when MCP is disabled or unserveable

Nested Shape: Discovery.services[string]

PropertyTypeRequiredDescription
enabledboolean✅Whether the slot is filled by something this host delivers. NOT "a channel exists": subscribing requires handlerReady:true AND a connectable route (isSubscribableChannel).
statusEnum<'available' | 'registered' | 'unavailable' | 'degraded' | 'stub'>✅available = fully operational, registered = route declared but handler unverified, unavailable = not installed, degraded = partial, stub = placeholder that returns 501
handlerReadybooleanoptionalWhether the HTTP handler is confirmed to be mounted. Omitted = readiness unknown/unverified; true = handler mounted; false = handler missing or stub (likely 501).
routestringoptionale.g. /api/v1/analytics
providerstringoptionale.g. "objectql", "plugin-redis", "driver-memory"
versionstringoptionalSemantic version of the service implementation (e.g. "3.0.6")
messagestringoptionale.g. "Install plugin-workflow to enable"
rateLimit{ requestsPerMinute?: integer; requestsPerHour?: integer; burstLimit?: integer; retryAfterMs?: integer }optionalRate limit and quota info for this service

Nested Shape: Discovery.capabilities

PropertyTypeRequiredDescription
comments{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend supports record comments / chatter (the sys_comment object served via the data API)
automation{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend supports Automation CRUD (flows, triggers)
cron{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend supports cron scheduling
search{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend supports full-text search
export{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend supports async export
chunkedUpload{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend supports chunked (multipart) uploads
transactionalBatch{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend exposes the atomic cross-object batch endpoint (POST {basePath}/batch, /ADR-0034): all ops commit or roll back together in one transaction. Lets clients skip non-atomic client-side simulation instead of runtime-probing 404/405/501. True ⟺ the /batch route is mounted AND the runtime can honour a transaction.
websockets{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend mounts a realtime push surface (WebSocket/SSE) clients can subscribe to. Derived from isSubscribableChannel(services.realtime): handlerReady true AND a connectable route.
files{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether a file-storage surface (upload/download/attachments) is served
analytics{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend serves the analytics / BI query surface
ai{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend serves the AI surface (NLQ, chat, agents, suggest)
notifications{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend serves the notification surface (inbox, delivery)
i18n{ enabled: boolean; features?: Record<string, boolean>; description?: string }✅Whether the backend serves the i18n surface (translations, locale negotiation)

Nested Shape: Discovery.schemaDiscovery

PropertyTypeRequiredDescription
openapistringoptionalURL to OpenAPI (Swagger) specification (e.g., "/api/v1/openapi.json")
jsonSchemastringoptionalURL to JSON Schema definitions

Nested Shape: Discovery.scoping

PropertyTypeRequiredDescription
enabledboolean✅Whether environment-scoped routes are mounted at all
resolutionEnum<'required' | 'optional' | 'auto'>✅How the environment id is resolved when scoping is enabled (mirrors RestApiConfig.projectResolution)
scopedboolean✅Whether THIS response was served from the environment-scoped mount
environmentIdstringoptionalThe resolved environment id — present only on a scoped mount

Nested Shape: Discovery.authFamilies

PropertyTypeRequiredDescription
adminboolean✅Whether the better-auth admin family ({routes.auth}/admin/*: list-users, set-role, update-user, ban-user, …) is mounted. Same value as features.admin on GET {routes.auth}/config, read from the same source; false means those routes answer a plain 404 on this deployment.

DiscoveryEnvironment

Deployment posture a discovery response advertises. Deliberately three coarse buckets — a client reads this to answer "am I talking to production?", not to identify a specific environment (that is sys_environment / EnvironmentTypeSchema, a richer 7-member taxonomy).

Allowed Values

  • production
  • sandbox
  • development

EnvironmentType

Environment categorical tag (prod/sandbox/dev/test/…)

Allowed Values

  • production
  • sandbox
  • development
  • test
  • staging
  • preview
  • trial

RouteHealthEntry

Properties

PropertyTypeRequiredDescription
routestring✅Route path pattern
methodEnum<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'>✅HTTP method (GET, POST, etc.)
servicestring✅Target service name
declaredboolean✅Whether the route is declared in discovery/metadata
handlerRegisteredboolean✅Whether the HTTP handler is registered
healthStatusEnum<'pass' | 'fail' | 'missing' | 'skip'>✅pass = handler responds, fail = 501/503, missing = no handler (404), skip = not checked
messagestringoptionalDiagnostic message

RouteHealthReport

Properties

PropertyTypeRequiredDescription
timestampstring✅ISO 8601 timestamp of report generation
adapterstring✅Adapter or runtime that produced this report
totalDeclaredinteger✅Total routes declared in discovery
totalRegisteredinteger✅Routes with confirmed handler
totalMissinginteger✅Routes missing a handler
routes{ route: string; method: Enum<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'>; service: string; declared: boolean; … }[]✅Per-route health entries

Nested Shape: RouteHealthReport.routes[number]

PropertyTypeRequiredDescription
routestring✅Route path pattern
methodEnum<'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'>✅HTTP method (GET, POST, etc.)
servicestring✅Target service name
declaredboolean✅Whether the route is declared in discovery/metadata
handlerRegisteredboolean✅Whether the HTTP handler is registered
healthStatusEnum<'pass' | 'fail' | 'missing' | 'skip'>✅pass = handler responds, fail = 501/503, missing = no handler (404), skip = not checked
messagestringoptionalDiagnostic message

ServiceInfo

Properties

PropertyTypeRequiredDescription
enabledboolean✅Whether the slot is filled by something this host delivers. NOT "a channel exists": subscribing requires handlerReady:true AND a connectable route (isSubscribableChannel).
statusEnum<'available' | 'registered' | 'unavailable' | 'degraded' | 'stub'>✅available = fully operational, registered = route declared but handler unverified, unavailable = not installed, degraded = partial, stub = placeholder that returns 501
handlerReadybooleanoptionalWhether the HTTP handler is confirmed to be mounted. Omitted = readiness unknown/unverified; true = handler mounted; false = handler missing or stub (likely 501).
routestringoptionale.g. /api/v1/analytics
providerstringoptionale.g. "objectql", "plugin-redis", "driver-memory"
versionstringoptionalSemantic version of the service implementation (e.g. "3.0.6")
messagestringoptionale.g. "Install plugin-workflow to enable"
rateLimit{ requestsPerMinute?: integer; requestsPerHour?: integer; burstLimit?: integer; retryAfterMs?: integer }optionalRate limit and quota info for this service

Nested Shape: ServiceInfo.rateLimit

PropertyTypeRequiredDescription
requestsPerMinuteintegeroptionalMaximum requests per minute
requestsPerHourintegeroptionalMaximum requests per hour
burstLimitintegeroptionalMaximum burst request count
retryAfterMsintegeroptionalSuggested retry-after delay in milliseconds when rate-limited

ServiceSelfInfo

Properties

PropertyTypeRequiredDescription
statusEnum<'stub' | 'degraded'>✅stub = placeholder or dev fake (do not use for real work); degraded = functional fallback with reduced capability
handlerReadybooleanoptionalWhether the HTTP handler genuinely serves requests. Defaults: false for stub, true for degraded.
messagestringoptionalHuman-readable explanation, e.g. what to install for the full implementation

ServiceStatus

available = fully operational, registered = route declared but handler unverified, unavailable = not installed, degraded = partial, stub = placeholder that returns 501

Allowed Values

  • available
  • registered
  • unavailable
  • degraded
  • stub

WellKnownCapabilities

Well-known capability flags for frontend intelligent adaptation

Properties

PropertyTypeRequiredDescription
commentsboolean✅Whether the backend supports record comments / chatter (the sys_comment object served via the data API)
automationboolean✅Whether the backend supports Automation CRUD (flows, triggers)
cronboolean✅Whether the backend supports cron scheduling
searchboolean✅Whether the backend supports full-text search
exportboolean✅Whether the backend supports async export
chunkedUploadboolean✅Whether the backend supports chunked (multipart) uploads
transactionalBatchboolean✅Whether the backend exposes the atomic cross-object batch endpoint (POST {basePath}/batch, /ADR-0034): all ops commit or roll back together in one transaction. Lets clients skip non-atomic client-side simulation instead of runtime-probing 404/405/501. True ⟺ the /batch route is mounted AND the runtime can honour a transaction.
websocketsboolean✅Whether the backend mounts a realtime push surface (WebSocket/SSE) clients can subscribe to. Derived from isSubscribableChannel(services.realtime): handlerReady true AND a connectable route.
filesboolean✅Whether a file-storage surface (upload/download/attachments) is served
analyticsboolean✅Whether the backend serves the analytics / BI query surface
aiboolean✅Whether the backend serves the AI surface (NLQ, chat, agents, suggest)
notificationsboolean✅Whether the backend serves the notification surface (inbox, delivery)
i18nboolean✅Whether the backend serves the i18n surface (translations, locale negotiation)

On this page