Graine AI
API referenceWorkflows

Save a workflow's draft

Replace the draft.

Replace the draft. Send the revision you read: if someone saved since, nothing is saved (409).

Rate limit: 2000 requests per minute per organization, counted together with every endpoint that has no stricter limit of its own. Exceeding it returns 429 with Retry-After; the X-RateLimit-* response headers report your remaining allowance on every call.

PUT
/v2/workflows/{workflow_id}/draft
/v2/workflows/owf_8a1d4e7c2b90f1a2b3c4d5e6/draft

The Authorization access token

Authorization

Authorization
Required
Bearer <token>

Your Graine API key. Create one in the dashboard under Developers, or via POST /v2/api-keys. Send it as Authorization: Bearer <key>.

In: header

Request Body

application/jsonRequired

definition
Required
Definition

The whole definition.

revision
Required
Revision

The revision you read (a newer save answers 409).

Minimum: 0

Path Parameters

workflow_id
Required
Workflow Id

The workflow's id.

Response Body

200

The workflow, its draft one revision on.

id
Required
Id

The workflow's id.

name
Required
Name

Its name.

description
Required
Description

What it is for.

versions
Required
Versions

How many versions were published.

latest_versionLatest Version | null

The newest version's number.

agent_ids
Required
Agent Ids

The agents it calls with.

draft_revision
Required
Draft Revision

The draft's revision.

draft_saved_atDraft Saved At | null

When the draft was saved.

trigger_enabled
Required
Trigger Enabled

Whether its trigger URL is on.

created_byCreated By | null

Who created it.

created_atCreated At | null

When.

updated_atUpdated At | null

When it last changed.

published_atPublished At | null

When the newest version was published.

draft
Required
WorkflowDraft

The workflow's editable copy.

trigger
Required
WorkflowTrigger

The URL your system posts a contact to, to start a run.

call_triggerAny properties in WorkflowCallTrigger

app_startAny properties in WorkflowAppStart

callback_urlCallback Url | null

The workflow's callback URL: your system posts here and the body names the contact (only to a key that may edit and run).

signing_secretSigning Secret | null

Signs requests to the trigger and callback URLs (only to a key that may edit and run).

publishedPublished | null

Its versions, newest first (on a read of the workflow).

401

1100 — missing, unknown or inactive API key. 1101 — a browser session token was presented instead of an API key.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

403

1102 — the key is valid but lacks the scope this endpoint requires, or names another organization. GET /v2/scopes reports what a key holds.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

404

1200 — no such resource in your organization. A resource belonging to another organization answers 404, never 403.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

409

1201 — the resource's current state does not allow this action.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

422

1001 — the body or query failed validation; the message names the first offending field.

error
Required
integer

Stable integer code from the error table. Branch on this, not on the message.

message
Required
string

One human-readable sentence. Wording may change; the code will not.

429

1300 — the per-organization request rate or concurrent-call limit was reached. Carries Retry-After.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

curl -X PUT "https://api.graine.ai/v2/workflows/owf_8a1d4e7c2b90f1a2b3c4d5e6/draft" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "definition": {
      "entry_node_id": "start",
      "on_no_match": "unreached",
      "nodes": [
        {
          "id": "start",
          "type": "start",
          "config": {
            "fields": [
              {
                "name": "plan",
                "type": "string"
              }
            ]
          },
          "cases": [
            {
              "when": {
                "always": true
              },
              "then": {
                "to": "call"
              }
            }
          ]
        },
        {
          "id": "call",
          "type": "agent",
          "config": {
            "agent_id": "agt_0123",
            "timeout_s": 1800
          },
          "cases": [
            {
              "when": {
                "cmp": "==",
                "left": {
                  "var": "call.status"
                },
                "right": {
                  "const": "completed"
                }
              },
              "then": {
                "to": "done"
              }
            }
          ]
        },
        {
          "id": "done",
          "type": "end",
          "config": {
            "label": "reached",
            "outcome": "success"
          }
        },
        {
          "id": "unreached",
          "type": "end",
          "config": {
            "label": "unreached",
            "outcome": "failure"
          }
        }
      ]
    },
    "revision": 3
  }'

The workflow, its draft one revision on.

{
  "id": "owf_8a1d4e7c2b90f1a2b3c4d5e6",
  "name": "New leads",
  "description": "Call every new lead within a minute.",
  "versions": 2,
  "latest_version": 2,
  "agent_ids": [
    "agt_0123"
  ],
  "draft_revision": 3,
  "draft_saved_at": "2026-10-25T09:30:00+00:00",
  "trigger_enabled": false,
  "created_by": "member-live-1a2b3c",
  "created_at": "2026-10-20T08:00:00+00:00",
  "updated_at": "2026-10-25T09:31:00+00:00",
  "published_at": "2026-10-25T09:31:00+00:00",
  "draft": {
    "definition": {
      "entry_node_id": "start",
      "on_no_match": "unreached",
      "nodes": [
        {
          "type": "start",
          "config": {
            "fields": [
              {
                "name": "plan",
                "type": "string"
              }
            ]
          },
          "cases": [
            {
              "when": {
                "always": true
              },
              "then": {
                "to": "call"
              }
            }
          ],
          "$id": "start"
        },
        {
          "type": "agent",
          "config": {
            "agent_id": "agt_0123",
            "timeout_s": 1800
          },
          "cases": [
            {
              "when": {
                "cmp": "==",
                "left": {
                  "var": "call.status"
                },
                "right": {
                  "enum": [
                    "completed"
                  ]
                }
              },
              "then": {
                "to": "done"
              }
            }
          ],
          "$id": "call"
        },
        {
          "type": "end",
          "config": {
            "label": "reached",
            "outcome": "success"
          },
          "$id": "done"
        },
        {
          "type": "end",
          "config": {
            "label": "unreached",
            "outcome": "failure"
          },
          "$id": "unreached"
        }
      ]
    },
    "revision": 3,
    "saved_at": "2026-10-25T09:30:00+00:00",
    "saved_by": "member-live-1a2b3c"
  },
  "trigger": {
    "enabled": false,
    "require_signature": true,
    "version": 2,
    "url": "https://api.graine.ai/v2/workflow-triggers/tk_example_trigger"
  },
  "call_trigger": {
    "enabled": true,
    "agent_ids": [
      "agt_0123"
    ],
    "statuses": [
      "no_answer",
      "busy"
    ],
    "directions": [
      "inbound",
      "app"
    ],
    "channels": [
      "call",
      "chat"
    ],
    "version": 2
  },
  "app_start": {
    "enabled": false,
    "fields": [
      "plan",
      "preferred_time"
    ],
    "version": 2
  },
  "callback_url": "https://api.graine.ai/v2/workflow-callbacks/cb_example_workflow",
  "signing_secret": "whsec_example_signing_secret",
  "published": []
}