Sanity Library Reference Docs
    Preparing search index...
    workflow: {
        abortInstance: (
            rawArgs: Clocked<Telemetered<AbortInstanceArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
        availableActions: (
            rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>,
        ) => Promise<AvailableActionsResult>;
        children: (
            rawArgs: ChildrenArgs & EngineScopeArgs,
        ) => Promise<WorkflowInstance[]>;
        commitEffectOps: (
            rawArgs: Clocked<Telemetered<CommitEffectOpsArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
        completeEffect: (
            rawArgs: Clocked<Telemetered<CompleteEffectArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
        definitionsForDocument: (
            rawArgs: Clocked<DefinitionsForDocumentArgs & EngineScopeArgs>,
        ) => Promise<DeployedDefinition[]>;
        deleteDefinition: (
            rawArgs: Clocked<Telemetered<DeleteDefinitionArgs & EngineScopeArgs>>,
        ) => Promise<DeleteDefinitionResult>;
        deployDefinitions: <
            T extends
                | Record<keyof T & string, unknown>
                | {
                    description?: string;
                    fields?: FieldEntry[];
                    groups?: Group[];
                    initialStage: string;
                    lifecycle?: WorkflowLifecycle;
                    name: string;
                    predicates?: Record<string, string>;
                    roleAliases?: RoleAliases;
                    semantics?: Semantic[];
                    stages: Stage[];
                    start?: StartBlock;
                    title: string;
                } & { [K in string
                | number
                | symbol]?: undefined },
        >(
            rawArgs: Telemetered<DeployDefinitionsArgs<T> & EngineScopeArgs>,
        ) => Promise<DeployDefinitionsResult>;
        diagnose: (
            rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>,
        ) => Promise<DiagnoseResult>;
        editField: (
            rawArgs: Clocked<Telemetered<EditFieldArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
        evaluate: (
            rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>,
        ) => Promise<WorkflowEvaluation>;
        evaluateStart: (
            rawArgs: Clocked<EvaluateStartArgs & EngineScopeArgs>,
        ) => Promise<StartEvaluation>;
        findPendingEffects: (
            rawArgs: FindPendingEffectsArgs & EngineScopeArgs,
        ) => Promise<PendingEffect[]>;
        fireAction: (
            rawArgs: Clocked<Telemetered<FireActionArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
        getInstance: (
            rawArgs: InstanceRefArgs & EngineScopeArgs,
        ) => Promise<WorkflowInstance>;
        guardsForDefinition: (
            rawArgs: GuardsForDefinitionArgs & EngineScopeArgs,
        ) => Promise<MutationGuardDoc[]>;
        guardsForInstance: (
            rawArgs: InstanceRefArgs & EngineScopeArgs,
        ) => Promise<MutationGuardDoc[]>;
        instancesForDocument: (
            rawArgs: InstancesForDocumentArgs & EngineScopeArgs,
        ) => Promise<WorkflowInstance[]>;
        listPendingEffects: (
            rawArgs: InstanceRefArgs & EngineScopeArgs,
        ) => Promise<PendingEffect[]>;
        permissions: {
            fetchGrants: (
                args: {
                    client: {
                        request: <T>(
                            opts: { signal?: AbortSignal; tag?: string; url: string },
                        ) => Promise<T>;
                    };
                    resourcePath: string;
                    signal?: AbortSignal;
                },
            ) => Promise<Grant[]>;
            grantsPermissionOn: (
                args: {
                    document?: { _id?: string; _type?: string; [key: string]: unknown };
                    grants: Grant[];
                    permission: "create" | "manage" | "read" | "update";
                    userId?: string;
                },
            ) => Promise<boolean>;
            matchesFilter: (
                args: {
                    document: { _id?: string; _type?: string; [key: string]: unknown };
                    filter: string;
                    userId?: string;
                },
            ) => Promise<boolean>;
        };
        query: <T = unknown>(rawArgs: QueryArgs & EngineScopeArgs) => Promise<T>;
        queryInScope: <T = unknown>(
            rawArgs: Clocked<QueryInScopeArgs & EngineScopeArgs>,
        ) => Promise<T>;
        resetActivity: (
            rawArgs: Clocked<Telemetered<ResetActivityArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
        setStage: (
            rawArgs: Clocked<Telemetered<SetStageArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
        startInstance: (
            rawArgs: Clocked<Telemetered<StartInstanceArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
        tick: (
            rawArgs: Clocked<Telemetered<OperationArgs & EngineScopeArgs>>,
        ) => Promise<OperationResult>;
    } = ...

    Type declaration

    • abortInstance: (
          rawArgs: Clocked<Telemetered<AbortInstanceArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Admin override — hard-stop an in-flight instance where it stands. No stage move, no transition effects, pending effects cancelled. The abort propagates through affected ancestor instances. Propagated, not cascaded — the instance is terminal, so cascaded is always 0 and ancestor movement is reported on the ancestors, not here. changed: false means the instance was already terminal.

    • availableActions: (
          rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>,
      ) => Promise<AvailableActionsResult>

      The actions an actor could fire on the instance's current stage, each flagged allowed with a structured disabledReason when not, returned with the evaluation they were projected from. Pure read.

    • children: (rawArgs: ChildrenArgs & EngineScopeArgs) => Promise<WorkflowInstance[]>

      Materialised spawned children of a parent instance. Walks history for spawned events — the durable record, and still the one place adoption and orphan events sit alongside spawns — strips each instanceRef's GDR URI to a bare _id, fetches the instances, drops any not visible to this engine's tag, and returns them sorted by startedAt ascending. Pass activity to restrict to a single spawning activity on the parent.

    • commitEffectOps: (
          rawArgs: Clocked<Telemetered<CommitEffectOpsArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Commit mid-dispatch field state from a running effect handler — the engine verb behind ctx.commitOps. Gates on the dispatch's exact claim (token match plus unexpired lease; a stale report throws StaleEffectClaimError and writes nothing), validates and applies the field.* ops through the shared op applier, records history and the mandatory idempotency key, renews the claim's lease in the same compare-and-swap commit, refreshes the stage's guards, then cascades — a report that satisfies a transition moves the instance, by design.

      completeEffect remains the authoritative final result and stays claim-blind; this verb only protects the mid-dispatch write channel from superseded handlers.

    • completeEffect: (
          rawArgs: Clocked<Telemetered<CompleteEffectArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Report a queued effect's outcome. Drains it from pendingEffects and appends an effectHistory entry whose outputs are what downstream bindings and conditions read as $effects['<name>'].<output> — the start-only context bag is never touched. Any ops the handler returned (field.*) are validated and applied in the same commit, through the op applier an action's field ops use. Cascades after. A completion that applies always changes state, so changed is true; a bad effectKey/status throws instead, and a keyed retry of an already-applied completion replays as changed: false.

      Completion is first-writer-wins — see CompleteEffectArgs. A completer reporting over a retrying transport (webhook redelivery, queue, cron) should pass idempotencyKey.

    • definitionsForDocument: (
          rawArgs: Clocked<DefinitionsForDocumentArgs & EngineScopeArgs>,
      ) => Promise<DeployedDefinition[]>

      The startable half of workflow.instancesForDocument: every deployed definition that APPLIES to document — what a start picker for it should offer. Loads the latest deployed version of each definition visible to the engine's tag and filters it through applicableDefinitions: startable ∧ the subject-kind entry accepts the doc's _typestart.filter passes, evaluated in the browse-time-pure start-filter context. Start requirements never participate — readiness is a start-time question; pre-flight it with workflow.evaluateStart.

      Takes the LOADED candidate document, not a ref — applicability evaluates its content under whatever perspective the caller read it with. Surfaces ALL matches (name ascending) with no engine ranking: presenting a picker or auto-picking is consumer policy. Advisory like every engine-side check.

    • deleteDefinition: (
          rawArgs: Clocked<Telemetered<DeleteDefinitionArgs & EngineScopeArgs>>,
      ) => Promise<DeleteDefinitionResult>

      Remove a deployed definition (all versions, or one via version). Refuses while non-terminal instances exist unless cascade aborts them first — instances are never deleted, only aborted in place. The operation also checks spawn referrers and cleans up guard documents.

    • deployDefinitions: <
          T extends
              | Record<keyof T & string, unknown>
              | {
                  description?: string;
                  fields?: FieldEntry[];
                  groups?: Group[];
                  initialStage: string;
                  lifecycle?: WorkflowLifecycle;
                  name: string;
                  predicates?: Record<string, string>;
                  roleAliases?: RoleAliases;
                  semantics?: Semantic[];
                  stages: Stage[];
                  start?: StartBlock;
                  title: string;
              } & { [K in string
              | number
              | symbol]?: undefined },
      >(
          rawArgs: Telemetered<DeployDefinitionsArgs<T> & EngineScopeArgs>,
      ) => Promise<DeployDefinitionsResult>

      Deploy a set of definitions as one call. Definitions are immutable and content-addressed: the author writes no version, identical content no-ops (unchanged), and any change mints the next version (created) — deploy never patches a deployed version out from under the instances pinned to it. The engine orders the batch itself (children before the parents that spawn them). Refs may point inside the batch or at already-deployed definitions; a ref resolving to neither, or a cycle, errors before any write. Input is authored content or a fetched definition document — the document envelope (_* system fields, tag, version, contentHash) is stripped at the boundary and never fingerprinted, so a fetched document redeploys as unchanged; any other unknown key fails loud.

    • diagnose: (rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>) => Promise<DiagnoseResult>

      Diagnose why an instance is or isn't progressing. Projects the instance (the same read as evaluate) and classifies it — terminal, progressing, waiting (an action is available, so healthy), or stuck with a structured cause — returning that verdict as a DiagnoseResult alongside the evaluation it came from, so a consumer can render the supporting evidence without a second projection. Pure read.

    • editField: (
          rawArgs: Clocked<Telemetered<EditFieldArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Edit a declared-editable field directly — reassign, reschedule, claim-by-hand, append to a running log — through the generic edit seam instead of a bespoke action per field. Soft-gates on the field's declared editability (the same projection a UI renders), applies the edit as a field.* op so provenance and history are stamped by the op path, refreshes the stage's guards, then cascades — an edit to a value a transition reads can and should move the instance. Advisory like every engine gate.

      Each call is a discrete COMMIT (a history entry, a guard refresh, a cascade, an ifRevisionId write), not a draft patch — so an inline-field UI must bind it to a deliberate boundary (blur, Enter, Save, debounce), never an onChange per keystroke.

    • evaluate: (
          rawArgs: Clocked<EvaluateArgs & EngineScopeArgs>,
      ) => Promise<WorkflowEvaluation>

      Project the instance from a given actor's perspective — a WorkflowEvaluation with per-action verdicts (allowed plus a structured disabledReason). Used by UIs to render disabled-with-reason buttons and by fireAction to gate writes through the same logic. Pure read; never writes.

    • evaluateStart: (
          rawArgs: Clocked<EvaluateStartArgs & EngineScopeArgs>,
      ) => Promise<StartEvaluation>

      Pre-flight the start gates for a definition plus candidate initialFields — the read startInstance enforces, as a StartEvaluation a surface can render: missingRequired mirrors the input contract, while requirements preserves every declared node's authored descriptor, outcome, and GROQ insight where applicable; allowed / outcome aggregate those ordered results.

      BINDABILITY-AWARE for partial mid-form inputs: when a predicate reads an entry initialFields doesn't supply — including a singleSubject node's implicit subject read — outcome is 'unevaluable' and unboundReads names the entries ("fill these to decide") instead of the collapsed answer GROQ equality would give. That is the ONE deliberate divergence from the gate, where absence is final rather than provisional. A definition declaring no start requirements is vacuously allowed, exactly like the verb. Pure read; advisory under races — the enforcement moment is startInstance itself.

    • findPendingEffects: (rawArgs: FindPendingEffectsArgs & EngineScopeArgs) => Promise<PendingEffect[]>

      Filter the instance's pending effects: claimed on claim presence, names on specific effect names. Both filters compose (AND).

    • fireAction: (
          rawArgs: Clocked<Telemetered<FireActionArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Fire an action against an active activity, then cascade and propagate to ancestors once it commits. The universal "something happened" call — editors fire it, and runtimes fire it in response to webhooks, effect completions, and timer firings; external signals never bypass it. A cascade-fired (when) action is rejected: the cascade is its only firing path.

    • getInstance: (rawArgs: InstanceRefArgs & EngineScopeArgs) => Promise<WorkflowInstance>

      Fetch a workflow instance by id, scoped to the engine's tag. Throws when the instance doesn't exist or isn't visible to this engine.

    • guardsForDefinition: (
          rawArgs: GuardsForDefinitionArgs & EngineScopeArgs,
      ) => Promise<MutationGuardDoc[]>
    • guardsForInstance: (rawArgs: InstanceRefArgs & EngineScopeArgs) => Promise<MutationGuardDoc[]>
    • instancesForDocument: (
          rawArgs: InstancesForDocumentArgs & EngineScopeArgs,
      ) => Promise<WorkflowInstance[]>

      Every in-flight instance whose reactive watch-set includes document — the reverse of an instance's subscription document set. For a content-change-driven runtime holding no instances in memory: a document changed, which instances should it tick? The watch-set covers the instance itself, its ancestors, live spawned children, and the docs named by doc.ref / subject / doc.refs / release.ref entries on the workflow scope AND the current stage, so a hand-rolled GROQ over fields[] gets it subtly wrong. The GROQ filter narrows candidates server-side and instanceWatchesDocument rechecks each result, keeping the reverse in lockstep with the forward set.

      document must be a resource-qualified GDR URI; a bare id is rejected, since it can't be resource-routed and would silently mismatch — an instance watching dataset:A:ds:doc is never matched by a change to dataset:B:ds:doc. Sorted by startedAt ascending.

    • listPendingEffects: (rawArgs: InstanceRefArgs & EngineScopeArgs) => Promise<PendingEffect[]>

      Every pending effect on the instance — the same entries the runtime would see, claimed and unclaimed alike.

    • permissions: {
          fetchGrants: (
              args: {
                  client: {
                      request: <T>(
                          opts: { signal?: AbortSignal; tag?: string; url: string },
                      ) => Promise<T>;
                  };
                  resourcePath: string;
                  signal?: AbortSignal;
              },
          ) => Promise<Grant[]>;
          grantsPermissionOn: (
              args: {
                  document?: { _id?: string; _type?: string; [key: string]: unknown };
                  grants: Grant[];
                  permission: "create" | "manage" | "read" | "update";
                  userId?: string;
              },
          ) => Promise<boolean>;
          matchesFilter: (
              args: {
                  document: { _id?: string; _type?: string; [key: string]: unknown };
                  filter: string;
                  userId?: string;
              },
          ) => Promise<boolean>;
      }

      Permission helpers — Sanity ACL grants evaluated against documents via GROQ, used by workflow.evaluate to soft-gate actions when the caller supplies grants. Deliberately namespace-only, not mirrored on Engine: pure helpers that need none of the engine's pinned scope.

      • fetchGrants: (
            args: {
                client: {
                    request: <T>(
                        opts: { signal?: AbortSignal; tag?: string; url: string },
                    ) => Promise<T>;
                };
                resourcePath: string;
                signal?: AbortSignal;
            },
        ) => Promise<Grant[]>
      • grantsPermissionOn: (
            args: {
                document?: { _id?: string; _type?: string; [key: string]: unknown };
                grants: Grant[];
                permission: "create" | "manage" | "read" | "update";
                userId?: string;
            },
        ) => Promise<boolean>
      • matchesFilter: (
            args: {
                document: { _id?: string; _type?: string; [key: string]: unknown };
                filter: string;
                userId?: string;
            },
        ) => Promise<boolean>
    • query: <T = unknown>(rawArgs: QueryArgs & EngineScopeArgs) => Promise<T>

      Run a caller-supplied GROQ query with the engine's tag bound as $tag. This does NOT rewrite the query — arbitrary GROQ can't be safely tag-scoped after the fact — so the CALLER MUST filter on $tag (e.g. tag == $tag). To guard against accidental cross-partition reads, a query that never references $tag is rejected before it reaches the lake. The caller is responsible for type-narrowing the result.

    • queryInScope: <T = unknown>(
          rawArgs: Clocked<QueryInScopeArgs & EngineScopeArgs>,
      ) => Promise<T>

      Snapshot-aware GROQ — runs against the same in-memory view that filters see for a given instance. Hydrates the instance's snapshot (instance + ancestors + every doc declared by a doc.ref / subject / doc.refs entry in scope), then evaluates the supplied GROQ in groq-js against that dataset. The caller-free rendered scope cascade gates evaluate in is auto-bound — the instance-derived vars with the open stage's overlay merged into $fields, $assigned at its caller-free false, plus the author's pre-evaluated $<predicate> booleans — with ids in GDR URI form to match the snapshot's keying. Pure read; never writes.

    • resetActivity: (
          rawArgs: Clocked<Telemetered<ResetActivityArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Admin override — reset a failed (or otherwise terminal) activity in the instance's current stage. to: 'active' (the default) re-runs it; to: 'skipped' is the bypass that resolves it so a $allActivitiesDone-gated exit can fire. Cascades after the reset, so an unblocked transition fires in the same call. ACL gating is enforced upstream by the caller; this verb performs no permission pre-flight. changed: false means the reset was a no-op (instance terminal, or the activity already at the target status).

    • setStage: (
          rawArgs: Clocked<Telemetered<SetStageArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Admin override — force the instance into targetStage regardless of filters or declared transitions. ACL gating is enforced upstream by the caller; this verb performs no permission pre-flight, only the mechanical move. changed: false means the move was a no-op (already at the target, or terminal).

    • startInstance: (
          rawArgs: Clocked<Telemetered<StartInstanceArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Spawn a new instance from a deployed definition: pins the snapshot, seeds the context bag, enters the initial stage with every in-scope activity born active, then cascades until stable. The gates run before anything is written — supplied rows must be structurally consumable, required inputs must be present, then every declared start requirement is evaluated in author order and all unmet nodes are reported by one StartNotAllowedError (no override arg; pre-flight with evaluateStart). start.filter is NOT evaluated here — that is a read-side visibility rule, see definitionsForDocument.

      Start is three commits — create, prime, first cascade — and a supplied instanceId is the idempotency key across them: reusing it for the SAME start RESUMES (input gates and field resolution are skipped, those values were pinned at create), while reusing it for a DIFFERENT start, or for an unfinished start that was aborted, throws ContractViolationError. changed is true on a fresh start and rev-derived on a resume; cascaded reports how far the instance auto-advanced.

    • tick: (
          rawArgs: Clocked<Telemetered<OperationArgs & EngineScopeArgs>>,
      ) => Promise<OperationResult>

      Run the cascade until stable — triggered actions fire, transitions move. For a runtime reacting to any event that might affect the workflow but isn't itself an action fire: a subject doc was patched, a sibling workflow completed, the clock crossed a deadline a when reads. The caller doesn't need to know what changed — it nudges the affected instance and the engine re-evaluates. changed reports whether the nudge wrote anything, derived from the instance's _rev rather than cascaded alone, since a hop can fire triggered actions without unlocking a transition.