> ## Documentation Index
> Fetch the complete documentation index at: https://doc.featherhq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Diff two workflow revisions

> Compute a two-layer diff between two revisions of the same workflow.

Returns:
  - ``source_diff``: deterministic diff of the authored config columns
    (always present, always exact).
  - ``graph_diff``: projected behavior diff over the compiled graphs
    (null when either side is uncompiled or the stored graph is unparseable).
  - ``fidelity``: ``"exact"`` | ``"approximate"`` | ``"source_only"``.



## OpenAPI

````yaml /api-reference/openapi.json get /v1/workflows/{workflow_id}/revisions/diff
openapi: 3.1.0
info:
  title: Feather API
  description: >-
    Unified customer experience platform API. Manages identity, conversations,
    memory, agents, procedures, policies, model routing, knowledge bases,
    integrations, and runtime execution.
  version: 1.21.0
servers:
  - url: https://api-sandbox.featherhq.com
    description: Sandbox
  - url: http://localhost:8000
    description: Local dev
security: []
paths:
  /v1/workflows/{workflow_id}/revisions/diff:
    get:
      tags:
        - workflows
      summary: Diff two workflow revisions
      description: |-
        Compute a two-layer diff between two revisions of the same workflow.

        Returns:
          - ``source_diff``: deterministic diff of the authored config columns
            (always present, always exact).
          - ``graph_diff``: projected behavior diff over the compiled graphs
            (null when either side is uncompiled or the stored graph is unparseable).
          - ``fidelity``: ``"exact"`` | ``"approximate"`` | ``"source_only"``.
      operationId: getWorkflowRevisionDiff
      parameters:
        - name: workflow_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
            title: Workflow Id
        - name: base
          in: query
          required: true
          schema:
            type: string
            format: uuid
            title: Base
        - name: target
          in: query
          required: true
          schema:
            type: string
            format: uuid
            title: Target
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowRevisionDiffResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Resource not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
        - APIKeyHeader: []
