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 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.
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.
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)
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
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.
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 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.
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
index
integer
✅
Index of the operation in the request operations array
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.
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
index
integer
✅
Index of the operation in the request operations array
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.