ObjectStackObjectStack

Protocol

Protocol protocol schemas

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

TypeScript Usage

import { AiAgentCapabilitiesSchema, AiAgentChatRequestSchema, AiAgentSummarySchema, AiAgentsResponseSchema, AiChatRequestSchema, AiChatResponseSchema, AiCompleteRequestSchema, AiConversationSchema, AiMessageSchema, AiModelsResponseSchema, AiPendingActionSchema, AiPendingActionStatusSchema, AiStreamChunkSchema, ApproveAiPendingActionResponseSchema, AuditMetaItemRequestSchema, AuditMetaItemResponseSchema, AutomationActionsResponseSchema, AutomationTriggerRequestSchema, AutomationTriggerResponseSchema, BatchDataRequestSchema, BatchDataResponseSchema, CheckPermissionRequestSchema, CheckPermissionResponseSchema, CloneDataResponseSchema, CreateAiConversationRequestSchema, CreateDataRequestSchema, CreateDataResponseSchema, CreateManyDataRequestSchema, CreateManyDataResponseSchema, DeleteDataRequestSchema, DeleteDataResponseSchema, DeleteManyDataRequestSchema, DeleteManyDataResponseSchema, DeleteMetaItemRequestSchema, DeleteMetaItemResponseSchema, DiffMetaItemResponseSchema, DisablePackageRequestSchema, DisablePackageResponseSchema, EnablePackageRequestSchema, EnablePackageResponseSchema, FindDataRequestSchema, FindDataResponseSchema, FindReferencesToMetaResponseSchema, GetDataRequestSchema, GetDataResponseSchema, GetDiscoveryRequestSchema, GetDiscoveryResponseSchema, GetEffectivePermissionsRequestSchema, GetEffectivePermissionsResponseSchema, GetFieldLabelsRequestSchema, GetFieldLabelsResponseSchema, GetLocalesRequestSchema, GetLocalesResponseSchema, GetMetaDiagnosticsResponseSchema, GetMetaItemCachedRequestSchema, GetMetaItemCachedResponseSchema, GetMetaItemLayeredRequestSchema, GetMetaItemLayeredResponseSchema, GetMetaItemRequestSchema, GetMetaItemResponseSchema, GetMetaItemsRequestSchema, GetMetaItemsResponseSchema, GetMetaTypesRequestSchema, GetMetaTypesResponseSchema, GetNotificationPreferencesRequestSchema, GetNotificationPreferencesResponseSchema, GetObjectPermissionsRequestSchema, GetObjectPermissionsResponseSchema, GetPackageRequestSchema, GetPackageResponseSchema, GetPresenceRequestSchema, GetPresenceResponseSchema, GetPublishedMetaItemResponseSchema, GetTranslationsRequestSchema, GetTranslationsResponseSchema, GetUiViewRequestSchema, GetUiViewResponseSchema, HistoryMetaItemRequestSchema, HistoryMetaItemResponseSchema, HttpFindQueryParamsSchema, InstallPackageRequestSchema, InstallPackageResponseSchema, ListAiConversationsRequestSchema, ListAiConversationsResponseSchema, ListAiPendingActionsRequestSchema, ListAiPendingActionsResponseSchema, ListDraftsResponseSchema, ListNotificationsRequestSchema, ListNotificationsResponseSchema, ListPackagesRequestSchema, ListPackagesResponseSchema, MarkAllNotificationsReadRequestSchema, MarkAllNotificationsReadResponseSchema, MarkNotificationsReadRequestSchema, MarkNotificationsReadResponseSchema, NotificationSchema, NotificationPreferencesSchema, PublishMetaItemRequestSchema, PublishMetaItemResponseSchema, PublishPackageDraftsResponseSchema, RealtimeConnectRequestSchema, RealtimeConnectResponseSchema, RealtimeDisconnectRequestSchema, RealtimeDisconnectResponseSchema, RealtimeSubscribeRequestSchema, RealtimeSubscribeResponseSchema, RealtimeUnsubscribeRequestSchema, RealtimeUnsubscribeResponseSchema, RegisterDeviceRequestSchema, RegisterDeviceResponseSchema, RejectAiPendingActionResponseSchema, RollbackMetaItemResponseSchema, RuntimeAuthoringIssueSchema, SaveMetaItemRequestSchema, SaveMetaItemResponseSchema, SearchAllHitSchema, SearchAllResponseSchema, SetPresenceRequestSchema, SetPresenceResponseSchema, UninstallPackageRequestSchema, UninstallPackageResponseSchema, UnregisterDeviceRequestSchema, UnregisterDeviceResponseSchema, UpdateAiConversationRequestSchema, UpdateDataRequestSchema, UpdateDataResponseSchema, UpdateManyDataRequestSchema, UpdateManyDataResponseSchema, UpdateNotificationPreferencesRequestSchema, UpdateNotificationPreferencesResponseSchema, ValidateDataIssueSchema, ValidateDataRequestSchema, ValidateDataResponseSchema } from '@objectstack/spec/api';
import type { AiAgentCapabilities, AiAgentChatRequest, AiAgentSummary, AiAgentsResponse, AiChatRequest, AiChatResponse, AiCompleteRequest, AiConversation, AiMessage, AiModelsResponse, AiPendingAction, AiPendingActionStatus, AiStreamChunk, ApproveAiPendingActionResponse, AuditMetaItemRequest, AuditMetaItemResponse, AutomationActionsResponse, AutomationTriggerRequest, AutomationTriggerResponse, BatchDataRequest, BatchDataResponse, CheckPermissionRequest, CheckPermissionResponse, CloneDataResponse, CreateAiConversationRequest, CreateDataRequest, CreateDataResponse, CreateManyDataRequest, CreateManyDataResponse, DeleteDataRequest, DeleteDataResponse, DeleteManyDataRequest, DeleteManyDataResponse, DeleteMetaItemRequest, DeleteMetaItemResponse, DiffMetaItemResponse, DisablePackageRequest, DisablePackageResponse, EnablePackageRequest, EnablePackageResponse, FindDataRequest, FindDataResponse, FindReferencesToMetaResponse, GetDataRequest, GetDataResponse, GetDiscoveryRequest, GetDiscoveryResponse, GetEffectivePermissionsRequest, GetEffectivePermissionsResponse, GetFieldLabelsRequest, GetFieldLabelsResponse, GetLocalesRequest, GetLocalesResponse, GetMetaDiagnosticsResponse, GetMetaItemCachedRequest, GetMetaItemCachedResponse, GetMetaItemLayeredRequest, GetMetaItemLayeredResponse, GetMetaItemRequest, GetMetaItemResponse, GetMetaItemsRequest, GetMetaItemsResponse, GetMetaTypesRequest, GetMetaTypesResponse, GetNotificationPreferencesRequest, GetNotificationPreferencesResponse, GetObjectPermissionsRequest, GetObjectPermissionsResponse, GetPackageRequest, GetPackageResponse, GetPresenceRequest, GetPresenceResponse, GetPublishedMetaItemResponse, GetTranslationsRequest, GetTranslationsResponse, GetUiViewRequest, GetUiViewResponse, HistoryMetaItemRequest, HistoryMetaItemResponse, InstallPackageRequest, InstallPackageResponse, ListAiConversationsRequest, ListAiConversationsResponse, ListAiPendingActionsRequest, ListAiPendingActionsResponse, ListDraftsResponse, ListNotificationsRequest, ListNotificationsResponse, ListPackagesRequest, ListPackagesResponse, MarkAllNotificationsReadRequest, MarkAllNotificationsReadResponse, MarkNotificationsReadRequest, MarkNotificationsReadResponse, Notification, NotificationPreferences, PublishMetaItemRequest, PublishMetaItemResponse, PublishPackageDraftsResponse, RealtimeConnectRequest, RealtimeConnectResponse, RealtimeDisconnectRequest, RealtimeDisconnectResponse, RealtimeSubscribeRequest, RealtimeSubscribeResponse, RealtimeUnsubscribeRequest, RealtimeUnsubscribeResponse, RegisterDeviceRequest, RegisterDeviceResponse, RejectAiPendingActionResponse, RollbackMetaItemResponse, RuntimeAuthoringIssue, SaveMetaItemRequest, SaveMetaItemResponse, SearchAllHit, SearchAllResponse, SetPresenceRequest, SetPresenceResponse, UninstallPackageRequest, UninstallPackageResponse, UnregisterDeviceRequest, UnregisterDeviceResponse, UpdateAiConversationRequest, UpdateDataRequest, UpdateDataResponse, UpdateManyDataRequest, UpdateManyDataResponse, UpdateNotificationPreferencesRequest, UpdateNotificationPreferencesResponse, ValidateDataIssue, ValidateDataRequest, ValidateDataResponse } from '@objectstack/spec/api';

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

AiAgentCapabilities

Properties

PropertyTypeRequiredDescription
authoringbooleanAuthors app metadata (objects/views/flows)
canvasbooleanDrives the Live Canvas split view (ADR-0037)
debugbooleanExposes the build-doctor debug drawer
resumebooleanTurns resume durable multi-step runs (ADR-0013)

AiAgentChatRequest

Properties

PropertyTypeRequiredDescription
messages({ role: Enum<'system' | 'user' | 'assistant' | 'tool'>; content?: any; parts?: any[] } & Record<string, any>)[]Conversation messages (at least one)
contextRecord<string, any>optionalAgent context (app, object, record, …)
optionsRecord<string, any>optionalRequest options (model, temperature, …)

Nested Shape: AiAgentChatRequest.messages[number]

PropertyTypeRequiredDescription
roleEnum<'system' | 'user' | 'assistant' | 'tool'>Message role
contentanyoptionalMessage content: a string, or an array of content parts
partsany[]optionalVercel AI SDK v6 message parts (alternative to content)

AiAgentSummary

Properties

PropertyTypeRequiredDescription
namestringAgent name — the :agentName path segment
labelstringDisplay label
rolestringAgent role
capabilities{ authoring: boolean; canvas: boolean; debug: boolean; resume: boolean }Capability set implied by the agent surface

Nested Shape: AiAgentSummary.capabilities

PropertyTypeRequiredDescription
authoringbooleanAuthors app metadata (objects/views/flows)
canvasbooleanDrives the Live Canvas split view (ADR-0037)
debugbooleanExposes the build-doctor debug drawer
resumebooleanTurns resume durable multi-step runs (ADR-0013)

AiAgentsResponse

Properties

PropertyTypeRequiredDescription
agents{ name: string; label: string; role: string; capabilities: object }[]Agents this caller may chat with

Nested Shape: AiAgentsResponse.agents[number]

PropertyTypeRequiredDescription
namestringAgent name — the :agentName path segment
labelstringDisplay label
rolestringAgent role
capabilities{ authoring: boolean; canvas: boolean; debug: boolean; resume: boolean }Capability set implied by the agent surface

AiChatRequest

Properties

PropertyTypeRequiredDescription
messages({ role: Enum<'system' | 'user' | 'assistant' | 'tool'>; content?: any; parts?: any[] } & Record<string, any>)[]Conversation messages (at least one)
systemstringoptionalSystem prompt, prepended as a system message
modelstringoptionalModel id override
temperaturenumberoptionalSampling temperature
maxTokensintegeroptionalMaximum tokens to generate
streambooleanoptionalfalse → JSON response; otherwise the UI Message Stream Protocol
conversationIdstringoptionalConversation to persist this turn into (auto-created when omitted)
turnIdstringoptionalStable per-turn idempotency key (ADR-0013 D1)
optionsRecord<string, any>optionalLegacy nested request options

Nested Shape: AiChatRequest.messages[number]

PropertyTypeRequiredDescription
roleEnum<'system' | 'user' | 'assistant' | 'tool'>Message role
contentanyoptionalMessage content: a string, or an array of content parts
partsany[]optionalVercel AI SDK v6 message parts (alternative to content)

AiChatResponse

Properties

PropertyTypeRequiredDescription
contentstringGenerated text
modelstringoptionalModel that produced it
toolCallsany[]optionalTool calls the model requested (Vercel ToolCallPart)
usage{ promptTokens: number; completionTokens: number; totalTokens: number }optionalToken usage
conversationIdstringoptionalConversation the turn was persisted into

Nested Shape: AiChatResponse.usage

PropertyTypeRequiredDescription
promptTokensnumberTokens consumed by the prompt
completionTokensnumberTokens generated
totalTokensnumberprompt + completion

AiCompleteRequest

Properties

PropertyTypeRequiredDescription
promptstringPrompt text
optionsRecord<string, any>optionalRequest options (model, temperature, maxTokens, …)

AiConversation

Properties

PropertyTypeRequiredDescription
idstringConversation id
titlestringoptionalTitle / summary
agentIdstringoptionalAgent this conversation is bound to
userIdstringoptionalOwning user
messages({ role: Enum<'system' | 'user' | 'assistant' | 'tool'>; content?: any; parts?: any[] } & Record<string, any>)[]Message history
createdAtstringCreation timestamp (ISO 8601)
updatedAtstringLast update timestamp (ISO 8601)
metadataRecord<string, any>optionalConversation metadata

Nested Shape: AiConversation.messages[number]

PropertyTypeRequiredDescription
roleEnum<'system' | 'user' | 'assistant' | 'tool'>Message role
contentanyoptionalMessage content: a string, or an array of content parts
partsany[]optionalVercel AI SDK v6 message parts (alternative to content)

AiMessage

Properties

PropertyTypeRequiredDescription
roleEnum<'system' | 'user' | 'assistant' | 'tool'>Message role
contentanyoptionalMessage content: a string, or an array of content parts
partsany[]optionalVercel AI SDK v6 message parts (alternative to content)

AiModelsResponse

Properties

PropertyTypeRequiredDescription
models(string | { id: string; label: string; default: boolean })[]Models this environment offers
defaultModelstringoptionalDefault model id, when the service reports one

Nested Shape: AiModelsResponse.models[number]

PropertyTypeRequiredDescription
idstringModel id
labelstringDisplay label for the picker
defaultbooleanWhether this is the environment default

AiPendingAction

Properties

PropertyTypeRequiredDescription
idstringPending action id
object_namestringObject the action targets
action_namestringAction name
tool_namestringTool that would execute it
tool_inputstringSerialized tool input
statusEnum<'pending' | 'approved' | 'executed' | 'failed' | 'rejected'>Lifecycle status
resultstringoptionalSerialized result, once executed
errorstringoptionalFailure message, when status is failed
rejection_reasonstringoptionalReason given at rejection
conversation_idstringoptionalConversation that proposed it
message_idstringoptionalMessage that proposed it
proposed_bystringoptionalActor that proposed it
decided_bystringoptionalActor that approved or rejected it
proposed_atstringProposal timestamp (ISO 8601)
decided_atstringoptionalDecision timestamp (ISO 8601)

AiPendingActionStatus

Allowed Values

  • pending
  • approved
  • executed
  • failed
  • rejected

AiStreamChunk

Properties

PropertyTypeRequiredDescription
typestringFrame type (text-delta, tool-input-available, finish, error, …)

ApproveAiPendingActionResponse

Properties

PropertyTypeRequiredDescription
statusEnum<'executed' | 'failed'>Outcome of executing the approved action
resultanyoptionalTool result, when executed
errorstringoptionalFailure reason, when failed

AuditMetaItemRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name
organizationIdstring | nulloptionalOrganization (tenant) scope for the read. With an organization, the trail includes that org's rows AND the env-wide (organization_id IS NULL) rows — the env-wide limb is load-bearing, because env-level writes are stamped org-less. null and absent are equivalent and both mean the env-wide rows only — the fail-closed direction: an unresolved organization reads env-wide rows, never every tenant's.
limitnumberoptionalMaximum events to return, newest first. The implementation clamps to [1, 500] and defaults to 100 — out-of-range values are clamped, never refused.

AuditMetaItemResponse

Properties

PropertyTypeRequiredDescription
events{ id: any; occurredAt: string; actor: string; source: string | null; … }[]Recent protection-audit events for the item, newest first. See the schema-level note for what an empty array means — and what it never means.

Nested Shape: AuditMetaItemResponse.events[number]

PropertyTypeRequiredDescription
idanyRow id of the audit event. Opaque to callers.
occurredAtstringWhen the attempt happened (ISO-8601 string).
actorstringWho attempted the operation. system when the row recorded no actor.
sourcestring | nullWhich code path recorded the event (e.g. protocol.deleteMetaItem). null when the row recorded none.
operationEnum<'save' | 'publish' | 'rollback' | 'delete' | 'reset'>Which metadata-protection door was attempted.
outcomeEnum<'allowed' | 'denied' | 'forced'>Whether the attempt went through, was refused, or overrode a lock (ADR-0010 §3.6).
codestringMachine-readable verdict code for the outcome (e.g. item_locked). Empty string when the row recorded none.
lockStateEnum<'none' | 'no-overlay' | 'no-delete' | 'full'> | nullThe lock verdict in force at the time of the attempt (ADR-0010 §3.3). null when no lock applied.
lockOverriddenbooleanTrue when the attempt went through by overriding a lock (outcome: "forced" rows).
requestIdstring | nullCorrelation id of the originating request. null when the row recorded none.
notestring | nullFree-text note recorded with the event. null when the row recorded none.

AutomationActionsResponse

Properties

PropertyTypeRequiredDescription
actions{ type: string; version: string; name: string; description?: string; … }[]Registered action descriptors (built-in + plugin)
totalintegerNumber of descriptors returned (after any filters)

Nested Shape: AutomationActionsResponse.actions[number]

Canonical cross-paradigm action/node descriptor (ADR-0018)

PropertyTypeRequiredDescription
typestringRegistry action/node type (matches the executor type)
versionstringExecutor version (semver)
namestringDisplay label (or i18n key)
descriptionstringoptionalAction description
iconstringoptionalIcon id resolved by the designer
categoryEnum<'logic' | 'data' | 'io' | 'human' | 'control' | 'custom'>optional (default: "custom")Palette category
paradigmsEnum<'flow' | 'approval'>[]optional (default: ["flow"])Authoring surfaces that may offer this action
configSchemaanyoptionalJSON Schema for the node config (drives the designer form; undeclared keys are rejected at registration)
supportsPausebooleanoptional (default: false)Supports async pause/resume
supportsCancellationbooleanoptional (default: false)Supports cancellation
supportsRetrybooleanoptional (default: true)Supports retry on failure
needsOutboxbooleanoptional (default: false)Dispatch via service-messaging outbox (retry/idempotency/dead-letter)
isAsyncneveroptional[REMOVED] ActionDescriptor.isAsync was removed in @objectstack/spec 17 (ADR-0049) — no execution path ever read it, so declaring it never made a node suspend and omitting it never stopped one. Delete the key. The live mechanism is two-part: an executor suspends by RETURNING suspend: true from execute(), and its descriptor must declare supportsPause: true (plus the resumeAuthority its pauses need) or the engine refuses that suspension. Declaring isAsync: true alongside supportsPause: true was always redundant; declaring it alone was always inert.
handlerContractEnum<'none' | 'pure'>optional (default: "none")Effect contract for author-supplied code this action invokes: 'none' (invokes none) or 'pure' (must not write — it returns a value and the flow graph persists it)
resumeAuthorityEnum<'any' | 'service'>optionalWho may resume a run this node suspended: 'any' (the generic resume route) or 'service' (only the owning service, e.g. approvals). Carries no schema default so an omission stays observable — and an omission is fail-CLOSED at run time, equivalent to 'service': a pausing node whose pause is open to the generic route must declare 'any' explicitly
maturityEnum<'ga' | 'beta' | 'reserved'>optional (default: "ga")Runtime maturity: ga (shipped), beta, or reserved (contract only — designers grey this out)
sourceEnum<'builtin' | 'plugin'>optional (default: "plugin")builtin = platform baseline; plugin = third-party contributed
deprecatedbooleanoptional (default: false)Deprecated alias kept for back-compat
aliasOfstringoptionalCanonical type this alias forwards to

