> ## Documentation Index
> Fetch the complete documentation index at: https://www.bolna.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Save Workflow Draft API

> Save the draft definition using an optimistic-concurrency revision token, so concurrent editors never silently overwrite each other.

<Note>
  `expected_revision` is a compare-and-set token. A fresh workflow's draft is at revision `0`, each save returns the next revision, and publishing resets the new draft to `0`. If your value is stale the save fails with `409 revision_conflict` carrying `current_revision` — re-read the draft and retry.
</Note>

<Info>
  Draft saves accept any well-formed JSON object up to 512 KB. Graph rules (reachability, cycles, node configs) are checked by [Validate](/docs/api-reference/workflows/validate) and enforced at [Publish](/docs/api-reference/workflows/publish). See [Nodes](/docs/guides/workflows/nodes) and [Conditions and variables](/docs/guides/workflows/conditions-and-variables) for the definition schema.
</Info>


## OpenAPI

````yaml PUT /workflows/{workflow_id}/draft
openapi: 3.1.0
info:
  title: Bolna API
  description: >-
    Use and leverage Bolna Voice AI using APIs through HTTP requests from any
    language in your applications and workflows.
  license:
    name: MIT
  version: 1.0.0
servers:
  - url: https://api.bolna.ai
    description: Production server
security:
  - bearerAuth: []
paths:
  /workflows/{workflow_id}/draft:
    put:
      description: >-
        Saves the draft definition. `expected_revision` is a compare-and-set
        token — send the revision you last read; a stale value returns `409
        revision_conflict` with the `current_revision`. Drafts store any
        well-formed JSON object; graph validation happens at validate and
        publish.
      parameters:
        - in: path
          name: workflow_id
          required: true
          schema:
            type: string
            format: uuid
          description: The unique `id` of the workflow
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WorkflowDraftPutRequest'
        required: true
      responses:
        '200':
          description: Draft saved
          content:
            application/json:
              schema:
                type: object
                properties:
                  revision:
                    type: integer
                    description: >-
                      The new draft revision. Send it as `expected_revision` on
                      the next save.
                required:
                  - revision
        '404':
          description: Workflow not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '409':
          description: >-
            `revision_conflict` — `expected_revision` is stale. The response
            carries `current_revision`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '413':
          description: Definition exceeds 512 KB
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '422':
          description: Malformed JSON body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
components:
  schemas:
    WorkflowDraftPutRequest:
      type: object
      required:
        - definition
        - expected_revision
      properties:
        expected_revision:
          type: integer
          minimum: 0
          description: >-
            The revision you last read. A fresh workflow's draft starts at `0`,
            and publish resets the new draft to `0`. A stale value returns `409
            revision_conflict` carrying `current_revision`.
        definition:
          $ref: '#/components/schemas/WorkflowDefinition'
    WorkflowError:
      type: object
      description: >-
        Error envelope returned by every workflow endpoint. `detail.code` is a
        stable machine-readable code; extra keys (such as `current_revision` on
        `revision_conflict`, `execution_id` on `duplicate_run`, or `issues` on
        `invalid_definition`) ride alongside it.
      required:
        - detail
      properties:
        detail:
          type: object
          additionalProperties: true
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code.
              example: revision_conflict
            message:
              type: string
              description: Human-readable explanation.
    WorkflowDefinition:
      type: object
      description: >
        The workflow graph. `nodes` is a flat list of node envelopes — `{id,
        type, name, config, cases}` — where `type` is one of `start`, `agent`,
        `extraction`, `api`, `time`, `retry`, `aisensy_whatsapp` or `end`. Each
        node's `cases` array routes to the next node via condition expressions
        (`time` nodes use a single `to` instead, `end` nodes have neither). See
        [Nodes](/guides/workflows/nodes) and [Conditions and
        variables](/guides/workflows/conditions-and-variables) for the full
        schema, and the [Node Types API](/api-reference/workflows/node-types)
        for every config parameter with defaults and bounds.
      required:
        - entry_node_id
        - on_no_match
        - nodes
      properties:
        entry_node_id:
          type: string
          description: The `id` of the workflow's single `start` node.
          example: n_start
        on_no_match:
          type: string
          description: >-
            Workflow-wide fallback — the node an execution moves to when none of
            a node's cases match and the node declares no `on_no_match` of its
            own. Usually an `end` node.
          example: n_end_unhandled
        nodes:
          type: array
          maxItems: 200
          description: The node envelopes making up the graph.
          items:
            type: object
      example:
        entry_node_id: n_start
        on_no_match: n_end_unhandled
        nodes:
          - id: n_start
            type: start
            config:
              trigger:
                kind: manual
            cases:
              - when:
                  always: true
                then:
                  to: n_call1
          - id: n_call1
            type: agent
            name: Qualify
            config:
              agent_id: 123e4567-e89b-12d3-a456-426655440000
              timeout_s: 3600
            cases:
              - when:
                  cmp: '=='
                  left:
                    var: call.status
                  right:
                    const: completed
                then:
                  to: n_end_done
          - id: n_end_done
            type: end
            config:
              label: reached
              outcome: success
          - id: n_end_unhandled
            type: end
            config:
              label: unhandled
              outcome: failure
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````