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

# Replace a workflow’s steps

> Write a workflow’s whole lines, only over the version you read. Every open canvas shows the change at once.

Replaces a workflow’s steps with the lines you send. Read the workflow with [Get a workflow](https://docs.orgo.ai/api-reference/workflows/get.md), change its `lines`, and send them back with `If-Match` set to the version you read. A change made since then is never written over: the answer is `409` with the newest version and lines, so you can merge your change into them and send it again.

A `PUT` to an id no workflow has makes a new one. A workflow that is on and can no longer run after the change is turned off, and the answer says `turned_off: true`. A write that changes nothing writes nothing, and the version stays.

## Path parameters

**id** `string` *required*

The workflow’s id, like `morning-report`: lowercase letters, digits and `-`, at most 60.

## Headers

**If-Match** `string`

The version you read, as [Get a workflow](https://docs.orgo.ai/api-reference/workflows/get.md) gave it in `version` or the `ETag` header. Without it, the write is made over whatever is there now.

**X-Agent** `string`

Who is writing, at most 30 characters, like `Claude Code`. The canvas names you on the change.

## Request body

**lines** `string` *required*

The whole workflow, one JSON object per step, the start first. The format is at [orgo.ai/workflows.md](https://www.orgo.ai/workflows.md).

**time\_zone** `string`

An IANA zone its times are read in, like `America/New_York`.

## Response

The workflow as kept, in the same shape [Get a workflow](https://docs.orgo.ai/api-reference/workflows/get.md) answers, with the new version in `version` and the `ETag` header. `lines` are the lines as the canvas reads them, and the next `GET` hands back exactly those. Two more fields appear only when they apply:

**dropped** `string[]`

What you sent that was left out or rewritten.

**turned\_off** `boolean`

`true` when this write left it unable to run, so it was turned off.

## Example

```bash cURL theme={null}
# Read it, edit morning-report.jsonl, then write it back over the version you read
curl -s https://www.orgo.ai/api/workflows/morning-report \
  -H "Authorization: Bearer $ORGO_API_KEY" | jq -r .lines > morning-report.jsonl

jq -Rs '{lines: .}' morning-report.jsonl | curl -X PUT https://www.orgo.ai/api/workflows/morning-report \
  -H "Authorization: Bearer $ORGO_API_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "3"' \
  -H "X-Agent: Claude Code" \
  -d @-
```

```python Python theme={null}
import os, requests

url = "https://www.orgo.ai/api/workflows/morning-report"
headers = {"Authorization": f"Bearer {os.environ['ORGO_API_KEY']}"}

current = requests.get(url, headers=headers).json()
lines = current["lines"].replace('"title":"9:00 AM"', '"title":"8:00 AM"')
r = requests.put(url, headers={**headers, "If-Match": str(current["version"])}, json={"lines": lines})
if r.status_code == 409:
    print("Changed since you read it. Merge into:", r.json()["lines"])
```

```javascript JavaScript theme={null}
const url = "https://www.orgo.ai/api/workflows/morning-report";
const headers = { Authorization: `Bearer ${process.env.ORGO_API_KEY}` };

const current = await (await fetch(url, { headers })).json();
const r = await fetch(url, {
  method: "PUT",
  headers: { ...headers, "Content-Type": "application/json", "If-Match": String(current.version) },
  body: JSON.stringify({ lines: current.lines.replace('"title":"9:00 AM"', '"title":"8:00 AM"') }),
});
```

### Response

```json theme={null}
{
  "id": "morning-report",
  "name": "Morning report",
  "on": false,
  "version": 4,
  "lines": "{\"id\":\"a\",\"kind\":\"start\",\"title\":\"8:00 AM\",\"sub\":\"Weekdays\",\"name\":\"Morning report\"}\n{\"id\":\"b\",\"after\":[\"a\"],\"kind\":\"agent\",\"agent\":\"hermes\",\"title\":\"Write the report\",\"task\":\"Write a short report on yesterday’s new customers.\"}\n{\"id\":\"c\",\"after\":[\"b\"],\"kind\":\"app\",\"app\":\"slack\",\"title\":\"Post\",\"where\":\"{channel}\",\"message\":\"{report}\"}",
  "settings": {},
  "to_fix": ["Fill in channel"],
  "problems": [{ "step": "a", "say": "Fill in channel" }]
}
```

### A version conflict (`409`)

```json theme={null}
{
  "error": "The workflow changed since you read it. Merge your change into these lines and send it again.",
  "code": "version_conflict",
  "version": 5,
  "lines": "<the newest lines>",
  "next": "Read it again (GET /api/workflows/morning-report), make your change on its lines, and PUT them with If-Match: <its version>."
}
```

When the workflow was deleted since you read it, `version` and `lines` are `null`: send it again without `If-Match` to make it again.

## Errors

| Status | Code | When |
| - | - | - |
| `400` | `invalid_body` | A body that is not `{ "lines": "..." }`, or a `time_zone` that is not an IANA zone. |
| `400` | `invalid_id` | An id that is not lowercase letters, digits and `-`, at most 60. |
| `401` | | No key, or a key Orgo doesn’t know. |
| `403` | `key_permission` | A restricted key without Workflows write, or steps that act on its computer from a key without Control write and AI write. |
| `404` | (no body) | Workflows aren’t open to your account. |
| `409` | `version_conflict` | `If-Match` names an older version. The body carries the newest `version` and `lines`. |
| `409` | `too_many_workflows` | A new id, and you have 200 workflows already. |
| `413` | `body_too_large` | The body is over 128 KB. |
| `422` | `invalid_lines` | Lines that could not be read, named in `dropped`. Nothing was written. |
| `429` | `rate_limited` | Too many writes. Carries `Retry-After`. |
| `503` | | Orgo couldn’t verify the key because of a server-side fault. Retry. |


## OpenAPI

````yaml PUT /workflows/{id}
openapi: 3.1.0
info:
  title: Orgo API
  description: >-
    Launch cloud computers that AI agents can control and interact with. Create
    workspaces, provision computers, and control them programmatically.
  version: 2.0.0
  contact:
    name: Orgo Support
    email: contact@orgo.ai
    url: https://orgo.ai
servers:
  - url: https://www.orgo.ai/api
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Support
    description: >-
      Orgo’s support agent, for programs: ask how to do something in Orgo and
      get an answer with full URLs and copyable commands.
  - name: Account
    description: >-
      Account capacity: how many computers an account may run, and adding or
      giving back more.
  - name: Agent accounts
    description: >-
      Accounts an AI agent makes for itself, and the claim link a person uses to
      take one over.
  - name: Clients
    description: >-
      Run Orgo for your clients from your own app: a workspace and scoped key
      each, billed to you or to them.
  - name: Workspaces
    description: Organize computers into named workspaces
  - name: Computers
    description: Provision and manage virtual computers
  - name: Computer Lifecycle
    description: Start, stop, and restart computers
  - name: Computer Actions
    description: Control mouse, keyboard, and execute commands
  - name: Screens
    description: >-
      More than one desktop on a single computer. Each screen is its own X
      server with its own cursor and window manager, so an agent working on one
      cannot disturb another.
  - name: Files
    description: Upload and download files
  - name: Templates
    description: Author, build, and launch reproducible computers from templates
  - name: Chat
    description: Run an agent on a computer through OpenAI-compatible chat completions.
  - name: Workflows
    description: >-
      Routines that run the same way every time: a start, then steps. Make them
      from plain words or lines, check them, run them and follow their runs (in
      preview).
paths:
  /workflows/{id}:
    put:
      tags:
        - Workflows
      summary: Write a workflow’s steps
      description: >-
        Body: { lines }, the whole workflow, one JSON object per step. Send
        If-Match: <version> (from GET) so a change made since is never written
        over: then a newer version answers 409 version_conflict with the newest
        version and lines to merge into. What is kept is lines as the canvas
        reads them (the answer’s lines); dropped names anything left out or
        rewritten. A workflow that is on and can no longer run is turned off
        (turned_off: true). A new id makes a new workflow. Every open canvas
        shows the change at once; X-Agent names who wrote it there.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            pattern: ^[a-z0-9-]{1,60}$
          example: morning-report
        - name: If-Match
          in: header
          required: false
          schema:
            type: string
          description: The version you read (GET’s version, or its ETag).
        - name: X-Agent
          in: header
          required: false
          schema:
            type: string
            maxLength: 30
          description: Who you are, as the canvas names you (like Claude Code).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - lines
              properties:
                lines:
                  type: string
                  description: >-
                    One JSON object per step, the start first
                    (https://www.orgo.ai/workflows.md).
                time_zone:
                  type: string
                  description: An IANA zone its times are read in, like America/New_York.
            example:
              lines: >-
                {"id":"a","kind":"start","title":"9:00
                AM","sub":"Weekdays","name":"Morning report"}

                {"id":"b","after":["a"],"kind":"agent","agent":"hermes","title":"Write
                the report","task":"Write a short report on yesterday’s new
                customers."}

                {"id":"c","after":["b"],"kind":"app","app":"slack","title":"Post","where":"{channel}","message":"{report}"}
      responses:
        '200':
          description: The workflow, as kept.
          headers:
            ETag:
              description: The version, quoted.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'
              example:
                id: morning-report
                name: Morning report
                'on': false
                version: 3
                lines: >-
                  {"id":"a","kind":"start","title":"9:00
                  AM","sub":"Weekdays","name":"Morning report"}

                  {"id":"b","after":["a"],"kind":"agent","agent":"hermes","title":"Write
                  the report","task":"Write a short report on yesterday’s new
                  customers."}

                  {"id":"c","after":["b"],"kind":"app","app":"slack","title":"Post","where":"{channel}","message":"{report}"}
                settings: {}
                to_fix:
                  - Fill in channel
                problems:
                  - step: a
                    say: Fill in channel
        '400':
          description: A body that is not the shape asked for (invalid_body).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '401':
          description: Unauthenticated (with WWW-Authenticate and docs).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '403':
          description: >-
            A key without Workflows access at this level (key_permission); a key
            that may not act on the workflow’s computer (key_permission: Control
            write and AI write, or computer_key_held).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '404':
          description: >-
            No workflow (or run) by that id (not_found); a bare 404 with no body
            where Workflows are not open to the account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '409':
          description: >-
            If-Match names an older version (version_conflict): the body carries
            the newest version and lines. Merge your change into them and send
            it again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '413':
          description: >-
            The body is too large (body_too_large): at most 64 KB, 128 KB for a
            PUT, 32 KB for an answer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '422':
          description: >-
            Lines that could not be read (invalid_lines, with dropped). Nothing
            was written.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/AuthUnavailable'
components:
  schemas:
    Workflow:
      type: object
      required:
        - id
        - name
        - 'on'
        - version
        - lines
        - settings
        - to_fix
        - problems
      properties:
        id:
          type: string
          pattern: ^[a-z0-9-]{1,60}$
        name:
          type:
            - string
            - 'null'
        'on':
          type: boolean
          description: Whether its start runs it. It is on only while it can run.
        version:
          type: integer
          description: Send it as If-Match on a write.
        lines:
          type: string
          description: >-
            The workflow, one JSON object per step, the start first
            (https://www.orgo.ai/workflows.md).
        settings:
          type: object
          additionalProperties:
            type: string
          description: What it asks for once, name to value.
        to_fix:
          type: array
          items:
            type: string
          description: >-
            What still has to be done before it can run, in the person’s words.
            Empty: ready.
        problems:
          type: array
          items:
            type: object
            properties:
              step:
                type: string
                description: 'The step it is on (empty: the whole workflow).'
              say:
                type: string
        turned_off:
          type: boolean
          description: This write left it unable to run, so it was turned off.
        dropped:
          type: array
          items:
            type: string
          description: What of the lines sent was left out or rewritten.
    WorkflowError:
      type: object
      required:
        - error
        - code
      properties:
        error:
          type: string
        code:
          type: string
        say:
          type: string
          description: The same, short, for a person.
        next:
          type: string
          description: The call that fixes it.
        to_fix:
          type: array
          items:
            type: string
        problems:
          type: array
          items:
            type: object
        version:
          type:
            - integer
            - 'null'
          description: 'version_conflict: the newest version.'
        lines:
          type:
            - string
            - 'null'
          description: 'version_conflict: the newest lines, to merge into.'
    RateLimited:
      type: object
      required:
        - error
        - code
        - limit
        - retry_after
      properties:
        error:
          type: string
          description: Which limit, in plain words, and when to try again.
        code:
          type: string
          const: rate_limited
        limit:
          type: string
          enum:
            - computer_create_per_minute
            - computer_create_per_hour
            - computer_create_per_day
            - computer_lifecycle_per_minute
            - computer_lifecycle_per_computer_per_minute
            - computer_control_per_minute
            - computer_control_per_computer_per_minute
            - chat_runs_per_minute
            - chat_runs_concurrent
            - workspace_create_per_hour
            - api_other_per_minute
        retry_after:
          type: integer
          minimum: 1
          description: Seconds to wait.
      example:
        error: >-
          Too many API requests this minute (120 a minute on your plan). Try
          again in 12 seconds.
        code: rate_limited
        limit: api_other_per_minute
        retry_after: 12
    Error:
      type: object
      description: >-
        The base error body. Every failure carries `error`; individual endpoints
        add the fields named in the schemas below.
      required:
        - error
      properties:
        error:
          type: string
          description: Human-readable message.
          example: Access denied
        code:
          type: string
          description: >-
            Machine-readable reason. Present on the failures that define one,
            absent otherwise.
  responses:
    RateLimited:
      description: >-
        Over one of the account's plan rate limits (`rate_limited`): `limit`
        names it, and `retry_after` and `Retry-After` say how many seconds to
        wait. Limits are per account, the workspace owner's when the request
        acts on a computer or a workspace, and set by its plan. See [Account
        limits and rate limits](/guides/account-limits#plan-rate-limits).
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimited'
    AuthUnavailable:
      description: >-
        Orgo could not verify the credential because of a server-side fault,
        such as a database outage. Retry after `Retry-After`. Do not rotate the
        key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: >-
              Service temporarily unavailable. The database is not accepting
              requests. Retry shortly.
      headers:
        Retry-After:
          description: Seconds to wait before retrying. Always `5`.
          schema:
            type: string
            example: '5'
  headers:
    RetryAfter:
      description: Seconds to wait before asking again.
      schema:
        type: integer
        minimum: 1
    RateLimitLimit:
      description: The tightest limit this request counts against.
      schema:
        type: integer
    RateLimitRemaining:
      description: How many are left of it in this window.
      schema:
        type: integer
    RateLimitReset:
      description: Seconds until its window starts again.
      schema:
        type: integer
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key authentication. Get your key at orgo.ai/workspaces

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.