AutomationTriggerRequest

Properties

PropertyTypeRequiredDescription
triggerstring
payloadRecord<string, any>

AutomationTriggerResponse

Properties

PropertyTypeRequiredDescription
successboolean
jobIdstringoptional
resultanyoptional

BatchDataRequest

Properties

PropertyTypeRequiredDescription
objectstringObject name
request{ operation: Enum<'create' | 'update' | 'upsert' | 'delete'>; records: object[]; options?: object }Batch operation request

Nested Shape: BatchDataRequest.request

PropertyTypeRequiredDescription
operationEnum<'create' | 'update' | 'upsert' | 'delete'>Type of batch operation
records{ id?: string; data?: Record<string, any>; externalId?: string }[]Array of records to process (server caps the count — see batch.maxBatchSize)
options{ atomic: boolean; returnRecords: boolean; continueOnError: boolean }optionalBatch operation options

BatchDataResponse

Properties

PropertyTypeRequiredDescription
successbooleanOperation success status
error{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; declaredCode?: string; message: string; userMessage?: string; … }optionalError details if success is false
meta{ timestamp: string; duration?: number; requestId?: string; traceId?: string }optionalResponse metadata
operationEnum<'create' | 'update' | 'upsert' | 'delete'>optionalOperation type that was performed
totalnumberTotal number of records in the batch
succeedednumberNumber of records that succeeded
failednumberNumber of records that failed
results{ id?: string; success: boolean; errors?: object[]; data?: Record<string, any>; … }[]Detailed results for each record

Nested Shape: BatchDataResponse.error

PropertyTypeRequiredDescription
codeEnum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)
declaredCodestringoptionalThe producer-declared code, verbatim, when it is not a member of the closed code vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112)
messagestringReadable error message
userMessagestringoptionalProducer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces message.
categorystringoptionalError category (e.g. validation, authorization)
httpStatusintegeroptionalHTTP status of the response carrying this error
detailsanyoptionalAdditional error context (e.g. field validation errors)
requestIdstringoptionalRequest ID for tracking

Nested Shape: BatchDataResponse.results[number]

PropertyTypeRequiredDescription
idstringoptionalRecord ID if operation succeeded
successbooleanWhether this record was processed successfully
errors{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; declaredCode?: string; message: string; userMessage?: string; … }[]optionalArray of errors if operation failed. Branch on errors[0].code — an atomic batch that rolled back marks rows that were written then undone with code ROLLED_BACK and rows never reached with NOT_ATTEMPTED, while the causal row keeps its own error. A NON-atomic batch that stopped (the continueOnError: false default) marks its un-attempted tail with the same NOT_ATTEMPTED code — rows before the failure stay written and keep reporting success, since nothing was rolled back.
dataRecord<string, any>optionalFull record data (if returnRecords=true)
indexnumberoptionalIndex of the record in the request array
droppedFields{ object: string; fields: string[]; reason: Enum<'readonly' | 'readonly_when' | 'primary_key'> }[]optionalWrite-observability: caller-supplied fields LEGALLY stripped from THIS row before it was written — static readonly / TRUE readonlyWhen on update, or the create-ingress strip. Per-row because a batch can drop different fields on different rows (readonlyWhen is record-state-dependent). Present ONLY when ≥1 field was dropped for this row; the row still succeeded (success unchanged). A single response header cannot express per-row drops, so this body field is the canonical bulk channel — REST does not emit X-ObjectStack-Dropped-Fields for batches. Optional — omit-when-empty keeps the shape backward-compatible.

CheckPermissionRequest

Properties

PropertyTypeRequiredDescription
objectstringObject name to check permissions for
actionEnum<'create' | 'read' | 'edit' | 'delete' | 'transfer' | 'restore' | 'purge'>Action to check
recordIdstringoptionalSpecific record ID (for record-level checks)
fieldstringoptionalSpecific field name (for field-level checks)

CheckPermissionResponse

Properties

PropertyTypeRequiredDescription
allowedbooleanWhether the action is permitted
reasonstringoptionalReason if denied

CloneDataResponse

Properties

PropertyTypeRequiredDescription
objectstringThe object name.
idstringThe ID of the newly created clone.
sourceIdstringThe ID of the record the clone was copied from.
recordRecord<string, any>The created clone, including server-generated fields. Engine-owned values (injected system/audit columns, autonumbers, computed formula/summary fields) are re-derived by the insert path rather than copied from the source; caller-supplied overrides win over copied values.

CreateAiConversationRequest

Properties

PropertyTypeRequiredDescription
titlestringoptionalInitial title
agentIdstringoptionalAgent to bind the conversation to
metadataRecord<string, any>optionalConversation metadata

CreateDataRequest

Properties

PropertyTypeRequiredDescription
objectstringThe object name.
dataRecord<string, any>The dictionary of field values to insert.

CreateDataResponse

Properties

PropertyTypeRequiredDescription
objectstringThe object name.
idstringThe ID of the newly created record.
recordRecord<string, any>The created record, including server-generated fields (created_at, owner).
droppedFields{ object: string; fields: string[]; reason: Enum<'readonly' | 'readonly_when' | 'primary_key'> }[]optionalWrite-observability: caller-supplied fields that were LEGALLY stripped before the record was written — a non-system create cannot seed a static readonly column (ingress strip), so those keys are dropped and the field re-derives its default. Present ONLY when ≥1 field was dropped; the create still succeeded without them (status/success semantics unchanged). REST additionally surfaces this as the X-ObjectStack-Dropped-Fields response header. Optional — omit-when-empty keeps the shape backward-compatible for existing clients.

Nested Shape: CreateDataResponse.droppedFields[number]

A write-path strip event: caller-supplied fields legally dropped from the payload

PropertyTypeRequiredDescription
objectstringObject the write targeted (resolved object name)
fieldsstring[]Caller-supplied field names the engine removed from the write payload
reasonEnum<'readonly' | 'readonly_when' | 'primary_key'>Why the fields were dropped: static readonly, a TRUE readonlyWhen predicate, or the primary-key strip of a payload id the engine ruled is not an identifier

CreateManyDataRequest

Properties

PropertyTypeRequiredDescription
objectstringObject name
recordsRecord<string, any>[]Array of records to create

CreateManyDataResponse

Properties

PropertyTypeRequiredDescription
objectstringObject name
recordsRecord<string, any>[]Created records
countnumberNumber of records created
droppedFields{ object: string; fields: string[]; reason: Enum<'readonly' | 'readonly_when' | 'primary_key'> }[]optionalWrite-observability: caller-supplied readonly fields the create-ingress strip removed before the rows were written. AGGREGATED across the batch (one event per object/reason with the union of dropped field names) rather than per-row, because the insert-time strip is static-readonly only — schema-uniform, so every row drops the same set. Present ONLY when ≥1 field was dropped; the creates still succeeded without them (count/success unchanged). Optional — omit-when-empty keeps the shape backward-compatible. (The per-row insertMany/batch paths carry per-row droppedFields on each result instead — see BatchOperationResultSchema.)

Nested Shape: CreateManyDataResponse.droppedFields[number]

A write-path strip event: caller-supplied fields legally dropped from the payload

PropertyTypeRequiredDescription
objectstringObject the write targeted (resolved object name)
fieldsstring[]Caller-supplied field names the engine removed from the write payload
reasonEnum<'readonly' | 'readonly_when' | 'primary_key'>Why the fields were dropped: static readonly, a TRUE readonlyWhen predicate, or the primary-key strip of a payload id the engine ruled is not an identifier

DeleteDataRequest

Properties

PropertyTypeRequiredDescription
objectstringObject name
idstringRecord ID to delete
expectedVersionstringoptionalOptimistic concurrency token (typically the updated_at value the client read). When provided, the server compares it against the current record version and returns 409 CONCURRENT_UPDATE if they differ. Optional — omit to skip the check.

DeleteDataResponse

Properties

PropertyTypeRequiredDescription
objectstringObject name
idstringDeleted record ID
successbooleanWhether deletion succeeded

DeleteManyDataRequest

Properties

PropertyTypeRequiredDescription
idsstring[]Array of record IDs to delete (server caps the count — see batch.maxBatchSize)
options{ atomic: boolean; returnRecords: boolean; continueOnError: boolean }optionalDelete options
objectstringObject name

Nested Shape: DeleteManyDataRequest.options

PropertyTypeRequiredDescription
atomicbooleanoptional (default: false)Opt-in all-or-nothing. When explicitly true the whole batch runs inside ONE engine transaction: the first failure rolls back every prior write, and the response reports zero successes — each row carries errors[0].code ROLLED_BACK (written, then undone), the causal row its own error, and rows never reached NOT_ATTEMPTED. A runtime that cannot roll back REFUSES the request (501 NOT_IMPLEMENTED) rather than silently degrading to best-effort — probe capabilities.transactionalBatch on /discovery first. Takes precedence over continueOnError. Default false: sequential best-effort.
returnRecordsbooleanoptional (default: false)If true, return full record data in response
continueOnErrorbooleanoptional (default: false)If true (and atomic=false), continue processing remaining records after errors. Default false: the first failure ENDS the run — records before it stay written (nothing is rolled back on this arm), and every record after it is reported errors[0].code NOT_ATTEMPTED rather than omitted, so results always covers all total records and succeeded + failed === total.
validateOnlyneveroptional[REMOVED] options.validateOnly was removed from BatchOptions in @objectstack/spec 17.0.0. It was never implemented: the batch surfaces persisted regardless, so a "dry-run" would have silently executed. There is no dry-run today — drop the key. If you need to preview a batch without writing, open an issue so it can be designed (no-commit cascade / constraint semantics) and reintroduced as a flag that actually holds.

DeleteManyDataResponse

Properties

PropertyTypeRequiredDescription
successbooleanOperation success status
error{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; declaredCode?: string; message: string; userMessage?: string; … }optionalError details if success is false
meta{ timestamp: string; duration?: number; requestId?: string; traceId?: string }optionalResponse metadata
operationEnum<'create' | 'update' | 'upsert' | 'delete'>optionalOperation type that was performed
totalnumberTotal number of records in the batch
succeedednumberNumber of records that succeeded
failednumberNumber of records that failed
results{ id?: string; success: boolean; errors?: object[]; data?: Record<string, any>; … }[]Detailed results for each record

Nested Shape: DeleteManyDataResponse.error

PropertyTypeRequiredDescription
codeEnum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)
declaredCodestringoptionalThe producer-declared code, verbatim, when it is not a member of the closed code vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112)
messagestringReadable error message
userMessagestringoptionalProducer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces message.
categorystringoptionalError category (e.g. validation, authorization)
httpStatusintegeroptionalHTTP status of the response carrying this error
detailsanyoptionalAdditional error context (e.g. field validation errors)
requestIdstringoptionalRequest ID for tracking

Nested Shape: DeleteManyDataResponse.results[number]

PropertyTypeRequiredDescription
idstringoptionalRecord ID if operation succeeded
successbooleanWhether this record was processed successfully
errors{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; declaredCode?: string; message: string; userMessage?: string; … }[]optionalArray of errors if operation failed. Branch on errors[0].code — an atomic batch that rolled back marks rows that were written then undone with code ROLLED_BACK and rows never reached with NOT_ATTEMPTED, while the causal row keeps its own error. A NON-atomic batch that stopped (the continueOnError: false default) marks its un-attempted tail with the same NOT_ATTEMPTED code — rows before the failure stay written and keep reporting success, since nothing was rolled back.
dataRecord<string, any>optionalFull record data (if returnRecords=true)
indexnumberoptionalIndex of the record in the request array
droppedFields{ object: string; fields: string[]; reason: Enum<'readonly' | 'readonly_when' | 'primary_key'> }[]optionalWrite-observability: caller-supplied fields LEGALLY stripped from THIS row before it was written — static readonly / TRUE readonlyWhen on update, or the create-ingress strip. Per-row because a batch can drop different fields on different rows (readonlyWhen is record-state-dependent). Present ONLY when ≥1 field was dropped for this row; the row still succeeded (success unchanged). A single response header cannot express per-row drops, so this body field is the canonical bulk channel — REST does not emit X-ObjectStack-Dropped-Fields for batches. Optional — omit-when-empty keeps the shape backward-compatible.

DeleteMetaItemRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name
organizationIdstringoptionalOrganization (tenant) scope for the reset. Load-bearing, not advisory: it selects the ADR-0005 overlay partition, so it decides WHICH row the reset destroys — an org-scoped delete removes that tenant's own overlay, while an org-less delete reaches the environment-wide row and would blank the item for every tenant. Absent = environment-wide.
parentVersionstringoptionalADR-0008 optimistic-concurrency pin: the version token the caller believes is current (on the REST door, the If-Match request header). Present, a concurrent edit is reported as a 409 conflict instead of silently reset; absent = last-write-wins against the current row (Studio's "Reset" button is unpinned).
actorstringoptionalIdentity recorded on the delete's history tombstone row. On the REST door this is the request's authenticated identity (one producer) — never a caller-supplied header. Absent, the event is recorded actor-less (null), deliberately not attributed to "system".
stateEnum<'active' | 'draft'>optionalWhich lifecycle row to discard: draft discards the pending draft overlay only (the still-active overlay, if any, keeps serving); active or absent resets the live row. Absent defaults to active.
dropStoragebooleanoptionalDestructive opt-in, default false: also drop the object's physical table after the metadata row is removed (object type + active state only; never sys_ tables). Used by the "discard a previewed object" flow so a publish-to-preview leaves no orphan table.

DeleteMetaItemResponse

Properties

PropertyTypeRequiredDescription
successboolean
resetbooleanoptional
seqintegeroptionalMonotonic sequence number of the metadata event this delete appended to the item history (sys_metadata_history.event_seq) — the ordering token the history/audit trail is read by, and the same key both write-verb siblings declare. Unlike version it is not an OCC token. Optional HERE, unlike on those siblings, because only the repository path's delete-ful branch appends an event: a no-op reset (no overlay row existed) and the control-plane legacy raw-engine path both answer without one. Absence means "this branch appended no history event", never "nothing was deleted" — read reset for that.
projectionApplied{ success: boolean; error?: string }optionalOutcome of the awaited ADR-0094 mutation projector — the post-persist step that materializes this metadata into its derived data-plane read model. The same receipt {@link SaveMetaItemResponseSchema} and {@link PublishMetaItemResponseSchema} carry, because the projector runs on all three verbs of this door: on a delete it re-reads the layered state and either retires the derived record or resets it to the artifact baseline. Present ONLY when a projector is registered for this metadata type AND a row was actually deleted, which is why it is optional: its absence means "no projector ran", never "the projection failed". Best-effort by design — a projector failure is reported here and logged, never thrown, so a caller that needs the read model to be live must check projectionApplied.success rather than rely on the 200.
messagestringoptional

Nested Shape: DeleteMetaItemResponse.projectionApplied

PropertyTypeRequiredDescription
successbooleanFalse when the projector threw; the metadata delete itself still succeeded.
errorstringoptionalProjector failure message, present only when success is false.

DiffMetaItemResponse

Properties

PropertyTypeRequiredDescription
typestringMetadata type of the diffed item.
namestringName of the diffed item.
fromVersionnumber | nullThe older side's history version, null when that side is absent (e.g. the item had no earlier version).
toVersionnumber | nullThe newer side's history version, null when that side is absent.
added{ path: string; value: any }[]Members present in to and absent in from.
removed{ path: string; value: any }[]Members present in from and absent in to.
changed{ path: string; from: any; to: any }[]Members present on both sides with different values.

Nested Shape: DiffMetaItemResponse.added[number]

PropertyTypeRequiredDescription
pathstringDot path of the added member.
valueanyThe added value.

Nested Shape: DiffMetaItemResponse.removed[number]

PropertyTypeRequiredDescription
pathstringDot path of the removed member.
valueanyThe removed value.

Nested Shape: DiffMetaItemResponse.changed[number]

PropertyTypeRequiredDescription
pathstringDot path of the changed member.
fromanyThe older side's value.
toanyThe newer side's value.

DisablePackageRequest

Disable package request

Properties

PropertyTypeRequiredDescription
idstringPackage ID to disable

DisablePackageResponse

Disable package response

Properties

PropertyTypeRequiredDescription
package{ manifest: object; status?: Enum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>; enabled?: boolean; installedAt?: string; … }Disabled package details
messagestringoptionalDisable status message

Nested Shape: DisablePackageResponse.package

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }Full package manifest
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

EnablePackageRequest

Enable package request

Properties

PropertyTypeRequiredDescription
idstringPackage ID to enable

EnablePackageResponse

Enable package response

Properties

PropertyTypeRequiredDescription
package{ manifest: object; status?: Enum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>; enabled?: boolean; installedAt?: string; … }Enabled package details
messagestringoptionalEnable status message

Nested Shape: EnablePackageResponse.package

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }Full package manifest
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

FindDataRequest

Properties

PropertyTypeRequiredDescription
objectstringThe unique machine name of the object to query (e.g. "account").
query{ object: string; fields?: string[]; where?: any; search?: string | object; … }optionalStructured query definition (filter, sort, select, pagination).

Nested Shape: FindDataRequest.query

PropertyTypeRequiredDescription
objectstringObject name (e.g. account)
fieldsstring[]optionalFields to retrieve — names of the queried object's OWN columns. A dotted path (owner.name) is not a projection: no driver resolves one, and the ingress refuses it with 400 INVALID_FIELD. Related data is read with expand, whose nested QueryAST both filters (where) and selects (fields) the related record's columns. The projection must RETAIN the foreign-key column: fields: ['title'] with expand: 'project_id' resolves nothing, because the relation is carried by that key — add 'project_id' and it works. Where the value is wanted on the queried object itself, denormalise it onto that object (a stored field, written when the source changes), the same remedy the sort axis prescribes.
whereanyoptionalFiltering criteria (WHERE)
searchstring | { query: string; fields?: string[]; fuzzy: boolean; operator: Enum<'and' | 'or'>; … }optionalFull-text search — the query text (canonical, ADR-0061 D1), or a structured FullTextSearch configuration
searchFieldsstring[]optionalNarrow the search to these fields (server-intersected with the allowed searchable set — can only narrow, never widen; ADR-0061 D1)
orderBy{ field: string; order: Enum<'asc' | 'desc'> }[]optionalSorting instructions (ORDER BY)
limitnumberoptionalMax records to return (LIMIT)
offsetnumberoptionalRecords to skip (OFFSET)
topnumberoptionalAlias for limit (OData compatibility)
cursorneveroptional[REMOVED] query.cursor was removed in @objectstack/spec 17 (ADR-0049) — no driver ever implemented keyset pagination, so the cursor was accepted and ignored and every page came back identical (a caller looping "until hasMore is false" never terminates). Delete the key; QueryBuilder.cursor() was removed with it. Express the keyset as an ordinary where predicate on your sort key — where: { created_at: { $gt: last.created_at } } with the matching orderBy — which every driver executes with canonicalised comparands. A first-class cursor, if ever built, will be a response-minted opaque token, not this caller-built record.
joinsneveroptional[REMOVED] query.joins was removed in @objectstack/spec 17 (ADR-0049) — no engine or driver ever read it: a query carrying joins behaved exactly as if the key were absent, while its name squatted on the reserved REST parameter set. Delete the key. Related records are read through expandexpand: { owner_id: { object: 'user', fields: ['name'] } } — which the engine resolves via batch $in queries, and whose nested query selects the related record's own columns. Keep the foreign key in your own projection (fields: ['title', 'owner_id']): the relation is carried by that column, so projecting it away leaves expansion nothing to resolve. A dotted fields path is NOT a replacement — no driver ever resolved one and the ingress refuses it (400 INVALID_FIELD).
aggregations{ function: Enum<'count' | 'sum' | 'avg' | 'min' | 'max' | 'count_distinct'>; field?: string; alias: string; filter?: any }[]optionalAggregation functions
groupBy(string | { field: string; dateGranularity?: Enum<'day' | 'week' | 'month' | 'quarter' | 'year'>; alias?: string })[]optionalGROUP BY targets (strings or {field, dateGranularity?} objects for date bucketing)
havinganyoptionalHAVING — filter over the AGGREGATED rows (aggregation aliases + groupBy projections); applied engine-side after aggregation
windowFunctionsneveroptional[REMOVED] query.windowFunctions was removed in @objectstack/spec 17 (ADR-0049) — find() never applied it: no engine or driver read the key on the query path, so every OVER clause it declared was silently dropped. Delete the key. Window functions are a SQL-driver capability behind SqlDriver.findWithWindowFunctions(object, query) (embedder-level; not on the IDataDriver contract or the REST surface); request-level analytics are aggregations + groupBy.
distinctneveroptional[REMOVED] query.distinct was removed in @objectstack/spec 17 (ADR-0049 / ADR-0078) — no driver ever rendered SELECT DISTINCT; the flag's only observable effect was MIS-WIRED: the REST list path treated a distinct query as not countable and silently degraded total/hasMore to a page-local estimate while still returning duplicate rows. Delete the key; QueryBuilder.distinct() was removed with it, and the count suppression is gone (total is truthful again). For unique values of one column use the SQL/memory drivers' distinct(object, field) door; for unique combinations, groupBy; for a deduplicated count, the count_distinct aggregation.
expandRecord<string, { object: string; fields?: string[]; where?: any; search?: string | object; … }>optionalRecursive relation loading map. Keys are lookup/master_detail field names; values are nested QueryAST objects that control select (fields) and filter (where, AND-merged with the batch $in), plus further expansion on the related object. The engine resolves expand via batch $in queries (driver-agnostic) with a default max depth of 3; per-parent limit/offset/orderBy are NOT applied on this path.

