Read a mutation confirmation
Resolve /api/workspace/... against WORKSPACE_API_BASE_URL, the installation
API origin supplied by your administrator, never moltaro.com. Confirm this
route in the answering installation’s Runtime OpenAPI before using it.
GET /api/workspace/entity-mutation-outcomes checks a previously saved operation
handle. It performs no mutation. Save the handle before the first write and
bind it to the installation and signed-in user. A lost handle cannot be
reconstructed from this API.
| Operation | Numeric value | Required parameters besides OperationKey |
|---|---|---|
| Create | 0 | EntityDefinitionId |
| Update | 1 | EntityDefinitionId, EntityInstanceId |
| Archive | 2 | EntityDefinitionId, EntityInstanceId |
| Restore | 3 | EntityDefinitionId, EntityInstanceId |
| Delete | 4 | EntityDefinitionId, EntityInstanceId |
| Composite | 5 | None |
OperationKey is a nonempty UUID. Definition IDs are stable IDs, not mutable keys. Irrelevant IDs and unknown selector parameters are errors. Actor and workspace are server-derived; a key is not an access token.
For example, a Create needs no generated record ID:
GET ${WORKSPACE_API_BASE_URL}/api/workspace/entity-mutation-outcomes?Operation=0&OperationKey=9da1d307-0469-4319-b1ad-f63c7a28a5f5&EntityDefinitionId=definition-idUnderstand the answer
Section titled “Understand the answer”The standard success envelope contains:
{ "Success": true, "Errors": [], "Warnings": [], "Data": { "SchemaVersion": 1, "State": 1, "Receipt": { "CommittedAt": "2026-10-01T12:00:00+00:00", "ExpiresAt": "2026-10-08T12:00:00+00:00" }, "Result": { "Kind": "Single", "EntityDefinitionId": "definition-id", "EntityInstanceId": "record-id", "RowVersion": "c7207704-d0b5-4a67-826a-5d44a5c741d5" } }}State 1 is ConfirmedCommitted. The unexpired receipt confirms the
operation in the selected scope. Result may be null after access loss,
deletion or unreadable stored result data; confirmation remains valid.
Composite returns all original ordered StepId/Kind/definition/record/version
identities, or a null result if any target is now unreadable. No fields,
labels, hidden IDs or hidden step count are returned. A step’s Kind is
Create, Update, Archive or Delete; Archive and Delete steps come from a
native Form save, whose unchanged root is not reported as a step. A Delete
step has a null RowVersion and reports the record identity that was sent; it
is not checked for readability, because the record no longer exists. Treat a
Kind you do not recognize as an unreadable result, never as permission to
write again.
State 0 is NoConfirmedReceipt, with Receipt and Result both null. The
write might still be running, commit later or have an expired receipt.
It never permits an automatic retry with the old or a new key. It does
not mean Failed, Pending, NotApplied or rollback. A failure of this GET is a
check error, not either known outcome.
Versions belong to the original operation, even after later edits. Use the ordinary secured record GET for current data before editing. Its failure does not undo the confirmed commit. Receipt timestamps are server-journalled UTC times, not an exact physical COMMIT time or a retry deadline.
The seven-day retention is unchanged; reads are allowed after expiry but cannot provide indefinite confirmation. Never reuse logical operation keys. Without the body this check does not prove equality to an arbitrary payload or resolve keyReuse. Existing exact-replay behavior is unchanged.
Workspace UI helper
Section titled “Workspace UI helper”The helper takes a typed selector and AbortSignal, makes exactly one safe GET and rejects unknown/malformed states or schemas. It supplies no storage, polling, retries, key generation or record refresh. Storage callbacks below belong to your page and must complete before the first send.
import type { WorkspaceUiEntityMutationHandle } from '@moltaro/workspace-ui'import { MoltaroApiClient, WorkspaceUiEntityMutationOperationEnum as Operation, WorkspaceUiEntityMutationOutcomeStateEnum as State } from '@moltaro/workspace-ui'
const api = new MoltaroApiClient()type SavedHandle = { InstallationId: string, ActorUserId: string, Handle: WorkspaceUiEntityMutationHandle }
export async function firstCreate ( installationId: string, actorUserId: string, definition: { Id: string, Key: string, RowVersion: string }, values: Record<string, unknown>, persist: (saved: SavedHandle) => Promise<void>, signal?: AbortSignal,) { const handle: WorkspaceUiEntityMutationHandle = { Operation: Operation.Create, OperationKey: crypto.randomUUID(), EntityDefinitionId: definition.Id, } await persist({ InstallationId: installationId, ActorUserId: actorUserId, Handle: handle }) return api.requestData<unknown>(`/api/workspace/entity/${encodeURIComponent(definition.Id)}/instances`, { method: 'POST', ...(signal ? { signal } : {}), body: JSON.stringify({ OperationKey: handle.OperationKey, EntityDefinitionKey: definition.Key, ExpectedDefinitionRowVersion: definition.RowVersion, Values: values, ResponsibilityCandidates: [] }), })}
// After reload, supply the saved handle and the current authenticated binding.export async function checkAfterReload (saved: SavedHandle, installationId: string, actorUserId: string, signal?: AbortSignal) { if (saved.InstallationId !== installationId || saved.ActorUserId !== actorUserId) { throw new Error('Handle belongs to another session.') } const outcome = await api.readEntityMutationOutcome(saved.Handle, signal) if (outcome.State === State.ConfirmedCommitted) { return outcome } // Keep the unresolved operation for user investigation. Do not write or generate a new key. return outcome}Workspace UI authoring disallows direct host globals such as window,
localStorage and sessionStorage. Supply caller-owned persistence through a
supported integration, for example ExternalApiClient to your own separately
authenticated and authorized storage service. This helper adds no storage service.
The current-user C# Composite service’s receipts are read through this REST route under the same actor. A system automation receipt does not belong to the interactive initiator. There is no new .NET query gateway.
Regenerate API types from the installation’s OpenAPI. Workspace UI declaration additions change the host fingerprint while keeping HostContractVersion 1: Check -> Build -> Activate -> full reload existing projects. Old artifacts follow the normal incompatible-artifact policy. No receipt migration, retention change or .NET rebuild is required by this addition.
Workspace UI pages can let the host do all of this:
Safe Entity writes
(useEntityMutation) saves the handle in the browser tab before the first
send, checks it once after a reload and never writes again on its own. The
record lifecycle dialog uses the same mechanism. Other built-in forms keep
their existing flow.