Editorial Workflows

History and audit trail

Understand the durable event history stored on every Editorial Workflows instance, what it records, and where its provenance boundary ends.

This page is for developers and operators who need to inspect what happened to one workflow run. If you have an instance ID, fetch the instance through the engine and read its typed history array. Use the CLI when inspecting a run interactively, and query the Content Lake directly only for reporting across many instances.

Every Editorial Workflows instance carries a durable history: HistoryEntry[]. The engine writes it in the same transaction as the state change, so a successful commit and its audit entries cannot diverge. Rejected commits write neither. Do not patch instance documents directly.

Get the history data

The normal programmatic path is engine.getInstance({instanceId}). It returns the complete WorkflowInstance; read instance.history and narrow each entry by _type.

import type {HistoryEntry} from '@sanity/workflow-engine'

const instance = await engine.getInstance({instanceId})
const history: HistoryEntry[] = instance.history

For reporting across instances, query the workflow resource directly and filter by the engine tag. This is the bulk-read path; getInstance is the safer default for one known instance.

const rows = await workflowClient.fetch(
  `*[_type == "sanity.workflow.instance" && tag == $tag]{
    _id,
    definition,
    currentStage,
    history
  }`,
  {tag: 'production'},
)

What history records

Every entry has _key, _type, and at; every engine-stamped entry may also have executionContext. Event-specific fields are listed in the reference table below.

HistoryEntry is exported from @sanity/workflow-engine. It is a discriminated union: switch on entry._type and TypeScript narrows the available fields.

  • stageEntered

    stage, fromStage?, transition?, via?, reason?, actor?

    A stage becomes current.

  • stageExited

    stage, toStage, transition?, via?, reason?, actor?

    The instance leaves a stage.

  • transitionFired

    fromStage, toStage, transition?, via?, reason?, actor?

    A transition or administrative move changes stages.

  • activityStatusChanged

    stage, activity, from, to, actor?

    An activity changes status.

  • actionFired

    stage, activity, action, actor?, driverKind?, triggered?

    A caller or cascade fires an action.

  • opApplied

    stage, activity?, action?, edit?, effect?, opType, target?, resolved?, actor?

    An operation changes instance state.

  • effectQueued

    effectKey, effect, origin

    An action queues an effect.

  • effectCompleted

    effectKey, effect, status, outputs?, detail?, actor?

    An effect settles.

  • effectClaimReleased

    effectKey, effect, claim, via, actor?

    An expired effect claim is taken over or swept.

  • spawned

    activity, instanceRef, rowKey?, actor?

    A child workflow is created.

  • subworkflowAdopted

    stage, activity, instanceRef, rowKey

    A live child is rebound on stage re-entry.

  • subworkflowResolved

    activity, instanceRef, status

    A child reaches done or aborted.

  • subworkflowOrphaned

    instanceRef, detail

    A terminal child cannot match a parent registry row.

  • fieldQueryDiscarded

    scope, field, detail

    A query-sourced initial value fails its declared shape.

  • aborted

    stage, reason?, actor?

    The instance is hard-stopped.

For interactive inspection, run sanity-workflows show <instanceId> --include history. To stream entries as they are appended, run sanity-workflows tail <instanceId>.

Actor and execution context

Where an actor exists, the engine stamps the identity resolved from the committing client. An actionFired entry also distinguishes caller-fired work from an action fired automatically during a cascade. executionContext records the runtime and the host-declared {kind, id}, answering “through what did this run?” alongside the actor’s “who?”

These stamps are operational provenance, not an authentication boundary. The engine does not cryptographically bind an actor or host-declared execution context to the write token. Sanity’s authenticated document history remains the ground truth for who committed a Content Lake mutation. Use the workflow history to explain workflow semantics; use Content Lake history when authenticated mutation provenance matters.

Resolve an actor to a current project user

Actor stamps preserve durable provenance rather than mutable profile data. Resolve a person when presenting current profile information with engine.resolveActor({actor, projectId}) or resolveClientActor(client, {actor, projectId}). The result distinguishes resolved, missing, inaccessible, and not-person; agent and system actors are never sent to a project-user API. Studio and App SDK integrations expose native project-user directories for the same contract.

History and effect history are different

The instance’s history is the workflow event trail. effectHistory is the result ledger for settled effect runs: status, duration, outputs, and error detail. The two correlate through effectKey. Conditions read declared effect outputs through $effects; they do not use the general history array as mutable workflow state. See Effects and runtimes.

Build an application activity feed

Keep durable events and current work as separate lanes in an application feed. Render history in its stored order for what happened; append or group pendingEffects as live work rather than converting it into audit events.

  • instance.history

    HistoryEntry[]

    Durable, ordered workflow events. Use actionFired for human decisions and effectCompleted for settled automation. Entries remain after the current work changes.

  • Current queued or claimed effect work. These rows disappear when they settle; completion is recorded durably in history and in the detailed effectHistory result ledger.

Retention and deletion

The engine does not prune an instance’s history when the workflow finishes. Deleting a definition version also does not delete its instances: a cascading definition deletion aborts active instances and leaves those instance documents as the audit record. Any separate retention or deletion policy must account for the loss of that record and must not mutate live instances behind the engine.

The audit operation is separate

The authoring audit operation is convenience syntax for appending a timestamped, attributed row to a workflow field such as notes. It is business data defined by your workflow. It does not replace the engine-owned history trail. See Operations.

Next steps

Visiting agent?

Was this page helpful?