FindDataResponse

Properties

PropertyTypeRequiredDescription
objectstringThe object name for the returned records.
recordsRecord<string, any>[]The list of matching records.
totalnumberoptionalTotal number of records matching the filter (if requested).
nextCursorstringoptionalCursor for the next page of results (cursor-based pagination).
hasMorebooleanoptionalTrue if there are more records available (pagination).

FindReferencesToMetaResponse

Properties

PropertyTypeRequiredDescription
references{ type: string; name: string; label?: string; path: string; … }[]Every found reference to the addressed item.

Nested Shape: FindReferencesToMetaResponse.references[number]

PropertyTypeRequiredDescription
typestringMetadata type of the REFERRING item.
namestringName of the referring item.
labelstringoptionalDisplay label of the referring item, when it has one.
pathstringWhere inside the referring item the reference sits (dot path).
kindstringWhat kind of reference this is (e.g. which key carries it).

GetDataRequest

Properties

PropertyTypeRequiredDescription
objectstringThe object name.
idstringThe unique record identifier (primary key).
selectstring[]optionalFields to include in the response (allowlisted query param).
expandstring[]optionalLookup/master_detail field names to expand. The engine resolves these via batch $in queries, replacing foreign key IDs with full objects.

GetDataResponse

Properties

PropertyTypeRequiredDescription
objectstringThe object name.
idstringThe record ID.
recordRecord<string, any>The complete record data.

GetDiscoveryRequest

Properties

PropertyTypeRequiredDescription

GetDiscoveryResponse

Properties

PropertyTypeRequiredDescription
namestringoptional
versionstring
environmentEnum<'production' | 'sandbox' | 'development'>optionalDeployment 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; … }optional
locale{ default: string; supported: string[]; timezone: string }optional
servicesRecord<string, { enabled: boolean; status: Enum<'available' | 'registered' | 'unavailable' | 'degraded' | 'stub'>; handlerReady?: boolean; route?: string; … }>optionalPer-service availability map keyed by CoreServiceName
capabilities{ comments: object; automation: object; cron: object; search: object; … }optionalHierarchical 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
metadataRecord<string, any>optionalCustom metadata key-value pairs for extensibility
apiNamestringoptionalAPI name (deprecated — use name; removed in protocol 18)

Nested Shape: GetDiscoveryResponse.routes

PropertyTypeRequiredDescription
datastringe.g. /api/v1/data
metadatastringe.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
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: GetDiscoveryResponse.services[string]

PropertyTypeRequiredDescription
enabledboolean
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: GetDiscoveryResponse.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. False while realtime is an in-process bus with no mounted HTTP/WS surface (ADR-0076 D12).
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: GetDiscoveryResponse.schemaDiscovery

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

Nested Shape: GetDiscoveryResponse.scoping

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

GetEffectivePermissionsRequest

Properties

PropertyTypeRequiredDescription

GetEffectivePermissionsResponse

Properties

PropertyTypeRequiredDescription
objectsRecord<string, { allowCreate: boolean; allowRead: boolean; allowEdit: boolean; allowDelete: boolean; … }>Effective object permissions keyed by object name
systemPermissionsstring[]Effective system-level permissions

Nested Shape: GetEffectivePermissionsResponse.objects[string]

PropertyTypeRequiredDescription
allowCreatebooleanoptional (default: false)Create permission
allowReadbooleanoptional (default: false)Read permission
allowEditbooleanoptional (default: false)Edit permission
allowDeletebooleanoptional (default: false)Delete permission
allowExportbooleanoptionalUser-level export axis over read (opt-in grant). true = export granted (still bounded by read); unset/false = no export. Merged most-permissively like the CRUD bits; NOT implied by viewAllRecords/modifyAllRecords.
allowTransferbooleanoptional (default: false)[RBAC-gated; ENFORCED via the insert/update owner_id guard] Change record ownership (assign/reassign/disown owner_id)
allowRestoreneveroptional[REMOVED] objects.<object>.allowRestore was removed in @objectstack/spec 17 (ADR-0049) — the restore ObjectQL operation it claimed to gate has never shipped (roadmap M2), so granting the bit delivered nothing. Delete the key — a dispatched restore stays denied fail-closed by the permission evaluator's destructive-operation backstop, and the bit returns with the M2 lifecycle initiative alongside the operation it gates. Run os migrate meta --from 17 to list the mechanical edits for existing sources; apply them by hand.
allowPurgeneveroptional[REMOVED] objects.<object>.allowPurge was removed in @objectstack/spec 17 (ADR-0049) — the purge ObjectQL operation it claimed to gate has never shipped (roadmap M2), so granting the bit delivered nothing (a compliance/GDPR erase the author believed was permission-locked was not — the operation itself does not exist). Delete the key — a dispatched purge stays denied fail-closed by the permission evaluator's destructive-operation backstop, and the bit returns with the M2 lifecycle initiative alongside the operation it gates. Run os migrate meta --from 17 to list the mechanical edits for existing sources; apply them by hand.
viewAllRecordsbooleanoptional (default: false)View All Data (Bypass Sharing)
modifyAllRecordsbooleanoptional (default: false)Modify All Data (Bypass Sharing) — bypasses sharing rules and ownership on the objects record sharing enforces on; on an object with NO owner field sharing abstains, so the platform created_by write floor still applies.
readScopeEnum<'own' | 'own_and_reports' | 'unit' | 'unit_and_below' | 'org'>optional[ADR-0057 D1] Read depth: own|unit|unit_and_below|org
writeScopeEnum<'own' | 'own_and_reports' | 'unit' | 'unit_and_below' | 'org'>optional[ADR-0057 D1] Write depth: own|unit|unit_and_below|org
apiOperationsEnum<'get' | 'list' | 'create' | 'update' | 'delete' | 'upsert' | 'bulk' | …>[]optionalServer-resolved effective API operations for this object. Present only when the object tightens exposure via apiMethods; absent = default-allow. The frontend renders this effective set, never the raw whitelist. Vocabulary is the EFFECTIVE ApiOperation set (six primitives + eight derived verbs), not the authored six-value ApiMethod enum.

GetFieldLabelsRequest

Properties

PropertyTypeRequiredDescription
objectstringObject name
localestringBCP-47 locale code

GetFieldLabelsResponse

Properties

PropertyTypeRequiredDescription
objectstringObject name
localestringLocale code
labelsRecord<string, { label: string; help?: string; options?: Record<string, string> }>Field labels keyed by field name

Nested Shape: GetFieldLabelsResponse.labels[string]

PropertyTypeRequiredDescription
labelstringTranslated field label
helpstringoptionalTranslated help text
optionsRecord<string, string>optionalTranslated option labels

GetLocalesRequest

Properties

PropertyTypeRequiredDescription

GetLocalesResponse

Properties

PropertyTypeRequiredDescription
locales{ code: string; label: string; isDefault: boolean }[]Available locales

Nested Shape: GetLocalesResponse.locales[number]

PropertyTypeRequiredDescription
codestringBCP-47 locale code (e.g., en-US, zh-CN)
labelstringLocale label. Equals code on every serving surface today — the client names locales for its UI
isDefaultbooleanoptional (default: false)Whether this is the default locale

GetMetaDiagnosticsResponse

Properties

PropertyTypeRequiredDescription
entries{ type: string; name: string; diagnostics: object }[]One entry per item that failed validation (after filters).
totalnumberNumber of entries in this answer.
scannedTypesnumberHow many metadata types the sweep visited.
scannedItemsnumberHow many items the sweep visited.
statsRecord<string, { count: number; locked: number; packages: string[] }>Per-type aggregate stats, keyed by metadata type — computed in the same sweep so a directory page renders tile counts and a package filter in one round-trip.

Nested Shape: GetMetaDiagnosticsResponse.entries[number]

PropertyTypeRequiredDescription
typestringMetadata type of the failing item.
namestringName of the failing item.
diagnostics{ valid: boolean; errors?: object[]; warnings?: object[] }The spec-validation verdict for the item — the same MetadataValidationResult the write path answers.

Nested Shape: GetMetaDiagnosticsResponse.stats[string]

PropertyTypeRequiredDescription
countnumberItems of this type present.
lockednumberItems of this type currently lock-protected.
packagesstring[]Packages contributing items of this type.

GetMetaItemCachedRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name
cacheRequest{ ifNoneMatch?: string; ifModifiedSince?: string; cacheControl?: object }optionalCache validation parameters
localestringoptionalResolved response locale. Folded into the ETag so a language switch never returns a stale-locale 304 — metadata is translated after the cache validator check (issue).
organizationIdstringoptionalOrganization (tenant) scope for the read. Selects the org partition in the ADR-0005 overlay read order — org overlay wins over env-wide overlay wins over packaged artifact — exactly as on the uncached read. Also folded into the ETag, so a scope switch never returns a stale 304 from another scope's cached representation. Absent = environment-wide read.

Nested Shape: GetMetaItemCachedRequest.cacheRequest

PropertyTypeRequiredDescription
ifNoneMatchstringoptionalETag value for conditional request (If-None-Match header)
ifModifiedSincestringoptionalTimestamp for conditional request (If-Modified-Since header)
cacheControl{ directives: Enum<'public' | 'private' | 'no-cache' | 'no-store' | 'must-revalidate' | 'max-age'>[]; maxAge?: number; staleWhileRevalidate?: number; staleIfError?: number }optionalClient cache control preferences

GetMetaItemCachedResponse

Properties

PropertyTypeRequiredDescription
dataanyoptionalMetadata payload (omitted for 304 Not Modified)
etag{ value: string; weak: boolean }optionalETag for this resource version
lastModifiedstringoptionalLast modification timestamp
cacheControl{ directives: Enum<'public' | 'private' | 'no-cache' | 'no-store' | 'must-revalidate' | 'max-age'>[]; maxAge?: number; staleWhileRevalidate?: number; staleIfError?: number }optionalCache control directives
notModifiedbooleanoptional (default: false)True if resource has not been modified (304 response)
versionstringoptionalMetadata version identifier

Nested Shape: GetMetaItemCachedResponse.etag

PropertyTypeRequiredDescription
valuestringETag value (hash or version identifier)
weakbooleanoptional (default: false)Whether this is a weak ETag

Nested Shape: GetMetaItemCachedResponse.cacheControl

PropertyTypeRequiredDescription
directivesEnum<'public' | 'private' | 'no-cache' | 'no-store' | 'must-revalidate' | 'max-age'>[]Cache control directives
maxAgenumberoptionalMaximum cache age in seconds
staleWhileRevalidatenumberoptionalAllow serving stale content while revalidating (seconds)
staleIfErrornumberoptionalAllow serving stale content on error (seconds)

GetMetaItemLayeredRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name
packageIdstringoptionalOptional package ID — scopes the code layer so a same-name collision resolves to the requested package's artifact (ADR-0048).
organizationIdstringoptionalOrganization (tenant) scope for the read. Selects the org partition in the ADR-0005 overlay read order, so it decides which tenant's customization row is reported as the overlay layer (and merged into effective). Absent = environment-wide read: overlay reports the env-level row only.

GetMetaItemLayeredResponse

Properties

PropertyTypeRequiredDescription
typestringMetadata type name (canonical singular)
namestringItem name
codeanyLAYER 1 — the packaged artifact baseline exactly as shipped, before any tenant customization. null when no artifact ships this item (it exists only as an overlay).
overlayanyLAYER 2 — the stored customization row ALONE, not merged with code. null when this tenant has not customized the item.
overlayScopeEnum<'org' | 'env'> | nullWhich scope the overlay row was read from — org for a tenant overlay, env for an environment-level one. null exactly when overlay is null.
effectiveanyLAYER 3 — the merged result, i.e. the value an ordinary GET /meta/:type/:name would return under item. null when the item resolves to nothing at all.
_diagnostics{ valid: boolean; errors?: object[]; warnings?: object[] }optionalLoad-time spec-validation verdict for effective, so the Studio edit page can raise invalid-metadata banners and inline field errors without a second round trip. ABSENT for metadata types that register no Zod schema (function / service / router) — absence means "no opinion", never "valid".
lockEnum<'none' | 'no-overlay' | 'no-delete' | 'full'>Resolved lock verdict (ADR-0010 §3.3), artifact winning over overlay. Always present on this path.
lockReasonstringoptionalHuman-readable explanation shown next to a refused write. Present only when the resolved item declares _lockReason.
lockSourceEnum<'artifact' | 'package' | 'env-forced'>optionalWhich layer asserted the lock. Present only when the resolved item declares _lockSource.
lockDocsUrlstringoptionalDocumentation link surfaced beside lockReason. Present only when the resolved item declares _lockDocsUrl.
provenanceEnum<'package' | 'org' | 'env-forced'>optionalWhere the item came from (package | org | env-forced). Present only when the resolved item declares _provenance.
packageIdstringoptionalOwning package machine id. Present only when the resolved item declares _packageId.
packageVersionstringoptionalOwning package version. Present only when the resolved item declares _packageVersion.
editablebooleanWhether an overlay write is permitted. Always present on this path.
deletablebooleanWhether deleting the overlay is permitted. Always present on this path.
resettablebooleanWhether the item can be reset to its packaged default. Always present on this path.

Nested Shape: GetMetaItemLayeredResponse._diagnostics

PropertyTypeRequiredDescription
validbooleanWhether the metadata is valid
errors{ path: string; message: string; code?: string }[]optionalValidation errors
warnings{ path: string; message: string }[]optionalValidation warnings

GetMetaItemRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name (snake_case identifier)
packageIdstringoptionalOptional package ID to filter items by
organizationIdstringoptionalOrganization (tenant) scope for the read. Selects the org partition in the ADR-0005 overlay read order — org overlay wins over env-wide overlay wins over packaged artifact — so it decides which tenant's customization row is served as the item. Absent = environment-wide read: only env-level overlays apply and no org partition is consulted.
stateEnum<'active' | 'draft'>optionalDraft-visibility switch — which lifecycle row to read (strict mode): 'draft' opens the pending draft buffer (Studio's editor read) and fails when no draft exists; absent or 'active' reads the live published row. Distinct from previewDrafts, which FALLS BACK to the active row when no draft exists. Declaration ≠ authorization: this member only selects which stored row is read — ADR-0106 masking is unaffected, and draft access is gated upstream, not by this schema.
previewDraftsbooleanoptionalDraft-visibility switch (ADR-0033 draft-overlay preview, non-strict): when true and state is not 'draft', a pending draft row is preferred if one exists, else the read falls back to the active row — the render path degrades to the published value instead of erroring. A served draft is tagged _draft: true so UIs can badge it. Declaration ≠ authorization: this member only switches which row is read, and ADR-0106 masking is unaffected — draft preview is admin-gated upstream, not by this schema.

GetMetaItemResponse

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name
itemanyMetadata item definition
sortability{ fields: Record<string, object> }optionalPer-column sortability projection — present exactly when type is object, on every serving branch. Computed at serve time from the served document via the spec's own storage predicates; consumers render sort affordances from this signal and never re-derive it from field type. See ObjectSortabilitySchema for the closed category set.
lockEnum<'none' | 'no-overlay' | 'no-delete' | 'full'>optionalResolved lock verdict for this item (ADR-0010 §3.3). none means unlocked; no-overlay / no-delete / full refuse the corresponding write with 403 ITEM_LOCKED. Resolved from the document's _lock, with the packaged artifact winning over any org overlay.
lockReasonstringoptionalHuman-readable explanation shown next to a refused write. Present only when the resolved item declares _lockReason.
lockSourceEnum<'artifact' | 'package' | 'env-forced'>optionalWhich layer asserted the lock. Present only when the resolved item declares _lockSource.
lockDocsUrlstringoptionalDocumentation link surfaced beside lockReason. Present only when the resolved item declares _lockDocsUrl.
provenanceEnum<'package' | 'org' | 'env-forced'>optionalWhere the item came from (package | org | env-forced). Present only when the resolved item declares _provenance.
packageIdstringoptionalOwning package machine id. Present only when the resolved item declares _packageId.
packageVersionstringoptionalOwning package version. Present only when the resolved item declares _packageVersion.
editablebooleanoptionalWhether an overlay write is permitted — false iff lock is no-overlay or full. A derived verdict: do not recompute it from lock client-side.
deletablebooleanoptionalWhether deleting the overlay is permitted — false iff lock is no-delete or full.
resettablebooleanoptionalWhether the item can be reset to its packaged default — true iff it is artifact-backed, i.e. there is a baseline to reset TO.

Nested Shape: GetMetaItemResponse.sortability

PropertyTypeRequiredDescription
fieldsRecord<string, { sortable: boolean; reason?: 'virtual-type'; caveat?: 'unprovisioned-anchor' }>Verdict per sortable-addressable column, keyed by field name. The domain is the served field map plus the always-provisioned id; a name absent from this map (an unknown field, a dotted path, an unprovisioned audit column) has no platform sort behind it and must get no sort affordance.

GetMetaItemsRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name (e.g., "object", "plugin")
packageIdstringoptionalOptional package ID to filter items by
organizationIdstringoptionalOrganization (tenant) scope for the read. Selects the org partition in the ADR-0005 overlay read order — org overlay wins over env-wide overlay wins over packaged artifact — so it decides which tenant's customization rows are merged into the list. Absent = environment-wide read: only env-level overlays apply and no org partition is consulted.
previewDraftsbooleanoptionalDraft-visibility switch (ADR-0033 draft-overlay preview): when true, pending state='draft' rows are overlaid on the active list — draft wins on name collision, draft-only items appear, and each overlaid item is tagged _draft: true so UIs can badge the preview. Absent/false = published world only. Declaration ≠ authorization: this member only switches which rows are read, and ADR-0106 masking is unaffected — callers without draft-preview authorization are refused upstream (admin-gated), not by this schema.

GetMetaItemsResponse

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
itemsany[]Array of metadata items

GetMetaTypesRequest

Properties

PropertyTypeRequiredDescription

GetMetaTypesResponse

Properties

PropertyTypeRequiredDescription
typesstring[]Available metadata type names (e.g., "object", "plugin", "view")
entries{ type: string; label: string; description?: string; filePatterns: string[]; … }[]optionalEnriched per-type registry entries (Phase 3a)

Nested Shape: GetMetaTypesResponse.entries[number]

PropertyTypeRequiredDescription
typestringSingular type identifier
labelstringHuman-readable label
descriptionstringoptionalBrief description
filePatternsstring[]Glob patterns used to discover artifacts of this type
supportsOverlaybooleanLoader can merge per-org overlays on top of artifact
allowOrgOverridebooleanPer-org overlay writes accepted at runtime (may be env-elevated)
allowRuntimeCreatebooleanNew artifacts of this type can be created via runtime API
supportsVersioningbooleanHistory is tracked for this type
executionPinnedbooleanRuntime transactions pin a specific historical version_hash (ADR-0009)
loadOrderintegerLoading priority (lower = earlier)
domainEnum<'data' | 'ui' | 'automation' | 'system' | 'security' | 'ai'>Protocol domain
overrideSourceEnum<'registry' | 'env'>Whether allowOrgOverride is set in the static registry or via OS_METADATA_WRITABLE env var
createSeedanyoptionalAuthoritative minimal valid create seed for this type — Studio/CLI/API derive create defaults from it (single source of truth in @objectstack/spec). Absent for canvas-create types whose shape is built interactively.

GetNotificationPreferencesRequest

Properties

PropertyTypeRequiredDescription

GetNotificationPreferencesResponse

Properties

PropertyTypeRequiredDescription
preferences{ email: boolean; push: boolean; inApp: boolean; digest: Enum<'none' | 'daily' | 'weekly'>; … }Current notification preferences

Nested Shape: GetNotificationPreferencesResponse.preferences

PropertyTypeRequiredDescription
emailbooleanoptional (default: true)Receive email notifications
pushbooleanoptional (default: true)Receive push notifications
inAppbooleanoptional (default: true)Receive in-app notifications
digestEnum<'none' | 'daily' | 'weekly'>optional (default: "none")Email digest frequency
channelsRecord<string, { enabled: boolean; email?: boolean; push?: boolean }>optionalPer-channel notification preferences

GetObjectPermissionsRequest

Properties

PropertyTypeRequiredDescription
objectstringObject name to get permissions for

GetObjectPermissionsResponse

Properties

PropertyTypeRequiredDescription
objectstringObject name
permissions{ allowCreate: boolean; allowRead: boolean; allowEdit: boolean; allowDelete: boolean; … }Object-level permissions
fieldPermissionsRecord<string, { readable: boolean; editable: boolean }>optionalField-level permissions keyed by field name

Nested Shape: GetObjectPermissionsResponse.permissions

PropertyTypeRequiredDescription
allowCreatebooleanoptional (default: false)Create permission
allowReadbooleanoptional (default: false)Read permission
allowEditbooleanoptional (default: false)Edit permission
allowDeletebooleanoptional (default: false)Delete permission
allowExportbooleanoptionalUser-level export axis over read (opt-in grant). true = export granted (still bounded by read); unset/false = no export. Merged most-permissively like the CRUD bits; NOT implied by viewAllRecords/modifyAllRecords.
allowTransferbooleanoptional (default: false)[RBAC-gated; ENFORCED via the insert/update owner_id guard] Change record ownership (assign/reassign/disown owner_id)
allowRestoreneveroptional[REMOVED] objects.<object>.allowRestore was removed in @objectstack/spec 17 (ADR-0049) — the restore ObjectQL operation it claimed to gate has never shipped (roadmap M2), so granting the bit delivered nothing. Delete the key — a dispatched restore stays denied fail-closed by the permission evaluator's destructive-operation backstop, and the bit returns with the M2 lifecycle initiative alongside the operation it gates. Run os migrate meta --from 17 to list the mechanical edits for existing sources; apply them by hand.
allowPurgeneveroptional[REMOVED] objects.<object>.allowPurge was removed in @objectstack/spec 17 (ADR-0049) — the purge ObjectQL operation it claimed to gate has never shipped (roadmap M2), so granting the bit delivered nothing (a compliance/GDPR erase the author believed was permission-locked was not — the operation itself does not exist). Delete the key — a dispatched purge stays denied fail-closed by the permission evaluator's destructive-operation backstop, and the bit returns with the M2 lifecycle initiative alongside the operation it gates. Run os migrate meta --from 17 to list the mechanical edits for existing sources; apply them by hand.
viewAllRecordsbooleanoptional (default: false)View All Data (Bypass Sharing)
modifyAllRecordsbooleanoptional (default: false)Modify All Data (Bypass Sharing) — bypasses sharing rules and ownership on the objects record sharing enforces on; on an object with NO owner field sharing abstains, so the platform created_by write floor still applies.
readScopeEnum<'own' | 'own_and_reports' | 'unit' | 'unit_and_below' | 'org'>optional[ADR-0057 D1] Read depth: own|unit|unit_and_below|org
writeScopeEnum<'own' | 'own_and_reports' | 'unit' | 'unit_and_below' | 'org'>optional[ADR-0057 D1] Write depth: own|unit|unit_and_below|org

Nested Shape: GetObjectPermissionsResponse.fieldPermissions[string]

PropertyTypeRequiredDescription
readablebooleanoptional (default: true)Field read access
editablebooleanoptional (default: false)Field edit access

GetPackageRequest

Get package request

Properties

PropertyTypeRequiredDescription
idstringPackage identifier

GetPackageResponse

Get package response

Properties

PropertyTypeRequiredDescription
package{ manifest: object; status?: Enum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>; enabled?: boolean; installedAt?: string; … }Package details

Nested Shape: GetPackageResponse.package

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }Full package manifest
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

GetPresenceRequest

Properties

PropertyTypeRequiredDescription
channelstringChannel to get presence for

GetPresenceResponse

Properties

PropertyTypeRequiredDescription
channelstringChannel name
members{ userId: string; status: Enum<'online' | 'away' | 'busy' | 'offline'>; lastSeen: string; metadata?: Record<string, any> }[]Active members and their presence state

Nested Shape: GetPresenceResponse.members[number]

PropertyTypeRequiredDescription
userIdstringUser identifier
statusEnum<'online' | 'away' | 'busy' | 'offline'>Current presence status
lastSeenstringISO 8601 datetime of last activity
metadataRecord<string, any>optionalCustom presence data (e.g., current page, custom status)

GetPublishedMetaItemResponse

The published metadata item body, opaque by ruling (1C). Shape is the item's own metadata-type schema, resolved at read time — never frozen into this contract.


GetTranslationsRequest

Properties

PropertyTypeRequiredDescription
localestringBCP-47 locale code

GetTranslationsResponse

Properties

PropertyTypeRequiredDescription
localestringLocale code
translations{ objects?: Record<string, object>; apps?: Record<string, object>; messages?: Record<string, string>; globalActions?: Record<string, object>; … }Translation data

Nested Shape: GetTranslationsResponse.translations

PropertyTypeRequiredDescription
objectsRecord<string, { label?: string; pluralLabel?: string; description?: string; fields?: Record<string, object>; … }>optionalObject translations keyed by object name
appsRecord<string, { label: string; description?: string; navigation?: Record<string, object> }>optionalApp translations keyed by app name
messagesRecord<string, string>optionalUI message translations keyed by message ID
globalActionsRecord<string, { label?: string; description?: string; confirmText?: string; successMessage?: string; … }>optionalGlobal action translations keyed by action name
dashboardsRecord<string, { label?: string; description?: string; actions?: Record<string, object>; widgets?: Record<string, object> }>optionalDashboard translations keyed by dashboard name
pagesRecord<string, { label?: string; description?: string; title?: string; subtitle?: string; … }>optionalPage translations keyed by page name
flowsRecord<string, { label?: string; screens?: Record<string, object> }>optionalScreen-flow translations keyed by flow name
settingsRecord<string, { title?: string; description?: string; groups?: Record<string, object>; keys?: Record<string, object>; … }>optionalSettings manifest translations keyed by namespace
metadataFormsRecord<string, { label?: string; description?: string; sections?: Record<string, object>; fields?: Record<string, object> }>optionalTranslations for metadata-type configuration forms keyed by metadata type
settingsCommon{ sourceLabels?: object }optionalCross-namespace Settings UI strings

GetUiViewRequest

Properties

PropertyTypeRequiredDescription
objectstringObject name (snake_case)
typeEnum<'list' | 'form'>View type

GetUiViewResponse

Properties

PropertyTypeRequiredDescription
namestringoptionalItem name — supplied by the metadata door; for an object-scoped container it is the object name.
labelstring | Record<string, string>optionalHuman-readable label shown in metadata lists.
objectstringoptionalObject this container binds to — how a stack-level views: [...] entry says which object its views belong to; read by getViewsByObject() / GET /meta/view?object=.
list{ name?: string; label?: string | Record<string, string>; type?: Enum<'grid' | 'kanban' | 'gallery' | 'calendar' | 'timeline' | 'gantt' | 'map' | …>; data?: object | … +3 more; … }optional
form{ type?: Enum<'simple' | 'tabbed' | 'wizard' | 'split' | 'drawer' | 'modal'>; layout?: Enum<'vertical' | 'horizontal' | 'inline' | 'grid'>; columns?: integer; title?: string; … }optional
listViewsRecord<string, { name?: string; label?: string | Record<string, string>; type?: Enum<'grid' | 'kanban' | 'gallery' | 'calendar' | 'timeline' | 'gantt' | 'map' | …>; data?: object | … +3 more; … }>optionalAdditional named list views (views mode — dropdown userFilters allowed, no tabs; ADR-0047)
formViewsRecord<string, { type?: Enum<'simple' | 'tabbed' | 'wizard' | 'split' | 'drawer' | 'modal'>; layout?: Enum<'vertical' | 'horizontal' | 'inline' | 'grid'>; columns?: integer; title?: string; … }>optionalAdditional named form views
protection{ lock: Enum<'none' | 'no-overlay' | 'no-delete' | 'full'>; reason: string; docsUrl?: string }optionalPackage author protection block — lock policy for this view.
_lockEnum<'none' | 'no-overlay' | 'no-delete' | 'full'>optionalItem-level lock — controls overlay & delete (ADR-0010).
_lockReasonstringoptionalHuman-readable reason shown when a write is refused by _lock.
_lockSourceEnum<'artifact' | 'package' | 'env-forced'>optionalLayer that set _lock (artifact | package | env-forced).
_provenanceEnum<'package' | 'org' | 'env-forced'>optionalOrigin of the item (package | org | env-forced).
_packageIdstringoptionalOwning package machine id.
_packageVersionstringoptionalOwning package version.
_lockDocsUrlstringoptionalOptional documentation link surfaced next to _lockReason.

Nested Shape: GetUiViewResponse.list

PropertyTypeRequiredDescription
namestringoptionalInternal view name (lowercase snake_case)
labelstring | Record<string, string>optionalDisplay label — the default-language string, or an inline locale map ({ en, "zh-CN" }) resolved at render time
typeEnum<'grid' | 'kanban' | 'gallery' | 'calendar' | 'timeline' | 'gantt' | 'map' | …>optional (default: "grid")
data{ provider: 'object'; object: string } | { provider: 'api'; read?: object; write?: object } | { provider: 'value'; items: any[] } | { provider: 'schema'; schemaId: string; schema?: Record<string, any> }optionalData source configuration (defaults to "object" provider)
columnsstring[] | { field: string; label?: string | Record<string, string>; width?: number; align?: Enum<'left' | 'center' | 'right'>; … }[]Fields to display as columns
filter{ field: string; operator?: Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'icontains' | …>; value?: string | number | boolean | null | (string | number)[] }[]optionalFilter criteria (JSON Rules)
sortstring | { field: string; order: Enum<'asc' | 'desc'> }[]optional
searchableFieldsstring[]optionalFields enabled for search
filterableFieldsstring[]optionalLegacy shorthand for userFilters.fields — bare field names enabled for end-user filtering. Prefer userFilters
resizablebooleanoptionalEnable column resizing
compactToolbarbooleanoptionalCollapse Group/Color/Density/Hide-fields into a single View settings popover
selection{ type?: Enum<'none' | 'single' | 'multiple'> }optionalRow selection configuration
navigation{ mode?: Enum<'page' | 'drawer' | 'modal' | 'split' | 'popover' | 'new_window' | 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }optionalConfiguration for item click navigation (page, drawer, modal, etc.)
pagination{ pageSize?: integer; pageSizeOptions?: integer[] }optionalPagination configuration
kanban{ groupByField: string; summarizeField?: string; columns: string[] }optionalKanban-board configuration — applies when the view renders as a kanban layout
calendar{ startDateField: string; endDateField?: string; titleField: string; colorField?: string }optionalCalendar configuration — applies when the view renders as a calendar layout
gantt{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … } & Record<string, any>optionalGantt-timeline configuration — applies when the view renders as a gantt layout
gallery{ coverField?: string; coverFit?: Enum<'cover' | 'contain'>; cardSize?: Enum<'small' | 'medium' | 'large'>; titleField?: string; … }optionalGallery/card view configuration
timeline{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }optionalTimeline view configuration
chart{ chartType?: Enum<'bar' | 'line' | 'pie' | 'area' | 'scatter'>; dataset: string; dimensions?: string[]; values: string[] }optionalList chart view configuration
map{ latitudeField?: string; longitudeField?: string; locationField?: string; titleField?: string; … }optionalMap configuration — applies when the view renders as a map layout
tree{ parentField?: string; labelField?: string; fields?: string[]; defaultExpandedDepth?: integer } & Record<string, any>optionalTree/hierarchy configuration — applies when the view renders as a tree layout
pageNamestringoptionalPublished page this view mounts — required when type: 'page', and refused on every other view type. Rendering is delegated to the existing page renderer; the page keeps its own assignedProfiles audience.
descriptionstring | Record<string, string>optionalView description for documentation/tooltips
sharing{ type?: Enum<'personal' | 'collaborative'>; lockedBy?: string }optionalView sharing and access configuration
rowHeightEnum<'compact' | 'short' | 'medium' | 'tall' | 'extra_tall'>optionalRow height / density setting
grouping{ fields: object[] }optionalGroup records by one or more fields
rowColor{ field: string; colors?: Record<string, string> }optionalColor rows based on field value
hiddenFieldsstring[]optionalFields to hide in this specific view
fieldOrderstring[]optionalExplicit field display order for this view
rowActionsstring[]optionalActions available for individual row items
bulkActionsstring[]optionalActions available when multiple rows are selected
bulkActionDefs{ name: string; label?: string; icon?: string; variant?: Enum<'primary' | 'secondary' | 'danger' | 'ghost' | 'outline'>; … }[]optionalRich bulk action definitions (schema-driven, executed via BulkActionDialog). Use a def for a mass data-plane mutation ('update' with a patch / 'delete') that no action expresses, or for an operation: 'custom' + execution: 'aggregate' entry that dispatches the action it NAMES once for the whole selection — the renderer injects params._selectedIds: string[] (read that on the server, not recordId) so a single call can produce one aggregate artifact (zip of QR codes, merged PDF, batch print). Aggregate results are all-or-nothing: a handler that cannot cover the whole selection must reject, and per-row retry is replaced by re-running the action. batchSize does not apply (the call is never chunked); set maxRecords on defs whose server work is expensive. For the PER-RECORD dispatch use bulkActions: ['<name>'] instead — the bare-string form, promoted with the action's own label, params and visible; a 'custom' def without execution: 'aggregate' has no dispatcher and is refused at parse time. Toolbar url/api actions can also interpolate the current selection via ${ctx.selection.ids} / ${ctx.selection.count}.
conditionalFormatting{ condition: string | object; style: Record<string, string> }[]optionalConditional formatting rules for list rows
inlineEditbooleanoptionalAllow inline editing of records directly in the list view
exportOptionsEnum<'csv' | 'xlsx' | 'json'>[] | { formats?: Enum<'csv' | 'xlsx' | 'json'>[]; maxRecords?: integer; includeHeaders?: boolean; fileNamePrefix?: string; … }optionalExport configuration for the list toolbar export menu: { formats?, maxRecords?, includeHeaders?, fileNamePrefix?, streaming? }. A bare format array is the legacy spelling and lifts to { formats: [...] } at parse.
userActions{ sort?: boolean; search?: boolean; filter?: boolean; refresh?: boolean; … }optionalUser action toggles for the view toolbar
appearance{ showDescription?: boolean; allowedVisualizations?: Enum<'grid' | 'kanban' | 'gallery' | 'calendar' | 'timeline' | 'gantt' | 'map' | 'chart' | 'tree'>[] }optionalAppearance and visualization configuration
tabs{ name: string; label?: string | Record<string, string>; icon?: string; view?: string; … }[]optionalTab definitions for multi-tab view interface
addRecord{ enabled?: boolean; position?: Enum<'top' | 'bottom' | 'both'>; mode?: Enum<'inline' | 'form' | 'modal'>; formView?: string }optionalAdd record entry point configuration
showRecordCountbooleanoptionalShow record count at the bottom of the list
allowPrintingbooleanoptionalAllow users to print the view
emptyState{ title?: string | Record<string, string>; message?: string | Record<string, string>; icon?: string }optionalEmpty state configuration when no records found
aria{ ariaLabel?: string | Record<string, string>; ariaDescribedBy?: string; role?: string }optionalARIA accessibility attributes for the list view
responsiveneveroptional[REMOVED] view.responsive was removed in @objectstack/spec 17.0.0 (audit close-out) — no renderer ever read it; the grid is responsive by its own layout rules. Delete the key. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
performanceneveroptional[REMOVED] view.performance was removed in @objectstack/spec 17.0.0 (audit close-out) — no renderer or runtime read it; list-view performance tuning was never implemented. Delete the key. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
stripedneveroptional[REMOVED] view.striped was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — every measured reader only copied it forward and no renderer ever applied it, so authoring it was a parse-clean no-op. There is no authorable striped-rows switch; delete the key. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
borderedneveroptional[REMOVED] view.bordered was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — every measured reader only copied it forward and no renderer ever applied it (the grid frame is the renderer's own constant, not authorable). Delete the key. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
virtualScrollneveroptional[REMOVED] view.virtualScroll was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — every measured reader only copied it forward and no grid ever virtualized off it; authoring it was a parse-clean no-op. Delete the key; large datasets page via pagination. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
userFilters{ element?: Enum<'dropdown' | 'toggle'>; fields?: object[] }optional

Nested Shape: GetUiViewResponse.form

PropertyTypeRequiredDescription
typeEnum<'simple' | 'tabbed' | 'wizard' | 'split' | 'drawer' | 'modal'>optional (default: "simple")
layoutEnum<'vertical' | 'horizontal' | 'inline' | 'grid'>optionalField layout direction
columnsintegeroptionalNumber of columns for the form body
titlestringoptionalForm title
descriptionstringoptionalForm description
defaultTabstringoptionalInitially active tab (tabbed forms)
tabPositionEnum<'top' | 'bottom' | 'left' | 'right'>optionalTab strip position (tabbed forms)
allowSkipbooleanoptionalAllow skipping steps (wizard forms)
showStepIndicatorbooleanoptionalShow the step indicator (wizard forms)
splitDirectionEnum<'horizontal' | 'vertical'>optionalSplit orientation (split forms)
splitSizenumberoptionalPrimary split panel size, % (split forms)
splitResizablebooleanoptionalWhether the split is resizable (split forms)
drawerSideEnum<'top' | 'bottom' | 'left' | 'right'>optionalDrawer side (drawer forms)
drawerWidthstringoptional[DEPRECATED → size buckets] Drawer width, e.g. "480px". A pixel width cannot be chosen without knowing the client viewport — the renderer derives it.
modalSizeEnum<'sm' | 'default' | 'lg' | 'xl' | 'full'>optionalModal size (modal forms)
data{ provider: 'object'; object: string } | { provider: 'api'; read?: object; write?: object } | { provider: 'value'; items: any[] } | { provider: 'schema'; schemaId: string; schema?: Record<string, any> }optionalData source configuration (defaults to "object" provider)
sections{ name?: string; label?: string | Record<string, string>; description?: string; collapsible?: boolean; … }[]optional
groups{ name?: string; label?: string | Record<string, string>; description?: string; collapsible?: boolean; … }[]optional[LEGACY ALIAS → sections] Accepted for back-compat and folded onto sections at parse; sections wins when both are present. Prefer sections.
subforms{ childObject: string; relationshipField?: string; columns?: any[]; amountField?: string; … }[]optionalInline master-detail child collections
defaultSortneveroptional[REMOVED] form.defaultSort was removed in @objectstack/spec 17.0.0 (audit close-out) — nothing read it: a related list inside a form sorts by its own list view's sort. Delete the key and set the sort on the related list view instead. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
sharing{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }optionalPublic sharing configuration for this form
submitBehavior{ kind: 'thank-you'; title?: string; message?: string } | { kind: 'redirect'; url: string; delayMs?: integer } | { kind: 'continue' } | { kind: 'next-record' }optionalPost-submit behavior. On the redirect arm, url is relative-only and interpolates only declared record fields as {{record.field_name}}, URL-escaped (ruled 2026-08-11).
buttons{ submit?: object; cancel?: object; reset?: object }optionalForm action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm.
defaultsRecord<string, any>optionalInitial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;).
arianeveroptional[REMOVED] form.aria was removed in @objectstack/spec 17.0.0 (audit close-out) — no form renderer ever applied it, so declared ARIA attributes silently did not reach the DOM. Delete the key. The form renderer emits its own semantic markup; report gaps as renderer issues rather than per-view attribute overrides. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.

