> Source: https://wexa.ai/docs/automation/update-process-flow

# update-process-flow

[Can be held for approval](/docs/sdks-and-apis/how-every-tool-call-works#lifecycle-and-governance)

Add one agent to an existing process flow, or edit one already in it, in place. Needs the `fabric:orchestrate.write` grant.

**Add an agent to a process flow**

**Wexa MCP server**

```bash
curl -sS -X POST https://fabric.wexa.ai/mcp/$FABRIC_PROJECT_ID \
  -H "authorization: Bearer $FABRIC_API_KEY" \
  -H "Mcp-Session-Id: $MCP_SESSION_ID" \
  -H "content-type: application/json" -H "accept: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"update-process-flow","arguments":{"process_flow_id":"6ab1945e7566f41b5c28e308","agent":{"title":"Fixture Step Two","role":"conversational","role_description":"A second step the documentation cites.","prompt":{"template":"Reply OK.","display_template":"Reply OK."},"has_knowledge_base":true,"context":["docs-fixture"]}}}}'
```

```json
{
  "content": [
    {
      "type": "text",
      "text": "{\"lifecycle_id\":\"qlc_000183\",\"result\":{\"process_flow_id\":\"6ab1945e7566f41b5c28e308\",\"agent_id\":\"6ab1968189fbbb1251ca6754\"}}"
    }
  ],
  "isError": false
}
```

**REST API**

```bash
curl -sS https://fabric.wexa.ai/v1/update-process-flow \
  -H "authorization: Bearer $FABRIC_API_KEY" \
  -H "content-type: application/json" \
  -d '{"process_flow_id":"6ab1945e7566f41b5c28e308","agent":{"title":"Fixture Step Two","role":"conversational","role_description":"A second step the documentation cites.","prompt":{"template":"Reply OK.","display_template":"Reply OK."},"has_knowledge_base":true,"context":["docs-fixture"]}}'
```

```json
{
  "lifecycle_id": "qlc_000183",
  "result": {
    "process_flow_id": "6ab1945e7566f41b5c28e308",
    "agent_id": "6ab1968189fbbb1251ca6754"
  }
}
```

**TypeScript SDK**

```ts
import { Fabric } from '@wexa-fabric/sdk'

const fabric = new Fabric()
const result = await fabric.updateProcessFlow({
  process_flow_id: '6ab1945e7566f41b5c28e308',
  agent: {
    title: 'Fixture Step Two',
    role: 'conversational',
    role_description: 'A second step the documentation cites.',
    prompt: {
      template: 'Reply OK.',
      display_template: 'Reply OK.'
    },
    has_knowledge_base: true,
    context: ['docs-fixture']
  }
})
```

```ts
// result is the payload itself, with no `.result` to read
{
  process_flow_id: '6ab1945e7566f41b5c28e308',
  agent_id: '6ab1968189fbbb1251ca6754'
}
```

**Python SDK**

```python
from wexa import Fabric

fabric = Fabric()
out = fabric.update_process_flow(
    process_flow_id="6ab1945e7566f41b5c28e308",
    agent={
        "title": "Fixture Step Two",
        "role": "conversational",
        "role_description": "A second step the documentation cites.",
        "prompt": {
            "template": "Reply OK.",
            "display_template": "Reply OK."
        },
        "has_knowledge_base": True,
        "context": ["docs-fixture"]
    },
)
```

```python
# out is the result, already unwrapped
{
    "process_flow_id": "6ab1945e7566f41b5c28e308",
    "agent_id": "6ab1968189fbbb1251ca6754"
}
```

## Parameters

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `agent` | object | Required | The agent to add (a `manifest.agents[]` entry minus `ref`; `title` required) or, with `agent_id`, the fields to change. See More details. A decider needs at least 2 conditions; skill ids must be in this project; an empty edit, or a field that cannot be edited in place, is refused. |
| `add_on_side_if_decider` | string | Optional | Required when `after_agent_id` is a decider agent: one of its condition names, not already wired. |
| `after_agent_id` | string | Optional | Add mode: agent id to insert after. Omit to prepend a skilled agent as the initial agent. Ignored when `agent_id` is set. |
| `agent_id` | string | Optional | Existing agent id to edit in place instead of adding one. `agent` is then a partial update. An agent in that flow; not found is reported as 502 here. |
| `flow_name` | string | Optional | Exact process flow name when the id is unknown. Names are not unique: a shared name fails and lists the ids. |
| `process_flow_id` | string | Optional | Process flow id. A process flow in this project. One of this or `flow_name` is required. |

**Errors**

- `UpstreamError` (502): a title in `agent.context_target_agent_ids` matches no agent; the error lists the names.

**Show full response**

A real response, captured from a live call to `update-process-flow`:

`flow` came back populated and is shown as `…` here, because the real value runs to
thousands of characters. Read it from your own call rather than from this page.

**Result of update-process-flow**

**REST API**

```json
{
  "lifecycle_id": "qlc_000183",
  "result": {
    "agent_id": "6ab1968189fbbb1251ca6754",
    "flow": "\u2026",
    "process_flow_id": "6ab1945e7566f41b5c28e308"
  }
}
```

**TypeScript SDK**

```ts
// the object you get back IS the result — there is no `.result` to read
{
  agent_id: '6ab1968189fbbb1251ca6754',
  flow: '…',
  process_flow_id: '6ab1945e7566f41b5c28e308'
}

// The TS SDK names these camelCase — lifecycleId, traceId — where Python uses
// lifecycle_id and trace_id. Both are non-enumerable: readable, but omitted by
// Object.keys() and JSON.stringify(). `result.lifecycleId` here is undefined.
result.lifecycleId // 'qlc_000183'
```

**Python SDK**

```python
# a dict subclass; `result` is already unwrapped
{
 "agent_id": "6ab1968189fbbb1251ca6754",
 "flow": "\u2026",
 "process_flow_id": "6ab1945e7566f41b5c28e308"
}

out.lifecycle_id  # 'qlc_000183' — an attribute, not a key
```

**More details**

**Refusals.** Before it runs, this tool refuses: neither `process_flow_id` nor `flow_name`; `agent.skills` ids that do not exist in the project; in add mode a missing `agent.title`, an unknown `type`, invalid agent settings, a skilled agent that is not runnable (no skills and not conversational) and a decider agent with fewer than 2 `conditions`; in edit mode an empty `agent`, and any non-editable field. A `context_target_agent_ids` title that matches no agent is raised only when the call runs, as an upstream error naming the unmatched titles.

**`agent` in full.** In add mode (no `agent_id`) it has the same shape as one `manifest.agents[]` entry, minus `ref` (there is only one agent): `title` is required, and `skills`, `next_agents` and `next` must be real existing ids, not manifest refs. In edit mode (`agent_id` set) `title` is not required: include only the fields you are changing, and every omitted field is left untouched. Editable in edit mode: `title`, `role`, `role_description`, `prompt`, `llm`, `skills`, `context`, `has_knowledge_base`, `is_preview_mode_enabled`, `triggers`, `context_required`, `context_target_agent_ids`, `conditions` (decider agent only), `context_access`, `agent_base_prompt`, `is_terminal_agent` (skilled agent only), `is_user_specific_task` (skilled agent only) and `registration_id` (interop agent only: repoints an external agent node at a different peer agent connection). Not editable in edit mode: `type` (a node type cannot change after creation), `next`, `next_agents` and `ref` (manifest-only). Passing one is refused before the call runs, and the error lists the editable fields.

This is the tool equivalent of the UI's **+** button. In add mode (no `agent_id`) the agent may be a `skilled_agent`, `decider_agent` or `interop_agent` (the external agent node), defaulting to `skilled_agent`. Positioning is add mode only: `after_agent_id` is the existing agent to insert after (omit it for a skilled agent to prepend it as the new initial agent; omit it for a decider agent only on an empty process flow), and `add_on_side_if_decider` is required when `after_agent_id` points at an existing decider agent.

Unlike `create-process-flow`'s manifest mode, which resolves `next_agents` against refs in the same call, this tool's `agent.next_agents` and `agent.next` values must be real existing ids already in the project and flow: fetch them with `get-process-flow` first if needed. `agent.skills` is always real existing skill ids, since there is no ref resolution and no Wexa tool creates a skill: call [`list-skills`](/docs/connectors/list-skills) to find them. The one exception is `agent.context_target_agent_ids`, which takes agent titles as they appear in the process flow, not ids: the tool resolves them for you and returns a clear error listing which names did not match.

In edit mode (`agent_id` set) `agent` is a partial update: only the fields you include change. Rewiring (`next`, `next_agents`) is not supported in place, because the backend has no such field on an in-place update. To rewire, remove the agent in the console and add it back: open the agent in the process flow editor and choose **Delete**, then **Remove from process flow**. No tool removes a single agent from a process flow.

The result carries `agent_id` and the updated `flow`, which the full response shows as `…` because the real value runs to thousands of characters. An agent this tool adds is not recorded as a version until you [`promote-flow`](/docs/automation/promote-flow).

In add mode, `add_on_side_if_decider` must be one of the existing decider's own condition names and that decision slot must not already be wired; the platform passes both positioning arguments on and the data service enforces them.

Marked consequential: it is audited in full, and a policy rule may hold it for a person to approve. See [Policy and approvals](/docs/governance/policy-and-approvals).

Every tool call works the same way: [How every tool call works](/docs/sdks-and-apis/how-every-tool-call-works).
