Sanity Library Reference Docs
    Preparing search index...
    interface WorkflowClient {
        action?: (action: any, options?: { tag?: string }) => Promise<any>;
        config?: () => WorkflowClientConfig;
        create: <T extends { _id: string; _type: string }>(
            doc: T,
            options?: WorkflowCommitOptions,
        ) => Promise<T>;
        fetch: <T = unknown>(
            query: string,
            params?: Record<string, unknown>,
            options?: WorkflowFetchOptions,
        ) => Promise<T>;
        getDocument: <T = SanityDocument>(
            id: string,
            options?: { tag?: string },
        ) => Promise<T | null | undefined>;
        patch: (documentId: string) => WorkflowPatch;
        request?: <T>(
            opts: {
                signal?: AbortSignal;
                tag?: string;
                uri?: string;
                url?: string;
            },
        ) => Promise<T>;
        transaction: () => WorkflowTransaction;
        withConfig?: (config: WorkflowClientConfig) => this;
    }
    Index

    Properties

    action?: (action: any, options?: { tag?: string }) => Promise<any>

    Optional — present on the real @sanity/client and on the test fake (from @sanity-labs/client-fake-for-test@0.2.0+). Effect handlers use it to dispatch Content Releases and version actions (sanity.action.release.publish, sanity.action.document.version.create, etc.) without depending on the broader client surface.

    Typed loosely so both the strict real-client Action union and the test fake's discriminated shape satisfy the field entry. Callers (effect handlers) pass the concrete action object literal.

    config?: () => WorkflowClientConfig

    Read the effective client configuration. The engine reads two fields from it. apiHost is probed before deriving the global /users/me sibling, because @sanity/client shallow-merges withConfig overrides and otherwise preserves an explicit project host even when useProjectHostname is set to false. projectId addresses the project's user directory when a project-scoped principal has to be bridged to its account-global id — a client reporting none has no directory to ask, and such a session is refused rather than stamped locally.

    create: <T extends { _id: string; _type: string }>(
        doc: T,
        options?: WorkflowCommitOptions,
    ) => Promise<T>

    Create a brand-new document. Errors loud (HTTP 409 / mutation error) if a document with the same _id already exists. The engine uses this whenever it mints a new doc — instances on startInstance, child instances on spawn, new definitions on first deploy.

    The interface deliberately omits createOrReplace: for existing docs the engine uses patch().set(...).ifRevisionId(rev).commit(), which fails fast if the doc was deleted or concurrently modified (createOrReplace's behaviour on a missing target is ambiguous and its rev-less form silently clobbers). If a consumer truly needs createOrReplace they can extend their own client type.

    fetch: <T = unknown>(
        query: string,
        params?: Record<string, unknown>,
        options?: WorkflowFetchOptions,
    ) => Promise<T>
    getDocument: <T = SanityDocument>(
        id: string,
        options?: { tag?: string },
    ) => Promise<T | null | undefined>
    patch: (documentId: string) => WorkflowPatch
    request?: <T>(
        opts: { signal?: AbortSignal; tag?: string; uri?: string; url?: string },
    ) => Promise<T>

    Optional — present on the real @sanity/client. Without it, ACL grant discovery uses its dry-run fallback, while a definition containing role-constrained assignment fields fails loudly because the engine cannot read the project membership directory required to validate assignees.

    transaction: () => WorkflowTransaction

    Build a multi-document transaction. All operations succeed or fail together. Used whenever the engine writes multiple documents that are provably in the same workflow resource (spawn fan-out, deploy batches, parent + child propagation that stays in one dataset).

    Shape matches @sanity/client.transaction() and the in-memory test client. patch() accepts a WorkflowPatch handle built via client.patch(id).set(...).ifRevisionId(rev) — pass the handle in without calling .commit() on it.

    withConfig?: (config: WorkflowClientConfig) => this

    Optional — present on the real @sanity/client and the in-memory test fake. Returns a sibling client with the given config overrides, inheriting everything else from the source (token, apiHost). The engine derives three things from it:

    • Cross-resource routing (resource + the dataset pair): a sibling for a foreign GDR when resourceClients doesn't map it. A client without withConfig makes foreign GDRs a loud routing error rather than a silent read of the wrong resource.
    • API-version normalization (apiVersion): every engine entry derives its working client with withConfig({apiVersion: ENGINE_API_VERSION}), so the caller's configured version never reaches engine traffic (the caller's own instance is untouched). A client without withConfig cannot be rebound and is used as-is — it must be built to serve ENGINE_API_VERSION already; older dated versions fail silently incomplete, not loud.
    • Effect request attribution (requestTagPrefix): handlers receive a derived client whose untagged traffic carries the effect tag while the caller's own client remains untouched. A client without withConfig uses the structural request wrapper instead.

    Dataset resources carry the {projectId, dataset} pair alongside resource: the real client routes by resource (it takes precedence in URL building), while the test fake keys its dataset registry by the pair. Known gap: the fake ignores resource, so it derives DATASET siblings only — a canvas/media-library derivation on the fake aliases the current store until the fake learns resource upstream.