Nested Shape: GetUiViewResponse.listViews[string]

PropertyTypeRequiredDescription
namestringoptionalInternal view name (lowercase snake_case)
labelstring | Record<string, string>optionalDisplay label — the default-language string, or an inline locale map ({ en, "zh-CN" }) resolved at render time
typeEnum<'grid' | 'kanban' | 'gallery' | 'calendar' | 'timeline' | 'gantt' | 'map' | …>optional (default: "grid")
data{ provider: 'object'; object: string } | { provider: 'api'; read?: object; write?: object } | { provider: 'value'; items: any[] } | { provider: 'schema'; schemaId: string; schema?: Record<string, any> }optionalData source configuration (defaults to "object" provider)
columnsstring[] | { field: string; label?: string | Record<string, string>; width?: number; align?: Enum<'left' | 'center' | 'right'>; … }[]Fields to display as columns
filter{ field: string; operator?: Enum<'equals' | 'not_equals' | 'contains' | 'not_contains' | 'icontains' | …>; value?: string | number | boolean | null | (string | number)[] }[]optionalFilter criteria (JSON Rules)
sortstring | { field: string; order: Enum<'asc' | 'desc'> }[]optional
searchableFieldsstring[]optionalFields enabled for search
filterableFieldsstring[]optionalLegacy shorthand for userFilters.fields — bare field names enabled for end-user filtering. Prefer userFilters
resizablebooleanoptionalEnable column resizing
compactToolbarbooleanoptionalCollapse Group/Color/Density/Hide-fields into a single View settings popover
selection{ type?: Enum<'none' | 'single' | 'multiple'> }optionalRow selection configuration
navigation{ mode?: Enum<'page' | 'drawer' | 'modal' | 'split' | 'popover' | 'new_window' | 'none'>; view?: string; preventNavigation?: boolean; openNewTab?: boolean; … }optionalConfiguration for item click navigation (page, drawer, modal, etc.)
pagination{ pageSize?: integer; pageSizeOptions?: integer[] }optionalPagination configuration
kanban{ groupByField: string; summarizeField?: string; columns: string[] }optionalKanban-board configuration — applies when the view renders as a kanban layout
calendar{ startDateField: string; endDateField?: string; titleField: string; colorField?: string }optionalCalendar configuration — applies when the view renders as a calendar layout
gantt{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … } & Record<string, any>optionalGantt-timeline configuration — applies when the view renders as a gantt layout
gallery{ coverField?: string; coverFit?: Enum<'cover' | 'contain'>; cardSize?: Enum<'small' | 'medium' | 'large'>; titleField?: string; … }optionalGallery/card view configuration
timeline{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }optionalTimeline view configuration
chart{ chartType?: Enum<'bar' | 'line' | 'pie' | 'area' | 'scatter'>; dataset: string; dimensions?: string[]; values: string[] }optionalList chart view configuration
map{ latitudeField?: string; longitudeField?: string; locationField?: string; titleField?: string; … }optionalMap configuration — applies when the view renders as a map layout
tree{ parentField?: string; labelField?: string; fields?: string[]; defaultExpandedDepth?: integer } & Record<string, any>optionalTree/hierarchy configuration — applies when the view renders as a tree layout
pageNamestringoptionalPublished page this view mounts — required when type: 'page', and refused on every other view type. Rendering is delegated to the existing page renderer; the page keeps its own assignedProfiles audience.
descriptionstring | Record<string, string>optionalView description for documentation/tooltips
sharing{ type?: Enum<'personal' | 'collaborative'>; lockedBy?: string }optionalView sharing and access configuration
rowHeightEnum<'compact' | 'short' | 'medium' | 'tall' | 'extra_tall'>optionalRow height / density setting
grouping{ fields: object[] }optionalGroup records by one or more fields
rowColor{ field: string; colors?: Record<string, string> }optionalColor rows based on field value
hiddenFieldsstring[]optionalFields to hide in this specific view
fieldOrderstring[]optionalExplicit field display order for this view
rowActionsstring[]optionalActions available for individual row items
bulkActionsstring[]optionalActions available when multiple rows are selected
bulkActionDefs{ name: string; label?: string; icon?: string; variant?: Enum<'primary' | 'secondary' | 'danger' | 'ghost' | 'outline'>; … }[]optionalRich bulk action definitions (schema-driven, executed via BulkActionDialog). Use a def for a mass data-plane mutation ('update' with a patch / 'delete') that no action expresses, or for an operation: 'custom' + execution: 'aggregate' entry that dispatches the action it NAMES once for the whole selection — the renderer injects params._selectedIds: string[] (read that on the server, not recordId) so a single call can produce one aggregate artifact (zip of QR codes, merged PDF, batch print). Aggregate results are all-or-nothing: a handler that cannot cover the whole selection must reject, and per-row retry is replaced by re-running the action. batchSize does not apply (the call is never chunked); set maxRecords on defs whose server work is expensive. For the PER-RECORD dispatch use bulkActions: ['<name>'] instead — the bare-string form, promoted with the action's own label, params and visible; a 'custom' def without execution: 'aggregate' has no dispatcher and is refused at parse time. Toolbar url/api actions can also interpolate the current selection via ${ctx.selection.ids} / ${ctx.selection.count}.
conditionalFormatting{ condition: string | object; style: Record<string, string> }[]optionalConditional formatting rules for list rows
inlineEditbooleanoptionalAllow inline editing of records directly in the list view
exportOptionsEnum<'csv' | 'xlsx' | 'json'>[] | { formats?: Enum<'csv' | 'xlsx' | 'json'>[]; maxRecords?: integer; includeHeaders?: boolean; fileNamePrefix?: string; … }optionalExport configuration for the list toolbar export menu: { formats?, maxRecords?, includeHeaders?, fileNamePrefix?, streaming? }. A bare format array is the legacy spelling and lifts to { formats: [...] } at parse.
userActions{ sort?: boolean; search?: boolean; filter?: boolean; refresh?: boolean; … }optionalUser action toggles for the view toolbar
appearance{ showDescription?: boolean; allowedVisualizations?: Enum<'grid' | 'kanban' | 'gallery' | 'calendar' | 'timeline' | 'gantt' | 'map' | 'chart' | 'tree'>[] }optionalAppearance and visualization configuration
tabs{ name: string; label?: string | Record<string, string>; icon?: string; view?: string; … }[]optionalTab definitions for multi-tab view interface
addRecord{ enabled?: boolean; position?: Enum<'top' | 'bottom' | 'both'>; mode?: Enum<'inline' | 'form' | 'modal'>; formView?: string }optionalAdd record entry point configuration
showRecordCountbooleanoptionalShow record count at the bottom of the list
allowPrintingbooleanoptionalAllow users to print the view
emptyState{ title?: string | Record<string, string>; message?: string | Record<string, string>; icon?: string }optionalEmpty state configuration when no records found
aria{ ariaLabel?: string | Record<string, string>; ariaDescribedBy?: string; role?: string }optionalARIA accessibility attributes for the list view
responsiveneveroptional[REMOVED] view.responsive was removed in @objectstack/spec 17.0.0 (audit close-out) — no renderer ever read it; the grid is responsive by its own layout rules. Delete the key. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
performanceneveroptional[REMOVED] view.performance was removed in @objectstack/spec 17.0.0 (audit close-out) — no renderer or runtime read it; list-view performance tuning was never implemented. Delete the key. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
stripedneveroptional[REMOVED] view.striped was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — every measured reader only copied it forward and no renderer ever applied it, so authoring it was a parse-clean no-op. There is no authorable striped-rows switch; delete the key. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
borderedneveroptional[REMOVED] view.bordered was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — every measured reader only copied it forward and no renderer ever applied it (the grid frame is the renderer's own constant, not authorable). Delete the key. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
virtualScrollneveroptional[REMOVED] view.virtualScroll was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — every measured reader only copied it forward and no grid ever virtualized off it; authoring it was a parse-clean no-op. Delete the key; large datasets page via pagination. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
userFilters{ element?: Enum<'dropdown' | 'toggle'>; fields?: object[] }optional

Nested Shape: GetUiViewResponse.formViews[string]

PropertyTypeRequiredDescription
typeEnum<'simple' | 'tabbed' | 'wizard' | 'split' | 'drawer' | 'modal'>optional (default: "simple")
layoutEnum<'vertical' | 'horizontal' | 'inline' | 'grid'>optionalField layout direction
columnsintegeroptionalNumber of columns for the form body
titlestringoptionalForm title
descriptionstringoptionalForm description
defaultTabstringoptionalInitially active tab (tabbed forms)
tabPositionEnum<'top' | 'bottom' | 'left' | 'right'>optionalTab strip position (tabbed forms)
allowSkipbooleanoptionalAllow skipping steps (wizard forms)
showStepIndicatorbooleanoptionalShow the step indicator (wizard forms)
splitDirectionEnum<'horizontal' | 'vertical'>optionalSplit orientation (split forms)
splitSizenumberoptionalPrimary split panel size, % (split forms)
splitResizablebooleanoptionalWhether the split is resizable (split forms)
drawerSideEnum<'top' | 'bottom' | 'left' | 'right'>optionalDrawer side (drawer forms)
drawerWidthstringoptional[DEPRECATED → size buckets] Drawer width, e.g. "480px". A pixel width cannot be chosen without knowing the client viewport — the renderer derives it.
modalSizeEnum<'sm' | 'default' | 'lg' | 'xl' | 'full'>optionalModal size (modal forms)
data{ provider: 'object'; object: string } | { provider: 'api'; read?: object; write?: object } | { provider: 'value'; items: any[] } | { provider: 'schema'; schemaId: string; schema?: Record<string, any> }optionalData source configuration (defaults to "object" provider)
sections{ name?: string; label?: string | Record<string, string>; description?: string; collapsible?: boolean; … }[]optional
groups{ name?: string; label?: string | Record<string, string>; description?: string; collapsible?: boolean; … }[]optional[LEGACY ALIAS → sections] Accepted for back-compat and folded onto sections at parse; sections wins when both are present. Prefer sections.
subforms{ childObject: string; relationshipField?: string; columns?: any[]; amountField?: string; … }[]optionalInline master-detail child collections
defaultSortneveroptional[REMOVED] form.defaultSort was removed in @objectstack/spec 17.0.0 (audit close-out) — nothing read it: a related list inside a form sorts by its own list view's sort. Delete the key and set the sort on the related list view instead. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.
sharing{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }optionalPublic sharing configuration for this form
submitBehavior{ kind: 'thank-you'; title?: string; message?: string } | { kind: 'redirect'; url: string; delayMs?: integer } | { kind: 'continue' } | { kind: 'next-record' }optionalPost-submit behavior. On the redirect arm, url is relative-only and interpolates only declared record fields as {{record.field_name}}, URL-escaped (ruled 2026-08-11).
buttons{ submit?: object; cancel?: object; reset?: object }optionalForm action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm.
defaultsRecord<string, any>optionalInitial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;).
arianeveroptional[REMOVED] form.aria was removed in @objectstack/spec 17.0.0 (audit close-out) — no form renderer ever applied it, so declared ARIA attributes silently did not reach the DOM. Delete the key. The form renderer emits its own semantic markup; report gaps as renderer issues rather than per-view attribute overrides. Run os migrate meta --from 16 to list the mechanical edits for existing sources; apply them by hand.

Nested Shape: GetUiViewResponse.protection

PropertyTypeRequiredDescription
lockEnum<'none' | 'no-overlay' | 'no-delete' | 'full'>Lock policy — none | no-overlay | no-delete | full.
reasonstringUser-visible reason shown when the lock blocks an action.
docsUrlstringoptionalOptional URL the Studio banner links to for more context.

HistoryMetaItemRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name
organizationIdstringoptionalOrganization (tenant) partition the change log is read from. Absent means the env-wide partition (the implementation resolves the overlay repository as organizationId ?? null, keyed org: 'env'). Declared because the implementation declares and reads it; plain string, not nullable, because that is the implementation's parameter type — unlike the audit twin, whose door always sends ctx?.tenantId ?? null and whose implementation declares string | null. The REST history door currently sends no organization at all (whether it should is a tenant-scoping question measured separately for that door — declaring the member records the implementation contract, it does not answer that question).
sinceSeqnumberoptionalExclusive lower bound on seq for pagination: only events with seq > sinceSeq are returned. Absent means "from the beginning".
limitnumberoptionalMaximum events to return, oldest first. Forwarded to the repository unclamped and with NO default — absent means the full remaining change log. (Deliberately no declared bounds: unlike the audit twin's [1, 500] clamp, nothing on this path clamps or refuses, so declaring .min()/.max() here would refuse values the shipped verb accepts.)

HistoryMetaItemResponse

Properties

PropertyTypeRequiredDescription
events{ seq: integer; op: Enum<'create' | 'update' | 'delete' | 'rename' | 'publish' | 'revert'>; ref: object; hash: string | null; … }[]The durable change-log for the item, oldest first. See the schema-level note for what an empty array means — and what it never means.

Nested Shape: HistoryMetaItemResponse.events[number]

PropertyTypeRequiredDescription
seqintegerSequence number this write produced in the org log (sys_metadata_history.event_seq) — the token the request's sinceSeq filters on, and the same key the write-verb receipts (SaveMetaItemResponseSchema.seq and siblings) carry.
opEnum<'create' | 'update' | 'delete' | 'rename' | 'publish' | 'revert'>Which change-log operation the event records (ADR-0008 §2.4). Closed vocabulary, mirrored from the producer's own enum.
ref{ org: string; type: string; name: string; version?: string }Which item the event is about.
hashstring | nullContent hash of the body this event wrote; null when the event wrote none (op="delete").
parentHashstring | nullHash the written version was derived from; null for a first version.
versionintegeroptionalPer-(org,type,name) monotonic lineage counter at this event — the token rollbackMetaItem({ toVersion }) pins. Absent when the row recorded none.
previousNamestringoptionalSet on op="rename": the old machine name.
actorstring | nullWho wrote this. null = system-initiated (boot sync, migration, scheduled job) — never a sentinel string: consumers that resolve this against sys_user must be able to tell "nobody" from "a user id".
messagestringoptionalOptional commit message recorded with the write.
tsstringWhen the write happened (ISO-8601 string).
sourcestringOrigin label of the write: "fs", "studio", "rest", "ai", "git-import", …

HttpFindQueryParams

Properties

PropertyTypeRequiredDescription
filterstringoptionalJSON-encoded filter expression (canonical, singular).
filtersstringoptionalJSON-encoded filter expression (deprecated plural alias).
selectstringoptionalComma-separated list of fields to retrieve.
sortstringoptionalSort expression (e.g. "name asc,created_at desc" or "-created_at").
orderBystringoptionalAlias for sort (OData compatibility).
topnumberoptionalMax records to return (limit).
skipnumberoptionalRecords to skip (offset).
expandstringoptionalComma-separated list of lookup/master_detail field names to expand. Resolved to populate array and passed to the engine for batch $in expansion.
searchstringoptionalFull-text search query.
distinctneveroptional[REMOVED] query.distinct was removed in @objectstack/spec 17 (ADR-0049 / ADR-0078) — no driver ever rendered SELECT DISTINCT; the flag's only observable effect was MIS-WIRED: the REST list path treated a distinct query as not countable and silently degraded total/hasMore to a page-local estimate while still returning duplicate rows. Delete the key; QueryBuilder.distinct() was removed with it, and the count suppression is gone (total is truthful again). For unique values of one column use the SQL/memory drivers' distinct(object, field) door; for unique combinations, groupBy; for a deduplicated count, the count_distinct aggregation.
countbooleanoptionalInclude total count in response.

InstallPackageRequest

Install package request

Properties

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }Package manifest to install
settingsRecord<string, any>optionalUser-provided settings at install time
enableOnInstallbooleanoptional (default: true)Whether to enable immediately after install
platformVersionstringoptionalCurrent platform version for compatibility verification

Nested Shape: InstallPackageRequest.manifest

PropertyTypeRequiredDescription
idstringUnique package identifier (reverse domain style)
namespacestringoptionalShort namespace identifier; also the mandatory prefix of every object name (e.g. "todo" → object names "todo_task", "todo_project")
defaultDatasourcestringoptional (default: "default")Default datasource for all objects in this package
versionstringPackage version (semantic versioning)
typeEnum<'plugin' | 'ui' | 'driver' | 'server' | 'app' | 'theme' | 'agent' | 'objectql' | …>Type of package
scopeEnum<'cloud' | 'system' | 'project'>optional (default: "project")Deployment scope: cloud | system | project
namestringHuman-readable package name
descriptionstringoptionalPackage description
permissionsstring[] | { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }optionalRequired permissions: legacy string[] or structured plugin block (ADR-0025 §3.2)
objectsstring[]optionalGlob patterns for ObjectQL schemas files
datasourcesstring[]optionalGlob patterns for Datasource definitions
dependenciesRecord<string, string>optionalPackage dependencies
configurationneveroptional[REMOVED] manifest.configuration was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read the block: no settings UI rendered it and no loader resolved a setting from it, so authoring it configured nothing. Worse, properties.*.secret promised "value is encrypted/masked (e.g. API Keys)" while nothing encrypted, masked or even parsed the flag — a false assurance about credential handling. Delete the key. A plugin is configured by the host that composes it: pass options to its constructor in defineStack({ plugins: [new MyPlugin({ … })] }), which is the enforced channel. A declarative settings surface must be designed with an enforcing reader first, not revived here.
contributes{ kinds?: object[] }optionalPlatform contributions
data{ object: string; externalId?: string | string[]; mode?: Enum<'insert' | 'update' | 'upsert' | 'replace' | 'ignore'>; env?: Enum<'prod' | 'dev' | 'test'>[]; … }[]optionalInitial seed data (prefer top-level data field)
capabilitiesneveroptional[REMOVED] manifest.capabilities was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — no discovery path ever consulted the block: nothing read implements, provides, requires, extensionPoints or extensions, so the declared "interoperability and automatic discovery" never happened. Delete the key. Real dependency resolution runs off top-level manifest.dependencies, which stays. Capability-based discovery must be designed with an enforcing reader first, not revived here.
extensionsneveroptional[REMOVED] manifest.extensions was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — an untyped map with zero readers: whatever was parked here was stored and never consulted. Delete the key. Extend the platform through the enforced channels instead: contributes.kinds registers metadata kinds, navigationContributions injects navigation into other packages' apps, and code-level extension happens in the plugin itself (init/start).
navigationContributions{ app: string; group?: string; priority?: integer; items: (object | … +8 more)[] }[]optionalNavigation items this package contributes into apps owned by other packages
loadingneveroptional[REMOVED] manifest.loading was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — the entire block (strategy, preload, codeSplitting, dynamicImport, initialization, dependencyResolution, hotReload, caching, sandboxing, monitoring) had no runtime reader in any repo, so authoring it configured nothing. Delete the key. Plugins are composed at boot — defineStack registers them and the kernel runs init then start in an order topologically resolved from each composed plugin's own dependencies / optionalDependencies (resolvePluginOrder); the set is fixed until the process restarts. ⚠️ loading.sandboxing in particular never isolated anything: it did not run plugins in a process, vm, iframe or web-worker, and allowedServices gated no call. If you were relying on it for isolation, you had none — and the plugin trust tier (manifest.runtime) does not give it back: that tier is enforced at the cloud marketplace PUBLISH gate only (an unverified publisher requesting the node tier is rejected with HTTP 422 and forced to manual review), while load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares. Use the permission declarations, which are enforced.
engine{ objectstack: string }optionalPlatform compatibility requirements (legacy; superseded by engines)
engines{ platform?: string; protocol?: string }optionalPlugin compatibility ranges (ADR-0025 §3.2; supersedes engine)
runtimeEnum<'node' | 'sandbox' | 'worker'>optionalPlugin trust tier the plugin declares (ADR-0025 §3.6) — enforced at the cloud marketplace publish gate (unverified publisher requesting node → HTTP 422 + manual review); load-side enforcement is NOT implemented, so a locally installed plugin is not isolated by the tier it declares
packagingEnum<'bundled' | 'manifest-deps'>optionalDependency packaging strategy (ADR-0025 §3.3)
integrityRecord<string, string>optionalPer-file content digests of the plugin artifact (ADR-0025 §3.2)