components:
  schemas:
    WorkflowRevisionDiffResponse:
      properties:
        workflow_id:
          type: string
          format: uuid
          title: Workflow Id
        base:
          $ref: '#/components/schemas/RevisionDiffSide'
        target:
          $ref: '#/components/schemas/RevisionDiffSide'
        fidelity:
          type: string
          enum:
            - exact
            - approximate
            - source_only
          title: Fidelity
        source_diff:
          $ref: '#/components/schemas/SourceDiff'
        graph_diff:
          anyOf:
            - $ref: '#/components/schemas/GraphDiff'
            - type: 'null'
      type: object
      required:
        - workflow_id
        - base
        - target
        - fidelity
        - source_diff
      title: WorkflowRevisionDiffResponse
      description: Top-level response returned by GET /workflows/{id}/revisions/diff.
    ErrorResponse:
      properties:
        error:
          type: string
          title: Error
        message:
          type: string
          title: Message
      type: object
      required:
        - error
        - message
      title: ErrorResponse
      description: Standard error response returned by all API error handlers.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    RevisionDiffSide:
      properties:
        revision_id:
          type: string
          format: uuid
          title: Revision Id
        name:
          type: string
          title: Name
        provenance:
          type: string
          enum:
            - compiled
            - hand_edited
            - uncompiled
          title: Provenance
      type: object
      required:
        - revision_id
        - name
        - provenance
      title: RevisionDiffSide
      description: Metadata about one side of the diff (base or target).
    SourceDiff:
      properties:
        instructions:
          $ref: '#/components/schemas/TextDiff'
        system_prompt:
          $ref: '#/components/schemas/TextDiff'
        config:
          $ref: '#/components/schemas/FieldChangeCollection'
        policies:
          $ref: '#/components/schemas/PolicyNamesDiff'
        tool_input_defaults:
          $ref: '#/components/schemas/FieldChangeCollection'
        model_settings:
          $ref: '#/components/schemas/ModelSettingsDiff'
      type: object
      required:
        - instructions
        - system_prompt
      title: SourceDiff
      description: >-
        Source-layer diff — exactly what the author edited (authored config
        columns).


        Always present and always deterministic regardless of graph provenance.
    GraphDiff:
      properties:
        summary:
          $ref: '#/components/schemas/GraphSummary'
        graph_meta:
          $ref: '#/components/schemas/src__workflow__diff__models__GraphMeta'
        nodes:
          $ref: '#/components/schemas/NodeCollectionDiff'
        edges:
          $ref: '#/components/schemas/EdgeCollectionDiff'
        context_variables:
          $ref: '#/components/schemas/ContextVarCollectionDiff'
        escape_hatches:
          $ref: '#/components/schemas/EscapeHatchCollectionDiff'
      type: object
      required:
        - summary
        - graph_meta
      title: GraphDiff
      description: Full behavior diff — projected graph comparison.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    TextDiff:
      properties:
        segments:
          items:
            $ref: '#/components/schemas/TextDiffSegment'
          type: array
          title: Segments
      type: object
      required:
        - segments
      title: TextDiff
      description: Line-level prose diff built from difflib.SequenceMatcher opcodes.
    FieldChangeCollection:
      properties:
        added:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Added
        removed:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Removed
        changed:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Changed
      type: object
      title: FieldChangeCollection
      description: Generic collection diff for scalar config fields.
    PolicyNamesDiff:
      properties:
        added:
          items:
            type: string
          type: array
          title: Added
        removed:
          items:
            type: string
          type: array
          title: Removed
      type: object
      title: PolicyNamesDiff
      description: Added/removed policy names (name-resolved).
    ModelSettingsDiff:
      properties:
        analyzer:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Analyzer
        router:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Router
      type: object
      title: ModelSettingsDiff
      description: Diff over analyzer and router model settings dicts.
    GraphSummary:
      properties:
        nodes_added:
          type: integer
          title: Nodes Added
          default: 0
        nodes_removed:
          type: integer
          title: Nodes Removed
          default: 0
        nodes_changed:
          type: integer
          title: Nodes Changed
          default: 0
        edges_added:
          type: integer
          title: Edges Added
          default: 0
        edges_removed:
          type: integer
          title: Edges Removed
          default: 0
        edges_changed:
          type: integer
          title: Edges Changed
          default: 0
        context_vars_added:
          type: integer
          title: Context Vars Added
          default: 0
        context_vars_removed:
          type: integer
          title: Context Vars Removed
          default: 0
        context_vars_changed:
          type: integer
          title: Context Vars Changed
          default: 0
        escape_hatches_added:
          type: integer
          title: Escape Hatches Added
          default: 0
        escape_hatches_removed:
          type: integer
          title: Escape Hatches Removed
          default: 0
        escape_hatches_changed:
          type: integer
          title: Escape Hatches Changed
          default: 0
      type: object
      title: GraphSummary
      description: Counts of changes across the graph diff (for the summary header).
    src__workflow__diff__models__GraphMeta:
      properties:
        persona:
          $ref: '#/components/schemas/TextDiff'
        system_prompt:
          $ref: '#/components/schemas/TextDiff'
      type: object
      required:
        - persona
        - system_prompt
      title: GraphMeta
      description: >-
        Workflow-scope graph metadata diff (persona + system_prompt prose
        fields).
    NodeCollectionDiff:
      properties:
        added:
          items:
            $ref: '#/components/schemas/ProjectedNode'
          type: array
          title: Added
        removed:
          items:
            $ref: '#/components/schemas/ProjectedNode'
          type: array
          title: Removed
        changed:
          items:
            $ref: '#/components/schemas/NodeChange'
          type: array
          title: Changed
      type: object
      title: NodeCollectionDiff
      description: Diff over the node collection.
    EdgeCollectionDiff:
      properties:
        added:
          items:
            $ref: '#/components/schemas/ProjectedEdge'
          type: array
          title: Added
        removed:
          items:
            $ref: '#/components/schemas/ProjectedEdge'
          type: array
          title: Removed
        changed:
          items:
            $ref: '#/components/schemas/EdgeChange'
          type: array
          title: Changed
      type: object
      title: EdgeCollectionDiff
      description: Diff over the edge collection.
    ContextVarCollectionDiff:
      properties:
        added:
          items:
            $ref: '#/components/schemas/ProjectedContextVar'
          type: array
          title: Added
        removed:
          items:
            $ref: '#/components/schemas/ProjectedContextVar'
          type: array
          title: Removed
        changed:
          items:
            $ref: '#/components/schemas/ContextVarChange'
          type: array
          title: Changed
      type: object
      title: ContextVarCollectionDiff
      description: Diff over the context_variables collection.
    EscapeHatchCollectionDiff:
      properties:
        added:
          items:
            $ref: '#/components/schemas/ProjectedEscapeHatch'
          type: array
          title: Added
        removed:
          items:
            $ref: '#/components/schemas/ProjectedEscapeHatch'
          type: array
          title: Removed
        changed:
          items:
            $ref: '#/components/schemas/EscapeHatchChange'
          type: array
          title: Changed
      type: object
      title: EscapeHatchCollectionDiff
      description: Diff over the escape_hatches collection.
    TextDiffSegment:
      properties:
        op:
          type: string
          enum:
            - equal
            - insert
            - delete
          title: Op
        text:
          type: string
          title: Text
      type: object
      required:
        - op
        - text
      title: TextDiffSegment
      description: One segment from a prose diff (equal / insert / delete).
    FieldChange:
      properties:
        field:
          type: string
          title: Field
        op:
          type: string
          enum:
            - added
            - removed
            - modified
          title: Op
        text_diff:
          anyOf:
            - $ref: '#/components/schemas/TextDiff'
            - type: 'null'
        before:
          anyOf:
            - {}
            - type: 'null'
          title: Before
        after:
          anyOf:
            - {}
            - type: 'null'
          title: After
      type: object
      required:
        - field
        - op
      title: FieldChange
      description: >-
        Change to a single named field within a node, config block, or
        collection entry.
    ProjectedNode:
      properties:
        id:
          type: string
          title: Id
        kind:
          type: string
          enum:
            - agent
            - action
            - handoff
            - approval
          title: Kind
        is_entry:
          type: boolean
          title: Is Entry
          default: false
        instructions:
          anyOf:
            - type: string
            - type: 'null'
          title: Instructions
        required_writes:
          items:
            type: string
          type: array
          title: Required Writes
        writes:
          items:
            type: string
          type: array
          title: Writes
        done_when_readable:
          anyOf:
            - type: string
            - type: 'null'
          title: Done When Readable
        tools:
          items:
            $ref: '#/components/schemas/ProjectedTool'
          type: array
          title: Tools
        knowledge_bases:
          items:
            $ref: '#/components/schemas/ProjectedKnowledgeBase'
          type: array
          title: Knowledge Bases
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
        failure_message:
          anyOf:
            - type: string
            - type: 'null'
          title: Failure Message
        steps:
          items:
            $ref: '#/components/schemas/ProjectedActionStep'
          type: array
          title: Steps
        result_write_names:
          items:
            type: string
          type: array
          title: Result Write Names
        static_text:
          anyOf:
            - type: string
            - type: 'null'
          title: Static Text
        voice_config:
          anyOf:
            - $ref: '#/components/schemas/ProjectedVoiceHandoff'
            - type: 'null'
        on_failure:
          anyOf:
            - type: string
            - type: 'null'
          title: On Failure
        condition_readable:
          anyOf:
            - type: string
            - type: 'null'
          title: Condition Readable
        approver_role:
          anyOf:
            - type: string
            - type: 'null'
          title: Approver Role
        suspend_message:
          anyOf:
            - type: string
            - type: 'null'
          title: Suspend Message
        timeout_seconds:
          anyOf:
            - type: integer
            - type: 'null'
          title: Timeout Seconds
        on_approve:
          anyOf:
            - type: string
            - type: 'null'
          title: On Approve
        on_deny:
          anyOf:
            - type: string
            - type: 'null'
          title: On Deny
      type: object
      required:
        - id
        - kind
      title: ProjectedNode
      description: >-
        Author-facing node shape — semantics only, IR wiring stripped.


        Fields shown per the §5 strip-list:

        - agent: id, kind, instructions, required_writes, writes,
        done_when_readable,
                 tools (resolved names + produced var names), knowledge_bases
        - action: id, kind, description, failure_message, steps (sanitized),
                  result_write_names (only the VAR NAMES, not dot-path templates)
        - handoff: id, kind, static_text, channels.voice (sanitized), on_failure

        - approval: id, kind, condition_readable, approver_role,
        suspend_message,
                    timeout_seconds, on_approve, on_deny

        is_entry: true for the graph entry node (entry_node_id projected here,
        not as a

        raw field on the graph object).
    NodeChange:
      properties:
        id:
          type: string
          title: Id
        kind:
          type: string
          enum:
            - agent
            - action
            - handoff
            - approval
          title: Kind
        changes:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Changes
      type: object
      required:
        - id
        - kind
      title: NodeChange
      description: Field-level changes within a matched (same id) node pair.
    ProjectedEdge:
      properties:
        from_node:
          type: string
          title: From Node
        to_node:
          type: string
          title: To Node
        condition_readable:
          anyOf:
            - type: string
            - type: 'null'
          title: Condition Readable
        is_failure_path:
          type: boolean
          title: Is Failure Path
          default: false
      type: object
      required:
        - from_node
        - to_node
      title: ProjectedEdge
      description: >-
        Author-facing edge — from/to node IDs + human-readable condition +
        failure label.


        priority raw int is stripped; order is preserved by list position in
        GraphDiff.
    EdgeChange:
      properties:
        from_node:
          type: string
          title: From Node
        to_node:
          type: string
          title: To Node
        changes:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Changes
      type: object
      required:
        - from_node
        - to_node
      title: EdgeChange
      description: >-
        Field-level changes within a matched (same from_node, to_node) edge
        pair.
    ProjectedContextVar:
      properties:
        name:
          type: string
          title: Name
        type:
          type: string
          title: Type
        source:
          type: string
          title: Source
        enum_values:
          anyOf:
            - items:
                type: string
              type: array
            - type: 'null'
          title: Enum Values
        reask_cap:
          anyOf:
            - type: integer
            - type: 'null'
          title: Reask Cap
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
      type: object
      required:
        - name
        - type
        - source
      title: ProjectedContextVar
      description: Author-facing context variable definition — all fields shown per §5.
    ContextVarChange:
      properties:
        name:
          type: string
          title: Name
        changes:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Changes
      type: object
      required:
        - name
      title: ContextVarChange
      description: Field-level changes within a matched (same name) context variable pair.
    ProjectedEscapeHatch:
      properties:
        trigger:
          type: string
          title: Trigger
        target_label:
          type: string
          title: Target Label
        static_line:
          anyOf:
            - type: string
            - type: 'null'
          title: Static Line
      type: object
      required:
        - trigger
        - target_label
      title: ProjectedEscapeHatch
      description: Author-facing escape hatch — trigger, rendered target, optional line.
    EscapeHatchChange:
      properties:
        trigger:
          type: string
          title: Trigger
        changes:
          items:
            $ref: '#/components/schemas/FieldChange'
          type: array
          title: Changes
      type: object
      required:
        - trigger
      title: EscapeHatchChange
      description: Field-level changes within a matched (same trigger) escape hatch pair.
    ProjectedTool:
      properties:
        canonical_key:
          type: string
          title: Canonical Key
        display_name:
          type: string
          title: Display Name
        produces_variables:
          items:
            type: string
          type: array
          title: Produces Variables
      type: object
      required:
        - canonical_key
        - display_name
      title: ProjectedTool
      description: Sanitized tool reference — name + produced variable names, no dot-paths.
    ProjectedKnowledgeBase:
      properties:
        kb_id:
          type: string
          title: Kb Id
        display_name:
          type: string
          title: Display Name
        clearance_level:
          anyOf:
            - type: integer
            - type: 'null'
          title: Clearance Level
      type: object
      required:
        - kb_id
        - display_name
      title: ProjectedKnowledgeBase
      description: Sanitized KB reference — name + clearance level only.
    ProjectedActionStep:
      properties:
        id:
          type: string
          title: Id
        kind:
          type: string
          enum:
            - speak
            - tool_call
            - knowledge_retrieval
          title: Kind
        text:
          anyOf:
            - type: string
            - type: 'null'
          title: Text
        tool_display_name:
          anyOf:
            - type: string
            - type: 'null'
          title: Tool Display Name
        knowledge_base_names:
          items:
            type: string
          type: array
          title: Knowledge Base Names
      type: object
      required:
        - id
        - kind
      title: ProjectedActionStep
      description: |-
        Sanitized action step — kind, speak text, tool name, KB names.

        input_mapping and output_bindings are stripped.
    ProjectedVoiceHandoff:
      properties:
        targets:
          items:
            $ref: '#/components/schemas/ProjectedVoiceTarget'
          type: array
          title: Targets
        connecting_text:
          anyOf:
            - type: string
            - type: 'null'
          title: Connecting Text
        mode:
          type: string
          title: Mode
          default: cold
        max_retries:
          type: integer
          title: Max Retries
          default: 1
        retry_text:
          anyOf:
            - type: string
            - type: 'null'
          title: Retry Text
      type: object
      title: ProjectedVoiceHandoff
      description: Voice channel config — targets + text, no internal destination fields.
    ProjectedVoiceTarget:
      properties:
        label:
          type: string
          title: Label
        when_to_use:
          anyOf:
            - type: string
            - type: 'null'
          title: When To Use
      type: object
      required:
        - label
      title: ProjectedVoiceTarget
      description: One voice transfer destination shown to the author.
  securitySchemes:
    APIKeyHeader:
      type: apiKey
      in: header
      name: x-api-key

````