API: Open Chat Studio v2 Description: Build, deploy and monitor chatbots. TAG: Pipelines Description: Discover the node types a pipeline may contain and the resource ids it may reference, and edit a chatbot's pipeline a node at a time. ENDPOINTS: POST /api/v2/chatbots/{id}/pipeline/edges/ Summary: Wire Pipeline Nodes Description: Add edges to the chatbot's working (draft) pipeline, wiring one node's output handle to another node's input. The wires go in a **list** under `wires` — a single wire is a list of one, and at most 100 go in one call — and a call carrying several is all or nothing: if any one of them is refused, none of them are stored and the pipeline is left exactly as the call found it. So a whole branch lands in one call with no half-wired state to unpick: ```json { "wires": [ {"source": "RouterNode-9f8e7", "target": "LLMResponseWithPrompt-d3e4f", "source_handle": "output_0"}, {"source": "RouterNode-9f8e7", "target": "CodeNode-a1b2c", "source_handle": "output_1"}, {"source": "CodeNode-a1b2c", "target": "EndNode-b2c3d"} ] } ``` `source` and `target` are enough for most nodes: a handle you leave out means the only one the node has. A router is the exception — it exposes one handle per branch, so name the branch to wire in `source_handle`. No node is moved on the canvas. Each edge's `id` is assigned by the server and cannot be chosen. It is what the `pipeline_edge_delete` endpoint takes, and `edges` reports them in the order the body named them. Wiring a pair that is already wired the same way is refused rather than stored twice, so a wire is safe to retry. A wire that leaves the *graph* wrong is not refused: it lands, and comes back in `pipeline_errors` for you to repair. Parameters: - id (path, string (required)): Chatbot ID Request Body: Content: application/json Schema: WireBody Responses: 201: Content: application/json Schema: EdgeWrite 400: Nothing was wired. Refusals come back under `wires`, **keyed by the position in the list of the wire at fault** — so one call reports everything wrong with it, and a wire that is fine is absent rather than empty. Each wire's entry is in turn keyed by the field at fault: a `source` or `target` that is not a node in this pipeline, a `source_handle` the source node does not offer (or a missing one where the node offers a choice), a `target_handle` that is not the target's input, a server-assigned key the client tried to set (`id`), or an unrecognised key on the wire. ```json { "wires": { "0": {"source": ["This pipeline has no node 'CodeNode-nope1'."]}, "2": {"source_handle": ["..."]} } } ``` A duplicate edge is reported under the wire's `non_field_errors`, with the existing edge's id between single quotes: > These nodes are already wired this way, by edge '<edge_id>'. Nothing was changed. So a client whose first attempt's response it never saw can recover the edge id rather than re-reading the pipeline. A body wiring the same pair twice is refused the same way, naming the index of the first of the two. A `source` that offers no output handles at all is refused under `source`, and the message says whether that is fixable: a router with no `keywords` set yet needs a PATCH first, whereas the End node can never be a source. A `wires` that is not a list, is an empty one, or names more than 100 wires is refused as a message on the field itself, before any wire in it is looked at; a body that is not an object at all is refused under `non_field_errors`. 403: The caller is authenticated but not authorised to modify this chatbot: either its role lacks permission to change chatbots, or it is a machine (client-credentials) token whose application is not authorised for this chatbot. 404: No such chatbot. Security: OAuth2 - Required scopes: chatbots:write apiKeyAuth tokenAuth DELETE /api/v2/chatbots/{id}/pipeline/edges/{edge_id}/ Summary: Unwire two Pipeline Nodes Description: Remove an edge from the chatbot's working (draft) pipeline. Both nodes stay, and stay where they are on the canvas. The hole this leaves is reported rather than refused: unwiring usually breaks the path to the End node, which comes back under `pipeline_errors.node[].root` for you to repair, and both ends of the edge reappear in `unwired_handles`. Repeating a delete answers 404, so a retry cannot disturb the graph the first call left. Parameters: - edge_id (path, string (required)): The edge's server-assigned id, as returned by a wire or by the `chatbot_inspect` endpoint. - id (path, string (required)): Chatbot ID Responses: 200: Content: application/json Schema: PipelineWrite 403: The caller is authenticated but not authorised to modify this chatbot: either its role lacks permission to change chatbots, or it is a machine (client-credentials) token whose application is not authorised for this chatbot. 404: No such chatbot, or no such edge in this chatbot's pipeline. Security: OAuth2 - Required scopes: chatbots:write apiKeyAuth tokenAuth POST /api/v2/chatbots/{id}/pipeline/nodes/ Summary: Add a Pipeline Node Description: Add a node to the chatbot's working (draft) pipeline. `type` alone is enough: the node is created with that type's defaults, which the response reports and which you can then change with PATCH. It is not wired to anything, so it appears in `unwired_handles` until you connect it — that is advisory, not an error. The node's `node_id` and its position on the canvas are assigned by the server and cannot be chosen. What `params` may hold depends on `type`, so the examples below show a full body for each type. The `pipeline_node_retrieve` endpoint is the authoritative JSON Schema for one type, and the `pipeline_node_options` endpoint serves the ids its resource params may name. Parameters: - id (path, string (required)): Chatbot ID Request Body: Content: application/json Schema: NodeCreate Responses: 201: Content: application/json Schema: NodeWrite 400: The body is not one this endpoint can act on. Errors are keyed by the field at fault, with param-level errors nested under `params`: an unrecognised body key, a server-assigned key the client tried to set (`node_id`, `position`), or a param naming a resource this team cannot reach. A param the type does not declare is dropped rather than refused, and a param whose value the type cannot parse is stored and reported in `pipeline_errors` — so a node can be built up over several calls. 403: The caller is authenticated but not authorised to modify this chatbot: either its role lacks permission to change chatbots, or it is a machine (client-credentials) token whose application is not authorised for this chatbot. 404: No such chatbot, or no such node `type`. An unknown type names the valid ones in `valid_types`; the Start and End types are refused here too, since the server creates those with the pipeline. Security: OAuth2 - Required scopes: chatbots:write apiKeyAuth tokenAuth PATCH /api/v2/chatbots/{id}/pipeline/nodes/{node_id}/ Summary: Edit a Pipeline Node Description: Change a node's params or its label. Params merge key by key, so send only what you want to change; everything else is left as it is. Editing a router's `keywords` regenerates its output handles — they are positional, so `output_0` serves `keywords[0]` — and the response carries the new list. The node's edges follow their keyword: dropping a keyword deletes the edge that served it, and a renamed keyword counts as one branch gone and another new. Handle names are not stable across a keyword edit, so re-read `output_handles` after one. What `params` may hold depends on the node's type, not on the verb: the `POST` examples name every param of each type, and the examples here are partial bodies instead. The `pipeline_node_retrieve` endpoint is the authoritative JSON Schema for one type, and the `pipeline_node_options` endpoint serves the ids its resource params may name. Parameters: - id (path, string (required)): Chatbot ID - node_id (path, string (required)): The node's server-assigned id, as returned by a write or by the `chatbot_inspect` endpoint. Request Body: Content: application/json Schema: PatchedNodeUpdate Responses: 200: Content: application/json Schema: NodeWrite 400: The body is not one this endpoint can act on. Errors are keyed by the field at fault, with param-level errors nested under `params`: an unrecognised body key, a server-assigned key the client tried to set (`node_id`, `position`), or a param naming a resource this team cannot reach. A param the type does not declare is dropped rather than refused, and a param whose value the type cannot parse is stored and reported in `pipeline_errors` — so a node can be built up over several calls. 403: The caller is authenticated but not authorised to modify this chatbot: either its role lacks permission to change chatbots, or it is a machine (client-credentials) token whose application is not authorised for this chatbot. 404: No such chatbot or node, or the node is of a type this API does not publish and so cannot describe the params of. 409: The node is part of the pipeline's structure (the Start or End node) and cannot be edited or deleted through the API. Security: OAuth2 - Required scopes: chatbots:write apiKeyAuth tokenAuth DELETE /api/v2/chatbots/{id}/pipeline/nodes/{node_id}/ Summary: Remove a Pipeline Node Description: Remove a node from the chatbot's working (draft) pipeline, along with every edge that referenced it — you do not have to unwire it first. The hole this leaves is reported rather than refused: unsplicing a node usually breaks the path to the End node, which comes back in `pipeline_errors` for you to repair. The Start and End nodes cannot be removed — nor edited. They are part of the pipeline's structure and cannot be added back through the API. Parameters: - id (path, string (required)): Chatbot ID - node_id (path, string (required)): The node's server-assigned id, as returned by a write or by the `chatbot_inspect` endpoint. Responses: 200: Content: application/json Schema: PipelineWrite 403: The caller is authenticated but not authorised to modify this chatbot: either its role lacks permission to change chatbots, or it is a machine (client-credentials) token whose application is not authorised for this chatbot. 404: No such chatbot or node. 409: The node is part of the pipeline's structure and cannot be deleted. Security: OAuth2 - Required scopes: chatbots:write apiKeyAuth tokenAuth GET /api/v2/pipeline/nodes/ Summary: List Pipeline Node Types Description: The node types a pipeline can contain, with the JSON Schema of each one's `params` and the outputs its edges leave by. Deprecated types are omitted. Static per deploy: revalidate a cached copy with `If-None-Match` against the `ETag`. Responses: 200: Content: application/json Schema: array of NodeType Security: OAuth2 - Required scopes: chatbots:read apiKeyAuth tokenAuth GET /api/v2/pipeline/nodes/{node_type}/ Summary: Retrieve a Pipeline Node Type Description: One node type, as `/pipeline/nodes/` serves it. A deprecated type is not retrievable either -- it is not something a client may build. Static per deploy: revalidate a cached copy with `If-None-Match` against the `ETag`. Parameters: - node_type (path, string (required)): The type's name, as carried in the `type` field of a `/pipeline/nodes/` entry. Responses: 200: Content: application/json Schema: NodeType 404: No such node type, or the type is deprecated and therefore not listed. Content: application/json Schema: NodeTypeNotFound Security: OAuth2 - Required scopes: chatbots:read apiKeyAuth tokenAuth GET /api/v2/pipeline/options/ Summary: List Pipeline Node Options Description: The values each node param accepts, scoped to the API key's team. A key holds the values for the node param of the same name: write one of `source_material`'s entries into a node's `source_material_id`, one of `collection_index`'s into `collection_index_ids`. The variable lists are the exception, because no param is named for the list it draws on and two different params are both named `prompt`. Jinja params -- `template_string` and `SendEmail`'s fields -- draw from `template_variables` and are written double-braced (`{{input}}`); an LLM node's `prompt` draws from `llm_prompt_variables` and a router's from `router_prompt_variables`, both written single-braced (`{source_material}`). Fetch `/pipeline/options/{node_type}/` to receive only the list that applies -- the sets are not interchangeable. Responses: 200: Content: application/json Schema: PipelineOptions Security: OAuth2 - Required scopes: chatbots:read apiKeyAuth tokenAuth GET /api/v2/pipeline/options/{node_type}/ Summary: List One Node Type's Options Description: The keys this node type's params can reference, plus its `default_llm_provider` where it has one -- `/pipeline/options/` cut down to what building this one node needs. Which of the three variable lists applies is settled here rather than left to the client: the sets are not interchangeable. Parameters: - node_type (path, string (required)): The type's name, as carried in the `type` field of a `/pipeline/nodes/` entry. Responses: 200: Content: application/json Schema: PipelineOptions 404: No such node type, or the type is deprecated and therefore not listed. Content: application/json Schema: NodeTypeNotFound Security: OAuth2 - Required scopes: chatbots:read apiKeyAuth tokenAuth SCHEMAS: EdgeCreate: - source: string (required) - target: string (required) - source_handle: string - target_handle: string EdgeWrite: - edges: array of WrittenEdge (required) - pipeline_valid: boolean (required) - pipeline_errors: PipelineBuildErrors (required) - unwired_handles: object (required) InspectOutputHandle: - handle: string (required) - label: string (required) LlmProviderModelOption: - value: integer (required) - label: string (required) - type: string (required) - max_token_limit: integer NodeCreate: - type: string (required) - label: string - params: object NodeType: - type: string (required) - description: string (required) - documentation_url: string - outputs: Unknown (required) - schema: object (required) NodeTypeNotFound: - detail: string (required) - valid_types: array of string (required) NodeWrite: - node: WrittenNode (required) - pipeline_valid: boolean (required) - pipeline_errors: PipelineBuildErrors (required) - unwired_handles: object (required) PatchedNodeUpdate: - label: string - params: object PipelineBuildErrors: - node: object (required) - edge: array of string (required) - pipeline: array of string (required) PipelineOptions: - llm_provider_id: array of ProviderOption - llm_provider_model_id: array of LlmProviderModelOption - synthetic_voice_id: array of SyntheticVoiceOption - source_material: array of ResourceOption - collection: array of ResourceOption - collection_index: array of ResourceOption - tools: array of ToolOption - custom_actions: array of ToolOption - built_in_tools: object - tool_config: object - template_variables: array of PromptVariable - llm_prompt_variables: array of PromptVariable - router_prompt_variables: array of PromptVariable - default_llm_provider: Unknown PipelineWrite: - pipeline_valid: boolean (required) - pipeline_errors: PipelineBuildErrors (required) - unwired_handles: object (required) PromptVariable: - label: string (required) - description: string (required) ProviderOption: - value: integer (required) - label: string (required) - type: string (required) ResourceOption: - value: integer (required) - label: string (required) SyntheticVoiceOption: - value: integer (required) - label: string (required) - type: string (required) - provider_id: integer (required) ToolOption: - value: string (required) - label: string (required) WireBody: - wires: array of EdgeCreate (required) WrittenEdge: - id: string (required) - source: string (required) - target: string (required) - source_handle: string (required) - target_handle: string (required) WrittenNode: - node_id: string (required) - type: string (required) - label: string (required) - params: object (required) - output_handles: array of InspectOutputHandle (required) SECURITY: - OAuth2 - Authorization Code Flow (authorization url: /o/authorize/, token url: /o/token/) - API Key authentication (header: X-api-key) - HTTP bearer authentication