InstallPackageResponse

Install package response

Properties

PropertyTypeRequiredDescription
package{ manifest: object; status?: Enum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>; enabled?: boolean; installedAt?: string; … }Installed package details
messagestringoptionalInstallation status message
dependencyResolution{ dependencies: object[]; canProceed: boolean; requiredActions: object[]; installOrder: string[]; … }optionalDependency resolution result from install analysis

Nested Shape: InstallPackageResponse.package

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }Full package manifest
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

Nested Shape: InstallPackageResponse.dependencyResolution

PropertyTypeRequiredDescription
dependencies{ packageId: string; requiredRange: string; resolvedVersion?: string; installedVersion?: string; … }[]Resolution result for each dependency
canProceedbooleanWhether installation can proceed
requiredActions{ type: Enum<'install' | 'upgrade' | 'confirm_conflict'>; packageId: string; description: string }[]Actions required before proceeding
installOrderstring[]Topologically sorted package IDs for installation
circularDependenciesstring[][]optionalCircular dependency chains detected (e.g. [["A", "B", "A"]])

ListAiConversationsRequest

Properties

PropertyTypeRequiredDescription
agentIdstringoptionalFilter by agent
limitintegeroptionalMaximum conversations to return
cursorstringoptionalPagination cursor

ListAiConversationsResponse

Properties

PropertyTypeRequiredDescription
conversations{ id: string; title?: string; agentId?: string; userId?: string; … }[]Matching conversations

Nested Shape: ListAiConversationsResponse.conversations[number]

PropertyTypeRequiredDescription
idstringConversation id
titlestringoptionalTitle / summary
agentIdstringoptionalAgent this conversation is bound to
userIdstringoptionalOwning user
messages({ role: Enum<'system' | 'user' | 'assistant' | 'tool'>; content?: any; parts?: any[] } & Record<string, any>)[]Message history
createdAtstringCreation timestamp (ISO 8601)
updatedAtstringLast update timestamp (ISO 8601)
metadataRecord<string, any>optionalConversation metadata

ListAiPendingActionsRequest

Properties

PropertyTypeRequiredDescription
statusEnum<'pending' | 'approved' | 'executed' | 'failed' | 'rejected'>optionalFilter by status
conversationIdstringoptionalFilter by proposing conversation
limitintegeroptionalMax rows (server default 100)

ListAiPendingActionsResponse

Properties

PropertyTypeRequiredDescription
items{ id: string; object_name: string; action_name: string; tool_name: string; … }[]Queued actions, newest first
totalnumberNumber of rows returned (not a total across pages)

Nested Shape: ListAiPendingActionsResponse.items[number]

PropertyTypeRequiredDescription
idstringPending action id
object_namestringObject the action targets
action_namestringAction name
tool_namestringTool that would execute it
tool_inputstringSerialized tool input
statusEnum<'pending' | 'approved' | 'executed' | 'failed' | 'rejected'>Lifecycle status
resultstringoptionalSerialized result, once executed
errorstringoptionalFailure message, when status is failed
rejection_reasonstringoptionalReason given at rejection
conversation_idstringoptionalConversation that proposed it
message_idstringoptionalMessage that proposed it
proposed_bystringoptionalActor that proposed it
decided_bystringoptionalActor that approved or rejected it
proposed_atstringProposal timestamp (ISO 8601)
decided_atstringoptionalDecision timestamp (ISO 8601)

ListDraftsResponse

Properties

PropertyTypeRequiredDescription
drafts{ type: string; name: string; organizationId: string | null; packageId: string | null; … }[]Every pending draft visible to the caller, one row per item.

Nested Shape: ListDraftsResponse.drafts[number]

PropertyTypeRequiredDescription
typestringMetadata type name (canonical singular).
namestringItem name.
organizationIdstring | nullOwning organization of the draft row, null for an environment-wide draft.
packageIdstring | nullPackage the draft is bound to, null for a package-less draft.
updatedAtstring | nullLast-touch timestamp of the draft row (ISO-8601 string), null when the row recorded none.
updatedBystring | nullWho last touched the draft, null when the row recorded none.

ListNotificationsRequest

Properties

PropertyTypeRequiredDescription
readbooleanoptionalFilter by read status
typestringoptionalFilter by notification type
limitnumberoptionalMaximum number of notifications to return — the newest N. Omitted leaves the window to the server, which is not a fixed part of this contract: the platform inbox answers 50 and clamps any requested value into 1..200 rather than refusing it. This endpoint is not paginated — there is no continuation token, so a larger window is the only way to see more.
cursorneveroptional[REMOVED] cursor was removed from GET /api/v1/notifications in @objectstack/spec 17 (ADR-0049) — it was declared on the request AND the response and honoured on neither: the server reads only read/type/limit, and no emit site ever wrote the response key, so a caller paginating by it re-read the first window forever with no error and no 400. Delete the key; the cursor argument of client.notifications.list() was removed with it. This route is NOT paginated — it answers the newest limit notifications and stops, so ask for a bigger window (limit, clamped by the server into 1..200) instead of a next page. A first-class inbox cursor, if ever built, will be a response-minted opaque token, not this key.

ListNotificationsResponse

Properties

PropertyTypeRequiredDescription
notifications{ id: string; type: string; title: string; body: string; … }[]List of notifications — the newest window, not a page
unreadCountnumberTotal number of unread notifications
cursorneveroptional[REMOVED] cursor was removed from GET /api/v1/notifications in @objectstack/spec 17 (ADR-0049) — it was declared on the request AND the response and honoured on neither: the server reads only read/type/limit, and no emit site ever wrote the response key, so a caller paginating by it re-read the first window forever with no error and no 400. Delete the key; the cursor argument of client.notifications.list() was removed with it. This route is NOT paginated — it answers the newest limit notifications and stops, so ask for a bigger window (limit, clamped by the server into 1..200) instead of a next page. A first-class inbox cursor, if ever built, will be a response-minted opaque token, not this key.

Nested Shape: ListNotificationsResponse.notifications[number]

PropertyTypeRequiredDescription
idstringNotification ID
typestringNotification type
titlestringNotification title
bodystringNotification body text
readbooleanoptional (default: false)Whether notification has been read
dataRecord<string, any>optionalAdditional notification data
actionUrlstringoptionalURL to navigate to when clicked
createdAtstringWhen notification was created

ListPackagesRequest

List packages request

Properties

PropertyTypeRequiredDescription
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optionalFilter by package status
typeEnum<'plugin' | 'ui' | 'driver' | 'server' | 'app' | 'theme' | 'agent' | 'objectql' | 'module' | 'gateway' | 'adapter'>optionalFilter by package type
enabledbooleanoptionalFilter by enabled state

ListPackagesResponse

List packages response

Properties

PropertyTypeRequiredDescription
packages{ manifest: object; status?: Enum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>; enabled?: boolean; installedAt?: string; … }[]List of installed packages
totalnumberTotal package count

Nested Shape: ListPackagesResponse.packages[number]

Installed package with runtime lifecycle state

PropertyTypeRequiredDescription
manifest{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }Full package manifest
statusEnum<'installed' | 'disabled' | 'installing' | 'upgrading' | 'uninstalling' | 'error'>optional (default: "installed")Package state: installed, disabled, installing, upgrading, uninstalling, or error
enabledbooleanoptional (default: true)Whether the package is currently enabled
installedAtstringoptionalInstallation timestamp
updatedAtstringoptionalLast update timestamp
installedVersionstringoptionalCurrently installed version for quick access
previousVersionstringoptionalVersion before the last upgrade
statusChangedAtstringoptionalStatus change timestamp
errorMessagestringoptionalError message when status is error
settingsRecord<string, any>optionalUser-provided configuration settings
upgradeHistory{ fromVersion: string; toVersion: string; upgradedAt: string; status: Enum<'success' | 'failed' | 'rolled_back'>; … }[]optionalVersion upgrade history
registeredNamespacesstring[]optionalNamespace prefixes registered by this package

MarkAllNotificationsReadRequest

Properties

PropertyTypeRequiredDescription

MarkAllNotificationsReadResponse

Properties

PropertyTypeRequiredDescription
successbooleanWhether the operation succeeded
readCountnumberNumber of notifications marked as read

MarkNotificationsReadRequest

Properties

PropertyTypeRequiredDescription
idsstring[]Notification IDs to mark as read

MarkNotificationsReadResponse

Properties

PropertyTypeRequiredDescription
successbooleanWhether the operation succeeded
readCountnumberNumber of notifications marked as read

Notification

Properties

PropertyTypeRequiredDescription
idstringNotification ID
typestringNotification type
titlestringNotification title
bodystringNotification body text
readbooleanoptional (default: false)Whether notification has been read
dataRecord<string, any>optionalAdditional notification data
actionUrlstringoptionalURL to navigate to when clicked
createdAtstringWhen notification was created

NotificationPreferences

Properties

PropertyTypeRequiredDescription
emailbooleanoptional (default: true)Receive email notifications
pushbooleanoptional (default: true)Receive push notifications
inAppbooleanoptional (default: true)Receive in-app notifications
digestEnum<'none' | 'daily' | 'weekly'>optional (default: "none")Email digest frequency
channelsRecord<string, { enabled: boolean; email?: boolean; push?: boolean }>optionalPer-channel notification preferences

Nested Shape: NotificationPreferences.channels[string]

PropertyTypeRequiredDescription
enabledbooleanoptional (default: true)Whether this channel is enabled
emailbooleanoptionalOverride email setting
pushbooleanoptionalOverride push setting

PublishMetaItemRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name — lowercase snake_case segments, optionally dot-qualified (crm_lead, crm_lead.pipeline). The promotion door enforces the same grammar as saveMetaItem.
organizationIdstringoptionalOrganization (tenant) scope for the promotion. The implementation resolves the draft through the org partition (ADR-0005), so a draft authored org-scoped must be published under the same scope or the lookup answers 404 [no_draft]. Absent = environment-wide.
actorstringoptionalIdentity recorded on the op='publish' history event. On the REST door this is the request's authenticated identity (one producer) — never a caller-supplied header.
messagestringoptionalOptional human-readable note recorded with the publish history event.
packageIdstring | nulloptionalADR-0048 — the software package the draft being promoted was listed under, when the caller has one to state (?package=<id> on the REST door). ⚠️ null is NOT the same as absent, and the difference is load-bearing: the implementation branches on the KEY BEING PRESENT, so an ABSENT key keeps the historical "match any package" resolution while null pins the lookup to the package-UNBOUND row. Spread it in conditionally; a present-and-undefined key coerces to null downstream and makes a package-bound draft unfindable — a silent no_draft on the untouched path.

PublishMetaItemResponse

Properties

PropertyTypeRequiredDescription
successbooleanAlways true on a 2xx — the draft was promoted. It does NOT cover the best-effort side effects below, each of which reports its own success.
versionstringContent hash of the just-promoted body, and the token the ADR-0008 optimistic-concurrency chain runs on: send it back as the If-Match request header on the next write to that item and a concurrent edit is reported as 409 metadata_conflict instead of silently overwritten. Opaque to callers — echo it verbatim, never parse it. Currently emitted as sha256:<64 hex chars>, but the format is not part of this contract.
seqintegerMonotonic sequence number of the op='publish' metadata event this promotion appended to the item history (sys_metadata_history.event_seq). Orders writes; unlike version it is not an OCC token.
seedApplied{ success: boolean; inserted: integer; updated: integer; error?: string; … }optionalOutcome of materializing a published seed body into data rows. Present ONLY when the published type is seed — publishing a seed is what makes its rows live, so the load rides along with the metadata promotion. Best-effort: a seed-load problem is surfaced here, never thrown, so a caller must check seedApplied.success instead of assuming the 200 covered the data. Absent on the batch path, which suppresses the per-item apply and loads every seed body in one later pass.
materializeApplied{ success: boolean; inserted: integer; updated: integer; error?: string }optionalOutcome of the ADR-0086 P2 publish-time materializer — the step that projects the published body into its data-plane row (e.g. permissionsys_permission_set, under the owning package). Present ONLY when a materializer is registered for this metadata type, which is why it is optional: its absence means "no materializer ran", never "it failed". Best-effort, same contract as seedApplied.
projectionApplied{ success: boolean; error?: string }optionalOutcome of the awaited ADR-0094 mutation projector — the post-persist step that materializes this metadata into its derived data-plane read model. The same receipt {@link SaveMetaItemResponseSchema} carries, because the projector runs on BOTH write doors: a direct active save and this draft→active promotion. Present ONLY when a projector is registered for this metadata type. Best-effort — a projector failure is reported here and logged, never thrown.
advisories{ rule: string; path: string; where: string; message: string; … }[]optionalNon-gating findings from the runtime authoring gate — the same shared author-time rules os validate / os build / os lint run, applied to the DRAFT body this promotion carried to active (the same key SaveMetaItemResponseSchema carries, because the gate runs on both write doors, D1). The promotion SUCCEEDED; these are what the gate has to say about it anyway. Present ONLY when at least one advisory was raised — an empty array is never emitted, so a clean publish's response bytes are unchanged and absence means "nothing to report", never "the gate did not run". Advisory by construction: every entry has severity warning or info, because an error finding refuses the promotion and arrives as the 422 invalid_metadata envelope instead of here. A caller that ignores this key behaves exactly as before. This door is the one Studio's designer takes on every edit (draft save, then publish), and a Studio / MCP / AI author has no CLI at all — which is the gap this key exists to close.
messagestringoptionalHuman-readable receipt, e.g. Published draft — type=view, name=cases [seq=3]. The producer sets it on every publish today; it stays optional to match the producer's own signature and its SaveMetaItemResponse twin, and because an absent human-readable string strips no data — the failure mode this key exists to prevent.

Nested Shape: PublishMetaItemResponse.seedApplied

PropertyTypeRequiredDescription
successbooleanFalse when the seed rows did not fully land. The publish itself still succeeded — check this rather than assuming data went live.
insertedintegerRows created by the externalId-keyed upsert.
updatedintegerRows updated by the externalId-keyed upsert.
errorstringoptionalSingle failure message, present when the seed apply threw before the loader ran (including "no readable seed bodies").
errorsany[]optionalPer-record failures reported by the seed loader. Present only when the loader ran and returned a non-empty error list.

Nested Shape: PublishMetaItemResponse.materializeApplied

PropertyTypeRequiredDescription
successbooleanFalse when the materializer threw or reported failure; the publish still succeeded.
insertedintegerData-plane rows created by the materializer.
updatedintegerData-plane rows updated by the materializer.
errorstringoptionalMaterializer failure message, present only when success is false.

Nested Shape: PublishMetaItemResponse.projectionApplied

PropertyTypeRequiredDescription
successbooleanFalse when the projector threw; the metadata promotion itself still succeeded.
errorstringoptionalProjector failure message, present only when success is false.

Nested Shape: PublishMetaItemResponse.advisories[number]

PropertyTypeRequiredDescription
rulestringStable diagnostic rule id (flow-multi-write-unfiltered, approval-expression-invalid, …). Machine-readable and stable across releases — the key a renderer groups or suppresses by.
pathstringConfig path inside the SUBMITTED body (flows[0].nodes[1].config.multi), so an editor can jump to the offending key. May be empty when the finding is about the item as a whole. For the collection-resident write types (object / permission / book) the TOP-LEVEL collection entry is keyed by NAME (objects.acme_invoice.sharingModel), never by an array index — the gate evaluates against a private per-write snapshot whose indexes no caller can resolve. Every other write type is the sole member of its own collection, so its [0] is trivially stable and stays positional (flows[0]...), as do nested positions inside one named item (objects.acme_invoice.indexes[1]), which index the author's own document.
wherestringHuman-readable location — flow "leave_approval" · node "approve". Prose for a person; use path for anything mechanical.
messagestringWhat is wrong, in the rule author's own words.
hintstringHow to fix it.
severityEnum<'error' | 'warning' | 'info'>How the gate treated this finding. error means the write was REFUSED (these appear on the 422, never on a 2xx); warning / info are advisory — the write succeeded and the finding is FYI.

PublishPackageDraftsResponse

Properties

