You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
returnRecords
boolean
optional (default: false)
If true, return full record data in response
continueOnError
boolean
optional (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.
validateOnly
never
optional
[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.
Array 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.
Write-observability: caller-supplied fields LEGALLY stripped from THIS row before it was written — static readonly / TRUE readonlyWhen on update, or the in-engine static readonly strip on create. 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 say which row dropped what, so this body field stays the canonical per-row channel; REST also sends the X-ObjectStack-Dropped-Fields response header on the answer, naming the union of the rows' drops (each field and reason once). Optional — omit-when-empty keeps the shape backward-compatible.
Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)
declaredCode
string
optional
The 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)
message
string
✅
Readable error message
userMessage
string
optional
Producer-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.
refusal
true
optional
Producer-declared: the 5xx this envelope carries is a deliberate refusal whose message is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose message is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — true is the only value.
category
string
optional
Error category (e.g. validation, authorization)
httpStatus
integer
optional
HTTP status of the response carrying this error
details
any
optional
Additional error context (e.g. field validation errors)
A write-path strip event: caller-supplied fields legally dropped from the payload, or written with the engine's computed value in place of the caller's
Property
Type
Required
Description
object
string
✅
Object the write targeted (resolved object name)
fields
string[]
✅
Caller-supplied field names whose value the engine did not store: removed from the write payload, or (stored_expression) written with the computed value in place of the caller's
Why the fields were dropped: static readonly, a TRUE readonlyWhen predicate, the primary-key strip of a payload id the engine ruled is not an identifier, a computed (formula) field no driver has a column for, or a stored field whose declared expression the engine computes on every write, so the caller's different value was replaced by the computed one
BatchOperationType
Allowed Values
create
update
upsert
delete
BatchOptions
Properties
Property
Type
Required
Description
atomic
boolean
optional (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.
returnRecords
boolean
optional (default: false)
If true, return full record data in response
continueOnError
boolean
optional (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.
validateOnly
never
optional
[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.
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.
returnRecords
boolean
optional (default: false)
If true, return full record data in response
continueOnError
boolean
optional (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.
validateOnly
never
optional
[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.
Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages)
declaredCode
string
optional
The 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)
message
string
✅
Readable error message
userMessage
string
optional
Producer-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.
refusal
true
optional
Producer-declared: the 5xx this envelope carries is a deliberate refusal whose message is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose message is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — true is the only value.
category
string
optional
Error category (e.g. validation, authorization)
httpStatus
integer
optional
HTTP status of the response carrying this error
details
any
optional
Additional error context (e.g. field validation errors)
Array 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.
Write-observability: caller-supplied fields LEGALLY stripped from THIS row before it was written — static readonly / TRUE readonlyWhen on update, or the in-engine static readonly strip on create. 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 say which row dropped what, so this body field stays the canonical per-row channel; REST also sends the X-ObjectStack-Dropped-Fields response header on the answer, naming the union of the rows' drops (each field and reason once). Optional — omit-when-empty keeps the shape backward-compatible.
CrossObjectBatchDroppedFields
A cross-object batch strip event: dropped fields plus the operation index
Properties
Property
Type
Required
Description
object
string
✅
Object the write targeted (resolved object name)
fields
string[]
✅
Caller-supplied field names whose value the engine did not store: removed from the write payload, or (stored_expression) written with the computed value in place of the caller's
Why the fields were dropped: static readonly, a TRUE readonlyWhen predicate, the primary-key strip of a payload id the engine ruled is not an identifier, a computed (formula) field no driver has a column for, or a stored field whose declared expression the engine computes on every write, so the caller's different value was replaced by the computed one
index
integer
✅
Index of the operation in the request operations array
CrossObjectBatchOperation
Properties
Property
Type
Required
Description
object
string
✅
Target object (table) name
action
Enum<'create' | 'update' | 'delete'>
optional (default: "create")
Operation to perform (default: create)
id
string
optional
Target record id — required for update and delete
data
Record<string, any>
optional
Record payload for create/update; a value may be { $ref: <opIndex> } to reference an earlier op's created id
Write-observability: caller-supplied fields the engine LEGALLY stripped from an operation before it was written — static readonly or a TRUE readonlyWhen predicate. This endpoint is the console record form's save path (master-detail writes parent + children in one transaction), so without it the ONE surface where a user edits a readonlyWhen field reported plain success while the value never landed. Each event carries the index of its operation. Present ONLY when ≥1 field was dropped; the batch still committed without them (results/success semantics unchanged). Optional — omit-when-empty keeps the shape backward-compatible.
A cross-object batch strip event: dropped fields plus the operation index
Property
Type
Required
Description
object
string
✅
Object the write targeted (resolved object name)
fields
string[]
✅
Caller-supplied field names whose value the engine did not store: removed from the write payload, or (stored_expression) written with the computed value in place of the caller's
Why the fields were dropped: static readonly, a TRUE readonlyWhen predicate, the primary-key strip of a payload id the engine ruled is not an identifier, a computed (formula) field no driver has a column for, or a stored field whose declared expression the engine computes on every write, so the caller's different value was replaced by the computed one
index
integer
✅
Index of the operation in the request operations array
DeleteManyRequest
Properties
Property
Type
Required
Description
ids
string[]
✅
Array of record IDs to delete (server caps the count — see batch.maxBatchSize)
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.
returnRecords
boolean
optional (default: false)
If true, return full record data in response
continueOnError
boolean
optional (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.
validateOnly
never
optional
[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.
UpdateManyRecord
Properties
Property
Type
Required
Description
id
string
✅
Record ID
data
Record<string, any>
✅
Fields to update
UpdateManyRequest
Properties
Property
Type
Required
Description
records
{ id: string; data: Record<string, any> }[]
✅
Array of records to update (server caps the count — see batch.maxBatchSize)
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.
returnRecords
boolean
optional (default: false)
If true, return full record data in response
continueOnError
boolean
optional (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.
validateOnly
never
optional
[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.