---
title: "Generate Protocol Endpoint"
method: POST
path: "/protocols/generate"
tags: ["protocols"]
---

# Generate Protocol Endpoint

`POST /protocols/generate`

## Request

- Base URL: `https://api.ionworks.com`
- URL: `https://api.ionworks.com/protocols/generate`
- Auth: none declared

Generate a UCP protocol from a natural-language description.

When the description leaves a value undecided that would change what runs
on the channel, the response carries ``questions`` and no protocol. Answer
them and call again with the full ``clarifications`` history.

Any protocol that *is* returned has already passed the same validation as
``POST /protocols/validate`` and contains no unresolved ``input["..."]``
references, so it can be saved as an experiment template directly.

Parameters
----------
body : GenerateProtocolRequest
    The natural-language prompt, any answers already given, and an
    optional existing protocol to edit rather than replace.

Returns
-------
GenerateProtocolResponse
    Either the generated YAML with a suggested name, an explanation, and
    the assumptions made — or the questions needed to write it.

Raises
------
ExternalServiceError
    If the model could not produce a valid protocol.

## Request body

- GenerateProtocolRequest: Request body for generating a protocol from natural language.
  - `prompt` string, required: Plain-English description of the test to run, e.g. '1C/1C cycling for 200 cycles between 4.2 V and 2.5 V with 10-minute rests'.
  - `current_protocol` string, nullable: Optional existing protocol as UCP YAML. When supplied the prompt is treated as an edit to it rather than a fresh protocol.
  - `clarifications` ClarificationAnswer[]: Answers to questions a previous call returned, oldest first. Send the full history each time — the exchange is stateless.
    - `question` string, required
    - `answer` string, required
  - `cell_specification_id` string, nullable: Cell the test will run on. Its voltage limits, capacity and rate limits are given to the generator as facts, so it stops guessing (or asking) about them.
  - `skip_existing` boolean: Skip the saved-protocol search and write a new protocol. Set when the user rejects the suggested matches.
  - `allow_inputs` boolean: Allow the protocol to carry `input["..."]` references for values supplied per run, and offer parametric saved protocols as matches. Leave false where every value has to be pinned before the protocol can run, as it does for a scheduled test.

## Response `200`

Successful Response

- GenerateProtocolResponse: A protocol, the questions needed to write one, or an existing match. Exactly one branch is populated. Callers should check ``questions`` and ``matches`` before reading ``protocol_yaml`` — both leave it empty.
  - `protocol_yaml` string
  - `name` string
  - `explanation` string
  - `assumptions` string[]
  - `questions` ClarifyingQuestionResponse[]
    - `question` string, required
    - `why` string
    - `options` string[]
  - `matches` ExistingProtocolMatchResponse: A saved protocol that already does what was asked for.
    - `template_ids` string[], required
    - `note` string

## Other responses

- `422`: Validation Error

## Changes

- **2026-09-25** `2a3fc9725f80`: 1 info
  - added the new optional request property `allow_inputs`

[Operation history](https://skmtc.dev/ionworks/apis/ionworks-api/changes/protocols/generate/post.md)

---

[API](https://skmtc.dev/ionworks/apis/ionworks-api.md) - [All operations](https://skmtc.dev/ionworks/apis/ionworks-api/llms.txt) - [OpenAPI document](https://skmtc.dev/ionworks/apis/ionworks-api/revisions/abebbbc5f60a?raw)