PropertyTypeRequiredDescription
successbooleanTrue only when every pending draft promoted (failed empty) AND at least one item published. A pre-flight refusal or an ADR-0067 D2 rollback answers false on a 200 — but so does a publish with nothing to promote, so false alone is NOT a refusal: read outcome, not this boolean or the HTTP status. Always equal to outcome === 'published' (pinned). It does NOT cover the best-effort receipts below, each of which reports its own success.
outcomeEnum<'published' | 'refused' | 'nothing_to_publish'>First-class discriminant for WHICH exit answered — the fact success compresses into one boolean. published: at least one draft promoted and none refused. refused: the batch was refused — a pre-flight violation or the ADR-0067 D2 all-or-nothing rollback; the per-item story is in failed[], which is non-empty exactly on this outcome (the invariant consumers previously had to reverse-engineer, now stated by the producer). nothing_to_publish: the package had no pending drafts — nothing landed AND nothing was refused; success stays false (a no-op is not a successful publish), which before this field made that answer indistinguishable from a refusal. Producer invariants, pinned in the conformance suites: success === (outcome === 'published'); refused if and only if failed.length > 0; nothing_to_publish if and only if published.length === 0 && failed.length === 0. Values are lowercase snake, matching the sys_metadata_audit outcome vocabulary.
publishedCountintegerNumber of drafts promoted to active — published.length. 0 on every refusal path (the batch is all-or-nothing, ADR-0067 D2).
failedCountintegerNumber of items that did not publish — failed.length. On a rollback this counts the WHOLE batch: the causal item plus every sibling marked BATCH_ABORTED.
published{ type: string; name: string; version: string; advisories?: object[] }[]Every draft promoted to active, in publish order. Empty on every refusal path.
failed{ type: string; name: string; error: string; code?: string; … }[]Items that did not publish. Because the batch is all-or-nothing (ADR-0067 D2), a non-empty list means NOTHING landed: published: [], publishedCount: 0.
seedApplied{ success: boolean; inserted?: integer; updated?: integer; error?: string; … }optionalAggregate outcome of materializing EVERY published seed body in one multi-pass loader run (cross-seed references need the whole set). Present ONLY when the batch published at least one seed. Two producers, one key: the batch itself self-applies (applySeedBodies), and the REST door back-fills the same key for custom protocols that do not — never both (an externalId-less seed would double-insert). Best-effort: a seed problem is surfaced here, never thrown.
materializeApplied{ success: boolean; inserted: integer; updated: integer; failures: object[] }optionalADR-0086 P2 — aggregate result of publish-time materializers across the batch (e.g. permissionsys_permission_set), including side-effect failures surfaced by the per-item effects loop. Present ONLY when at least one published item had a registered materializer or a side-effect failure. Best-effort, same contract as seedApplied.
probesanyoptionalADR-0038 L3 post-publish runtime probe report — one real read per published artifact (seeded objects have rows, views are readable, widget dataset selections execute). DELIBERATELY OPAQUE in this contract: the key is declared and carried through verbatim, but its inner shape is intentionally not modeled until a consumer needs a field of it. Present only when something was publishable; probes never fail the publish.
commitIdstringoptionalADR-0067 — id of the commit this publish recorded. Absent when nothing published.
unhiddenAppsstring[]optionalADR-0045 §3 — names of the apps whose _unpublished gate this publish cleared (publish = live AND visible; a materialized additive build has no drafts, only this flip). Attached by the REST door, not the protocol helper. Present ONLY when at least one app flipped — on a mid-loop failure it names the apps that DID persist, beside unhideError ('s split report). Spelling is a permanent wire contract (see the door's note).
unhideErrorstringoptionalPresent when the ADR-0045 visibility flip failed (wholly or partway): the drafts ARE published, but apps still stored _unpublished: true stay externally unobservable. Client-facing text only — undeclared driver text is withheld per ADR-0112; the full cause is in the server log. The route is idempotent: re-run it once the cause is resolved.
rebindErrorstringoptionalPresent when the post-publish metadata:reloaded announce failed: everything is published and stored, but boot-cached consumers keep the pre-publish view until re-run or restart (a newly published record-triggered flow does not bind its trigger). Client-facing text only, same ADR-0112 withhold as unhideError.

Nested Shape: PublishPackageDraftsResponse.published[number]

PropertyTypeRequiredDescription
typestringMetadata type of the promoted draft (canonical singular).
namestringItem name of the promoted draft.
versionstringContent hash of the just-promoted body — the same ADR-0008 optimistic-concurrency token the single-item doors return: echo it back as If-Match on the next write to this item. Opaque to callers; currently sha256:<64 hex chars>, but the format is not part of this contract.
advisories{ rule: string; path: string; where: string; message: string; … }[]optionalNon-gating findings the runtime authoring gate raised against THIS draft's promotion (the same element shape and the same omitted-when-empty discipline as PublishMetaItemResponseSchema.advisories, riding each element rather than a parallel top-level map). Present ONLY when at least one finding was raised — an empty array is never emitted, so an advisory-free batch's response bytes are unchanged. Advisory by construction: every entry is warning/info, because an error finding refuses the promotion and — the batch being all-or-nothing — aborts the whole batch as failed[] instead; failed[] elements never carry this key.

Nested Shape: PublishPackageDraftsResponse.failed[number]

PropertyTypeRequiredDescription
typestringMetadata type of the item that did not publish.
namestringItem name.
errorstringWhat refused it. On a rollback, the causal item carries its real error and every sibling carries the all-or-nothing explanation. A refusal that produced structured findings states a one-sentence HEADLINE here (what failed, where, which rules, how many); the per-path detail rides issues[] instead of being restated in this string (consumers rendering both channels were showing every finding twice).
codestringoptionalMachine code for the refusal class (SCREAMING_SNAKE, ADR-0112 vocabulary) — e.g. a pre-flight violation code, or BATCH_ABORTED on the non-causal items of a rolled-back batch.
issues{ rule: string; path: string; where: string; message: string; … }[]optionalThe structured findings behind the refusal, when the refusing error carried them — today the author-time gate's INVALID_METADATA refusal on the causal item. The producer has emitted this key; declaring it is what lets a typed consumer read it back, and what lets error stay a headline without losing the per-path detail. Same element shape as published[].advisories and the single-item 422's issues[] (— one dialect, declared once). Present ONLY on the causal item and only when the refusal produced structured findings; BATCH_ABORTED siblings never carry it. Absent means "this refusal carried no structured findings", never "no problems".

Nested Shape: PublishPackageDraftsResponse.seedApplied

PropertyTypeRequiredDescription
successbooleanFalse when the seed rows did not fully land. The publish itself still succeeded — check this rather than assuming data went live.
insertedintegeroptionalRows created by the externalId-keyed upsert. Optional ONLY because the route-level fallback producer (custom protocols that do not self-apply) reports early failures without counters; the in-batch producer always emits both counters.
updatedintegeroptionalRows updated by the externalId-keyed upsert. Same optionality rationale as inserted.
errorstringoptionalSingle failure message, present when the apply failed before the loader ran (including "no readable seed bodies"). When the failure is the seed bodies' own schema refusal, this is a one-sentence headline and the per-path detail rides issues[].
errorsany[]optionalPer-record failures reported by the seed loader, plus any seed-body read failures. May be present and empty on a clean load.
issues{ path: string; message: string; code?: string }[]optionalStructured spec-validation findings behind error, present when the apply was refused by the seed bodies' own schema — the declared 422 seedRequestValidationError mints. The per-path detail lives HERE, once; error stays a one-sentence headline. Absent on non-validation failures (driver faults, unreadable bodies), whose whole story is error / errors[].

Nested Shape: PublishPackageDraftsResponse.materializeApplied

PropertyTypeRequiredDescription
successbooleanFalse when any item's materializer failed; the publish still succeeded.
insertedintegerData-plane rows created across the batch.
updatedintegerData-plane rows updated across the batch.
failures{ type: string; name: string; error: string }[]Each item whose data-plane projection did NOT land — named per-item, unlike the single-item door's scalar error, because one aggregate boolean over N items would hide WHICH ones never went live.

RealtimeConnectRequest

Properties

PropertyTypeRequiredDescription
transportEnum<'websocket' | 'sse' | 'polling'>optionalPreferred transport protocol
channelsstring[]optionalChannels to subscribe to on connect
tokenstringoptionalAuthentication token

RealtimeConnectResponse

Properties

PropertyTypeRequiredDescription
connectionIdstringUnique connection identifier
transportEnum<'websocket' | 'sse' | 'polling'>Negotiated transport protocol
urlstringoptionalWebSocket/SSE endpoint URL

RealtimeDisconnectRequest

Properties

PropertyTypeRequiredDescription
connectionIdstringoptionalConnection ID to disconnect

RealtimeDisconnectResponse

Properties

PropertyTypeRequiredDescription
successbooleanWhether disconnection succeeded

RealtimeSubscribeRequest

Properties

PropertyTypeRequiredDescription
channelstringChannel name to subscribe to
eventsstring[]optionalSpecific event types to listen for
filterRecord<string, any>optionalEvent filter criteria

RealtimeSubscribeResponse

Properties

PropertyTypeRequiredDescription
subscriptionIdstringUnique subscription identifier
channelstringSubscribed channel name

RealtimeUnsubscribeRequest

Properties

PropertyTypeRequiredDescription
subscriptionIdstringSubscription ID to cancel

RealtimeUnsubscribeResponse

Properties

PropertyTypeRequiredDescription
successbooleanWhether unsubscription succeeded

RegisterDeviceRequest

Properties

PropertyTypeRequiredDescription
tokenstringDevice push notification token
platformEnum<'ios' | 'android' | 'web'>Device platform
deviceIdstringoptionalUnique device identifier
namestringoptionalDevice friendly name

RegisterDeviceResponse

Properties

PropertyTypeRequiredDescription
deviceIdstringRegistered device ID
successbooleanWhether registration succeeded

RejectAiPendingActionResponse

Properties

PropertyTypeRequiredDescription
status'rejected'Always "rejected"
idstringThe rejected action id

RollbackMetaItemResponse

Properties

PropertyTypeRequiredDescription
successbooleanWhether the rollback landed.
versionstringThe new live row's ADR-0008 optimistic-concurrency token — the same carrier saveItem returns; pass it back as options.ifMatch.
seqnumberThe new live row's history sequence number.
restoredFromVersionnumberWhich history version was restored.
messagestringoptionalRollback note, when one was recorded.

RuntimeAuthoringIssue

Properties

PropertyTypeRequiredDescription
rulestringStable diagnostic rule id (flow-multi-write-unfiltered, approval-expression-invalid, …). Machine-readable and stable across releases — the key a renderer groups or suppresses by.
pathstringConfig path inside the SUBMITTED body (flows[0].nodes[1].config.multi), so an editor can jump to the offending key. May be empty when the finding is about the item as a whole. For the collection-resident write types (object / permission / book) the TOP-LEVEL collection entry is keyed by NAME (objects.acme_invoice.sharingModel), never by an array index — the gate evaluates against a private per-write snapshot whose indexes no caller can resolve. Every other write type is the sole member of its own collection, so its [0] is trivially stable and stays positional (flows[0]...), as do nested positions inside one named item (objects.acme_invoice.indexes[1]), which index the author's own document.
wherestringHuman-readable location — flow "leave_approval" · node "approve". Prose for a person; use path for anything mechanical.
messagestringWhat is wrong, in the rule author's own words.
hintstringHow to fix it.
severityEnum<'error' | 'warning' | 'info'>How the gate treated this finding. error means the write was REFUSED (these appear on the 422, never on a 2xx); warning / info are advisory — the write succeeded and the finding is FYI.

SaveMetaItemRequest

Properties

PropertyTypeRequiredDescription
typestringMetadata type name
namestringItem name — lowercase snake_case segments, optionally dot-qualified (crm_lead, crm_lead.pipeline). Slash-compound names are refused at the publish door.
itemanyMetadata item definition
organizationIdstringoptionalOrganization (tenant) scope for the write. Load-bearing, not advisory: it selects the overlay partition (ADR-0005) the row lands in — an org-scoped save writes that tenant's own overlay, while an org-less save writes the environment-wide row every tenant reads — and it is the scope stamped on the write's audit row. An org-scoped write of a type whose registry entry declares allowOrgOverride: false is refused (403). Absent = environment-wide.
parentVersionstring | nulloptionalADR-0008 optimistic-concurrency pin: the version token the caller believes is current (on the REST door, the If-Match request header). Present as a string, a concurrent edit is reported as a 409 conflict instead of silently overwritten. ⚠️ null is NOT the same as absent: a present null asserts "no current row of this lifecycle" — the first-write pin, refused 409 when a row already exists — while an ABSENT key is unpinned: the implementation adopts the current row's hash as the parent (last-write-wins). Nullable because that is the implementation's parameter type, and unlike the reset twin (which folds a present null back to the current hash) this verb passes null through to the repository's conflict check unchanged.
actorstringoptionalIdentity recorded on the write's history event (recorded_by, a lookup into sys_user) and audit row. On the REST door this is the request's authenticated identity (one producer) — never a caller-supplied header. Absent, the event is recorded actor-less (null), deliberately not attributed to "system".
forcebooleanoptionalDestructive-change acknowledgement (?force=true on the REST door): skips the safety diff that refuses an object save whose body drops fields or narrows types the stored item still carries (409 with the findings otherwise). Only object saves reach that diff, so the flag is inert for every other type. Absent = the guard runs.
modeEnum<'draft' | 'publish'>optionalPer-item lifecycle (ADR-0005 drafts): draft stages the body as a pending draft overlay (?mode=draft on the REST door; the publish door promotes it later); publish or ABSENT writes straight to the live active row — the legacy default, kept so callers that predate the draft/publish split keep working. Any value other than draft is read as publish.
packageIdstring | nulloptionalADR-0048 — the software package to bind the saved row to (sys_metadata.package_id; ?package=<id> on the REST door, sent only when it names a real package). Set when authoring inside a Studio package workspace; a named read-only base package is refused. On create the row is stamped with this id; on update an existing binding is preserved, never silently re-bound. Absent = env-local overlay (no package stamp); it also scopes which row the unpinned parent-version resolution reads.
writeFaceEnum<'package-duplicate' | 'meta-envelope' | 'meta-dispatch'>optionalWhich write door a refusal is being rendered FOR — stated by the SERVER, never by a remote caller: every door builds this request field by field and never spreads a request body into it, so there is no path for a client to smuggle a face in, and a face arriving in a wire body is simply never read. Two refusals branch on it, for different questions: the 409 destructive-change remedy names the acknowledgement mechanism that actually exists on the refusing door (?force=true on the REST doors; the dispatcher and the duplicate-package door have none), and the 422 invalid-metadata message adapts to whether a structured issues[] channel reaches the consumer beside it. Absent = the conservative default wording.

SaveMetaItemResponse

Properties

PropertyTypeRequiredDescription
successboolean
versionstringContent hash of the just-committed body, and the token the ADR-0008 optimistic-concurrency chain runs on: send it back as the If-Match request header on the next write to that item and a concurrent edit is reported as 409 metadata_conflict instead of silently overwritten. Opaque to callers — echo it verbatim, never parse it. Currently emitted as sha256:<64 hex chars>, but the format is not part of this contract.
seqintegerMonotonic sequence number of the metadata event this write appended to the item history (sys_metadata_history.event_seq). Orders writes; unlike version it is not an OCC token.
stateEnum<'draft' | 'active'>Lifecycle the body was written into: "draft" when the request asked for draft mode (?mode=draft), otherwise "active" (published and live). A draft is staged only — it is not served to the runtime until published.
projectionApplied{ success: boolean; error?: string }optionalOutcome of the awaited ADR-0094 mutation projector — the post-persist step that materializes this metadata into its derived data-plane read model (e.g. permissionsys_permission_set). Present ONLY when a projector is registered for this metadata type, which is why it is optional: its absence means "no projector ran", never "the projection failed". Best-effort by design — a projector failure is reported here and logged, never thrown, so a caller that needs the read model to be live must check projectionApplied.success rather than rely on the 200.
advisories{ rule: string; path: string; where: string; message: string; … }[]optionalNon-gating findings from the runtime authoring gate — the same shared author-time rules os validate / os build / os lint run, applied to this body on its way to active. The write SUCCEEDED; these are what the gate has to say about it anyway (closing D3). Present ONLY when at least one advisory was raised — an empty array is never emitted, so a clean save's response bytes are unchanged and absence means "nothing to report", never "the gate did not run". Advisory by construction: every entry has severity warning or info, because an error finding refuses the write and arrives as the 422 invalid_metadata envelope instead of here. A caller that ignores this key behaves exactly as before. Runtime-only: the CLI surfaces the same findings on its own stdout, and a Studio / MCP / AI author has no CLI at all, which is the gap this key exists to close. The gate runs on both write doors (D1), and both report: POST /meta/:type/:name/publish carries the same key on PublishMetaItemResponseSchema.
messagestringoptional

Nested Shape: SaveMetaItemResponse.projectionApplied

PropertyTypeRequiredDescription
successbooleanFalse when the projector threw; the metadata write itself still succeeded.
errorstringoptionalProjector failure message, present only when success is false.

Nested Shape: SaveMetaItemResponse.advisories[number]

PropertyTypeRequiredDescription
rulestringStable diagnostic rule id (flow-multi-write-unfiltered, approval-expression-invalid, …). Machine-readable and stable across releases — the key a renderer groups or suppresses by.
pathstringConfig path inside the SUBMITTED body (flows[0].nodes[1].config.multi), so an editor can jump to the offending key. May be empty when the finding is about the item as a whole. For the collection-resident write types (object / permission / book) the TOP-LEVEL collection entry is keyed by NAME (objects.acme_invoice.sharingModel), never by an array index — the gate evaluates against a private per-write snapshot whose indexes no caller can resolve. Every other write type is the sole member of its own collection, so its [0] is trivially stable and stays positional (flows[0]...), as do nested positions inside one named item (objects.acme_invoice.indexes[1]), which index the author's own document.
wherestringHuman-readable location — flow "leave_approval" · node "approve". Prose for a person; use path for anything mechanical.
messagestringWhat is wrong, in the rule author's own words.
hintstringHow to fix it.
severityEnum<'error' | 'warning' | 'info'>How the gate treated this finding. error means the write was REFUSED (these appear on the 422, never on a 2xx); warning / info are advisory — the write succeeded and the finding is FYI.

SearchAllHit

Properties

PropertyTypeRequiredDescription
objectstringName of the object the hit belongs to.
idstringID of the matched record.
titlestringDisplay title for the hit, resolved in order: the object's titleFormat template → the declared primary-title pointer (nameField, ADR-0079; deprecated alias displayNameField still honored) → conventional name fields → first/last name → the record ID as a string.
snippetstringoptionalExcerpt cut around the first matched term in a searchable text column, ellipsized at both ends when truncated. ABSENT when no source column literally contains a term (e.g. a pinyin companion match) — absence is a correct answer, not a miss.
recordRecord<string, any>The matched record as the engine's find path returns it (row-level security applied, internal fields already stripped). Object-specific — no cross-object field shape is promised beyond "a record of the named object".

SearchAllResponse

Properties

PropertyTypeRequiredDescription
querystringThe TRIMMED query text the sweep ran with — empty string when the request carried none (the no-scan short-circuit).
hits{ object: string; id: string; title: string; snippet?: string; … }[]Matched records across objects, in scan order, capped at the overall limit (default 20, max 100) with at most perObject (default 5, max 25) per object.
totalObjectsnumberNumber of objects the sweep actually SCANNED (searchable, API-enabled, with a resolvable search-field set) — not the number of objects with hits. An object whose table was never provisioned is skipped and not counted.
totalHitsnumberNumber of hits returned — equals hits.length. NOT a deployment-wide total-match count: matches beyond limit / perObject are not counted.
truncatedbooleanTrue when the sweep stopped at the overall limit — more matches may exist beyond the returned set.

Nested Shape: SearchAllResponse.hits[number]

PropertyTypeRequiredDescription
objectstringName of the object the hit belongs to.
idstringID of the matched record.
titlestringDisplay title for the hit, resolved in order: the object's titleFormat template → the declared primary-title pointer (nameField, ADR-0079; deprecated alias displayNameField still honored) → conventional name fields → first/last name → the record ID as a string.
snippetstringoptionalExcerpt cut around the first matched term in a searchable text column, ellipsized at both ends when truncated. ABSENT when no source column literally contains a term (e.g. a pinyin companion match) — absence is a correct answer, not a miss.
recordRecord<string, any>The matched record as the engine's find path returns it (row-level security applied, internal fields already stripped). Object-specific — no cross-object field shape is promised beyond "a record of the named object".

SetPresenceRequest

Properties

PropertyTypeRequiredDescription
channelstringChannel to set presence in
state{ userId: string; status: Enum<'online' | 'away' | 'busy' | 'offline'>; lastSeen: string; metadata?: Record<string, any> }Presence state to set

Nested Shape: SetPresenceRequest.state

PropertyTypeRequiredDescription
userIdstringUser identifier
statusEnum<'online' | 'away' | 'busy' | 'offline'>Current presence status
lastSeenstringISO 8601 datetime of last activity
metadataRecord<string, any>optionalCustom presence data (e.g., current page, custom status)

SetPresenceResponse

Properties

PropertyTypeRequiredDescription
successbooleanWhether presence was set

UninstallPackageRequest

Uninstall package request

Properties

PropertyTypeRequiredDescription
idstringPackage ID to uninstall

UninstallPackageResponse

Uninstall package response

Properties

PropertyTypeRequiredDescription
idstringUninstalled package ID
successbooleanWhether uninstall succeeded
messagestringoptionalUninstall status message

UnregisterDeviceRequest

Properties

PropertyTypeRequiredDescription
deviceIdstringDevice ID to unregister

UnregisterDeviceResponse

Properties

PropertyTypeRequiredDescription
successbooleanWhether unregistration succeeded

UpdateAiConversationRequest

Properties

PropertyTypeRequiredDescription
titlestringoptionalNew title
metadataRecord<string, any>optionalNew metadata

UpdateDataRequest

Properties

PropertyTypeRequiredDescription
objectstringThe object name.
idstringThe ID of the record to update.
dataRecord<string, any>The fields to update (partial update).
expectedVersionstringoptionalOptimistic concurrency token (typically the updated_at value the client read). When provided, the server compares it against the current record version and returns 409 CONCURRENT_UPDATE if they differ. Optional — omit to skip the check.

UpdateDataResponse

Properties

PropertyTypeRequiredDescription
objectstringObject name
idstringUpdated record ID
recordRecord<string, any>Updated record
droppedFields{ object: string; fields: string[]; reason: Enum<'readonly' | 'readonly_when' | 'primary_key'> }[]optionalWrite-observability: caller-supplied fields the engine LEGALLY stripped from the write before persisting — static readonly or a TRUE readonlyWhen predicate. Present ONLY when ≥1 field was dropped; the update still succeeded without them (status/success semantics unchanged — stripping is legitimate, not an error). REST additionally surfaces this as the X-ObjectStack-Dropped-Fields response header. Optional — omit-when-empty keeps the shape backward-compatible for existing clients that only read record.

Nested Shape: UpdateDataResponse.droppedFields[number]

A write-path strip event: caller-supplied fields legally dropped from the payload

PropertyTypeRequiredDescription
objectstringObject the write targeted (resolved object name)
fieldsstring[]Caller-supplied field names the engine removed from the write payload
reasonEnum<'readonly' | 'readonly_when' | 'primary_key'>Why the fields were dropped: static readonly, a TRUE readonlyWhen predicate, or the primary-key strip of a payload id the engine ruled is not an identifier

UpdateManyDataRequest

Properties

PropertyTypeRequiredDescription
records{ id: string; data: Record<string, any> }[]Array of records to update (server caps the count — see batch.maxBatchSize)
options{ atomic: boolean; returnRecords: boolean; continueOnError: boolean }optionalUpdate options
objectstringObject name

Nested Shape: UpdateManyDataRequest.records[number]

PropertyTypeRequiredDescription
idstringRecord ID
dataRecord<string, any>Fields to update

Nested Shape: UpdateManyDataRequest.options

PropertyTypeRequiredDescription
atomicbooleanoptional (default: false)Opt-in all-or-nothing. When explicitly true the whole batch runs inside ONE engine transaction: the first failure rolls back every prior write, and the response reports zero successes — each row carries errors[0].code ROLLED_BACK (written, then undone), the causal row its own error, and rows never reached NOT_ATTEMPTED. A runtime that cannot roll back REFUSES the request (501 NOT_IMPLEMENTED) rather than silently degrading to best-effort — probe capabilities.transactionalBatch on /discovery first. Takes precedence over continueOnError. Default false: sequential best-effort.
returnRecordsbooleanoptional (default: false)If true, return full record data in response
continueOnErrorbooleanoptional (default: false)If true (and atomic=false), continue processing remaining records after errors. Default false: the first failure ENDS the run — records before it stay written (nothing is rolled back on this arm), and every record after it is reported errors[0].code NOT_ATTEMPTED rather than omitted, so results always covers all total records and succeeded + failed === total.
validateOnlyneveroptional[REMOVED] options.validateOnly was removed from BatchOptions in @objectstack/spec 17.0.0. It was never implemented: the batch surfaces persisted regardless, so a "dry-run" would have silently executed. There is no dry-run today — drop the key. If you need to preview a batch without writing, open an issue so it can be designed (no-commit cascade / constraint semantics) and reintroduced as a flag that actually holds.

UpdateManyDataResponse

Properties

PropertyTypeRequiredDescription
successbooleanOperation success status
error{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; declaredCode?: string; message: string; userMessage?: string; … }optionalError details if success is false
meta{ timestamp: string; duration?: number; requestId?: string; traceId?: string }optionalResponse metadata
operationEnum<'create' | 'update' | 'upsert' | 'delete'>optionalOperation type that was performed
totalnumberTotal number of records in the batch
succeedednumberNumber of records that succeeded
failednumberNumber of records that failed
results{ id?: string; success: boolean; errors?: object[]; data?: Record<string, any>; … }[]Detailed results for each record

Nested Shape: UpdateManyDataResponse.error

PropertyTypeRequiredDescription
codeEnum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)
declaredCodestringoptionalThe producer-declared code, verbatim, when it is not a member of the closed code vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112)
messagestringReadable error message
userMessagestringoptionalProducer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces message.
categorystringoptionalError category (e.g. validation, authorization)
httpStatusintegeroptionalHTTP status of the response carrying this error
detailsanyoptionalAdditional error context (e.g. field validation errors)
requestIdstringoptionalRequest ID for tracking

Nested Shape: UpdateManyDataResponse.results[number]

PropertyTypeRequiredDescription
idstringoptionalRecord ID if operation succeeded
successbooleanWhether this record was processed successfully
errors{ code: Enum<'VALIDATION_ERROR' | 'INVALID_FIELD' | 'MISSING_REQUIRED_FIELD' | …>; declaredCode?: string; message: string; userMessage?: string; … }[]optionalArray of errors if operation failed. Branch on errors[0].code — an atomic batch that rolled back marks rows that were written then undone with code ROLLED_BACK and rows never reached with NOT_ATTEMPTED, while the causal row keeps its own error. A NON-atomic batch that stopped (the continueOnError: false default) marks its un-attempted tail with the same NOT_ATTEMPTED code — rows before the failure stay written and keep reporting success, since nothing was rolled back.
dataRecord<string, any>optionalFull record data (if returnRecords=true)
indexnumberoptionalIndex of the record in the request array
droppedFields{ object: string; fields: string[]; reason: Enum<'readonly' | 'readonly_when' | 'primary_key'> }[]optionalWrite-observability: caller-supplied fields LEGALLY stripped from THIS row before it was written — static readonly / TRUE readonlyWhen on update, or the create-ingress strip. Per-row because a batch can drop different fields on different rows (readonlyWhen is record-state-dependent). Present ONLY when ≥1 field was dropped for this row; the row still succeeded (success unchanged). A single response header cannot express per-row drops, so this body field is the canonical bulk channel — REST does not emit X-ObjectStack-Dropped-Fields for batches. Optional — omit-when-empty keeps the shape backward-compatible.

UpdateNotificationPreferencesRequest

Properties

PropertyTypeRequiredDescription
preferences{ email?: boolean; push?: boolean; inApp?: boolean; digest?: Enum<'none' | 'daily' | 'weekly'>; … }Preferences to update

Nested Shape: UpdateNotificationPreferencesRequest.preferences

PropertyTypeRequiredDescription
emailbooleanoptional (default: true)Receive email notifications
pushbooleanoptional (default: true)Receive push notifications
inAppbooleanoptional (default: true)Receive in-app notifications
digestEnum<'none' | 'daily' | 'weekly'>optional (default: "none")Email digest frequency
channelsRecord<string, { enabled: boolean; email?: boolean; push?: boolean }>optionalPer-channel notification preferences

UpdateNotificationPreferencesResponse

Properties

PropertyTypeRequiredDescription
preferences{ email: boolean; push: boolean; inApp: boolean; digest: Enum<'none' | 'daily' | 'weekly'>; … }Updated notification preferences

Nested Shape: UpdateNotificationPreferencesResponse.preferences

PropertyTypeRequiredDescription
emailbooleanoptional (default: true)Receive email notifications
pushbooleanoptional (default: true)Receive push notifications
inAppbooleanoptional (default: true)Receive in-app notifications
digestEnum<'none' | 'daily' | 'weekly'>optional (default: "none")Email digest frequency
channelsRecord<string, { enabled: boolean; email?: boolean; push?: boolean }>optionalPer-channel notification preferences

ValidateDataIssue

Properties

PropertyTypeRequiredDescription
fieldstringThe field the finding is about (_record for an object-level rule).
codestringMachine-readable finding code, e.g. required, invalid_type, rule_violation.
messagestringHuman-readable message — a validation rule's author-written text where one exists.

ValidateDataRequest

Properties

PropertyTypeRequiredDescription
objectstringThe object name.
dataRecord<string, any> | Record<string, any>[]A candidate record, or an array of them. Nothing is persisted.
modeEnum<'insert' | 'update'>optionalWhich write the verdict should predict. insert (default) walks every declared field, so a missing required field is a finding; update judges only the supplied keys, matching a PATCH.

ValidateDataResponse

Properties

PropertyTypeRequiredDescription
objectstringThe object name.
modeEnum<'insert' | 'update'>The write mode the verdict was reached for.
validbooleanTrue when EVERY row is valid — the whole-set answer.
results{ valid: boolean; errors: object[]; warnings: object[] }[]Per-row verdicts, in submission order.
posture{ valueShapeStrict: boolean; mediaValueShapeStrict: boolean }The ADR-0104 posture the verdict was reached under — reported because it is the difference between "this row is fine" and "this row is fine HERE". The same row can be an error on a self-certified deployment and an admitted warning on an un-migrated one, and a caller explaining a verdict needs to know which it got. An unconditionally-strict preview was considered and rejected (option B): it would fail rows on every un-migrated deployment that the write would have accepted.

Nested Shape: ValidateDataResponse.results[number]

PropertyTypeRequiredDescription
validbooleanTrue when this row would be accepted by the write path.
errors{ field: string; code: string; message: string }[]Findings that would REJECT this row. Empty when valid.
warnings{ field: string; code: string; message: string }[]Findings the target deployment ADMITS rather than rejects — today, ADR-0104 value shapes under a warn-first posture. The row is valid; the write would store it and log the same complaint.

Nested Shape: ValidateDataResponse.posture

PropertyTypeRequiredDescription
valueShapeStrictbooleanTrue when this deployment rejects non-conforming value shapes (ADR-0104 self-certified).
mediaValueShapeStrictbooleanThe same, for media field value shapes.

On this page

TypeScript UsageAiAgentCapabilitiesPropertiesAiAgentChatRequestPropertiesNested Shape: AiAgentChatRequest.messages[number]AiAgentSummaryPropertiesNested Shape: AiAgentSummary.capabilitiesAiAgentsResponsePropertiesNested Shape: AiAgentsResponse.agents[number]AiChatRequestPropertiesNested Shape: AiChatRequest.messages[number]AiChatResponsePropertiesNested Shape: AiChatResponse.usageAiCompleteRequestPropertiesAiConversationPropertiesNested Shape: AiConversation.messages[number]AiMessagePropertiesAiModelsResponsePropertiesNested Shape: AiModelsResponse.models[number]AiPendingActionPropertiesAiPendingActionStatusAllowed ValuesAiStreamChunkPropertiesApproveAiPendingActionResponsePropertiesAuditMetaItemRequestPropertiesAuditMetaItemResponsePropertiesNested Shape: AuditMetaItemResponse.events[number]AutomationActionsResponsePropertiesNested Shape: AutomationActionsResponse.actions[number]AutomationTriggerRequestPropertiesAutomationTriggerResponsePropertiesBatchDataRequestPropertiesNested Shape: BatchDataRequest.requestBatchDataResponsePropertiesNested Shape: BatchDataResponse.errorNested Shape: BatchDataResponse.results[number]CheckPermissionRequestPropertiesCheckPermissionResponsePropertiesCloneDataResponsePropertiesCreateAiConversationRequestPropertiesCreateDataRequestPropertiesCreateDataResponsePropertiesNested Shape: CreateDataResponse.droppedFields[number]CreateManyDataRequestPropertiesCreateManyDataResponsePropertiesNested Shape: CreateManyDataResponse.droppedFields[number]DeleteDataRequestPropertiesDeleteDataResponsePropertiesDeleteManyDataRequestPropertiesNested Shape: DeleteManyDataRequest.optionsDeleteManyDataResponsePropertiesNested Shape: DeleteManyDataResponse.errorNested Shape: DeleteManyDataResponse.results[number]DeleteMetaItemRequestPropertiesDeleteMetaItemResponsePropertiesNested Shape: DeleteMetaItemResponse.projectionAppliedDiffMetaItemResponsePropertiesNested Shape: DiffMetaItemResponse.added[number]Nested Shape: DiffMetaItemResponse.removed[number]Nested Shape: DiffMetaItemResponse.changed[number]DisablePackageRequestPropertiesDisablePackageResponsePropertiesNested Shape: DisablePackageResponse.packageEnablePackageRequestPropertiesEnablePackageResponsePropertiesNested Shape: EnablePackageResponse.packageFindDataRequestPropertiesNested Shape: FindDataRequest.queryFindDataResponsePropertiesFindReferencesToMetaResponsePropertiesNested Shape: FindReferencesToMetaResponse.references[number]GetDataRequestPropertiesGetDataResponsePropertiesGetDiscoveryRequestPropertiesGetDiscoveryResponsePropertiesNested Shape: GetDiscoveryResponse.routesNested Shape: GetDiscoveryResponse.services[string]Nested Shape: GetDiscoveryResponse.capabilitiesNested Shape: GetDiscoveryResponse.schemaDiscoveryNested Shape: GetDiscoveryResponse.scopingGetEffectivePermissionsRequestPropertiesGetEffectivePermissionsResponsePropertiesNested Shape: GetEffectivePermissionsResponse.objects[string]GetFieldLabelsRequestPropertiesGetFieldLabelsResponsePropertiesNested Shape: GetFieldLabelsResponse.labels[string]GetLocalesRequestPropertiesGetLocalesResponsePropertiesNested Shape: GetLocalesResponse.locales[number]GetMetaDiagnosticsResponsePropertiesNested Shape: GetMetaDiagnosticsResponse.entries[number]Nested Shape: GetMetaDiagnosticsResponse.stats[string]GetMetaItemCachedRequestPropertiesNested Shape: GetMetaItemCachedRequest.cacheRequestGetMetaItemCachedResponsePropertiesNested Shape: GetMetaItemCachedResponse.etagNested Shape: GetMetaItemCachedResponse.cacheControlGetMetaItemLayeredRequestPropertiesGetMetaItemLayeredResponsePropertiesNested Shape: GetMetaItemLayeredResponse._diagnosticsGetMetaItemRequestPropertiesGetMetaItemResponsePropertiesNested Shape: GetMetaItemResponse.sortabilityGetMetaItemsRequestPropertiesGetMetaItemsResponsePropertiesGetMetaTypesRequestPropertiesGetMetaTypesResponsePropertiesNested Shape: GetMetaTypesResponse.entries[number]GetNotificationPreferencesRequestPropertiesGetNotificationPreferencesResponsePropertiesNested Shape: GetNotificationPreferencesResponse.preferencesGetObjectPermissionsRequestPropertiesGetObjectPermissionsResponsePropertiesNested Shape: GetObjectPermissionsResponse.permissionsNested Shape: GetObjectPermissionsResponse.fieldPermissions[string]GetPackageRequestPropertiesGetPackageResponsePropertiesNested Shape: GetPackageResponse.packageGetPresenceRequestPropertiesGetPresenceResponsePropertiesNested Shape: GetPresenceResponse.members[number]GetPublishedMetaItemResponseGetTranslationsRequestPropertiesGetTranslationsResponsePropertiesNested Shape: GetTranslationsResponse.translationsGetUiViewRequestPropertiesGetUiViewResponsePropertiesNested Shape: GetUiViewResponse.listNested Shape: GetUiViewResponse.formNested Shape: GetUiViewResponse.listViews[string]Nested Shape: GetUiViewResponse.formViews[string]Nested Shape: GetUiViewResponse.protectionHistoryMetaItemRequestPropertiesHistoryMetaItemResponsePropertiesNested Shape: HistoryMetaItemResponse.events[number]HttpFindQueryParamsPropertiesInstallPackageRequestPropertiesNested Shape: InstallPackageRequest.manifestInstallPackageResponsePropertiesNested Shape: InstallPackageResponse.packageNested Shape: InstallPackageResponse.dependencyResolutionListAiConversationsRequestPropertiesListAiConversationsResponsePropertiesNested Shape: ListAiConversationsResponse.conversations[number]ListAiPendingActionsRequestPropertiesListAiPendingActionsResponsePropertiesNested Shape: ListAiPendingActionsResponse.items[number]ListDraftsResponsePropertiesNested Shape: ListDraftsResponse.drafts[number]ListNotificationsRequestPropertiesListNotificationsResponsePropertiesNested Shape: ListNotificationsResponse.notifications[number]ListPackagesRequestPropertiesListPackagesResponsePropertiesNested Shape: ListPackagesResponse.packages[number]MarkAllNotificationsReadRequestPropertiesMarkAllNotificationsReadResponsePropertiesMarkNotificationsReadRequestPropertiesMarkNotificationsReadResponsePropertiesNotificationPropertiesNotificationPreferencesPropertiesNested Shape: NotificationPreferences.channels[string]PublishMetaItemRequestPropertiesPublishMetaItemResponsePropertiesNested Shape: PublishMetaItemResponse.seedAppliedNested Shape: PublishMetaItemResponse.materializeAppliedNested Shape: PublishMetaItemResponse.projectionAppliedNested Shape: PublishMetaItemResponse.advisories[number]PublishPackageDraftsResponsePropertiesNested Shape: PublishPackageDraftsResponse.published[number]Nested Shape: PublishPackageDraftsResponse.failed[number]Nested Shape: PublishPackageDraftsResponse.seedAppliedNested Shape: PublishPackageDraftsResponse.materializeAppliedRealtimeConnectRequestPropertiesRealtimeConnectResponsePropertiesRealtimeDisconnectRequestPropertiesRealtimeDisconnectResponsePropertiesRealtimeSubscribeRequestPropertiesRealtimeSubscribeResponsePropertiesRealtimeUnsubscribeRequestPropertiesRealtimeUnsubscribeResponsePropertiesRegisterDeviceRequestPropertiesRegisterDeviceResponsePropertiesRejectAiPendingActionResponsePropertiesRollbackMetaItemResponsePropertiesRuntimeAuthoringIssuePropertiesSaveMetaItemRequestPropertiesSaveMetaItemResponsePropertiesNested Shape: SaveMetaItemResponse.projectionAppliedNested Shape: SaveMetaItemResponse.advisories[number]SearchAllHitPropertiesSearchAllResponsePropertiesNested Shape: SearchAllResponse.hits[number]SetPresenceRequestPropertiesNested Shape: SetPresenceRequest.stateSetPresenceResponsePropertiesUninstallPackageRequestPropertiesUninstallPackageResponsePropertiesUnregisterDeviceRequestPropertiesUnregisterDeviceResponsePropertiesUpdateAiConversationRequestPropertiesUpdateDataRequestPropertiesUpdateDataResponsePropertiesNested Shape: UpdateDataResponse.droppedFields[number]UpdateManyDataRequestPropertiesNested Shape: UpdateManyDataRequest.records[number]Nested Shape: UpdateManyDataRequest.optionsUpdateManyDataResponsePropertiesNested Shape: UpdateManyDataResponse.errorNested Shape: UpdateManyDataResponse.results[number]UpdateNotificationPreferencesRequestPropertiesNested Shape: UpdateNotificationPreferencesRequest.preferencesUpdateNotificationPreferencesResponsePropertiesNested Shape: UpdateNotificationPreferencesResponse.preferencesValidateDataIssuePropertiesValidateDataRequestPropertiesValidateDataResponsePropertiesNested Shape: ValidateDataResponse.results[number]Nested Shape: ValidateDataResponse.posture