UpdateDraftVersion
Edits the draft with a partial update of its configuration; If-Match is required.
Members that are absent stay unchanged, nested objects are merged and lists are replaced whole.
To return a member to its default, name it in clear; naming a member that is not clearable, or
one the same request also sets, is refused with 400 VALIDATION_FAILED. Returns the whole draft
and its new ETag.
Not yet available. The SDKs have this operation, but no Caitlyn release serves it yet: a call gets 404. This page describes how it will work. GetAgentVersion is the one v3 operation served today.
| HTTP | PATCH /api/v3/agents/{agentId}/versions/draft |
| TypeScript | caitlyn.versions.updateDraft(agentId, { … }) |
| Python | caitlyn.versions.update_draft(agent_id, …) |
| Go | client.Versions.UpdateDraft(ctx, agentID, &caitlyn.UpdateDraftVersionInput{…}) |
| Rust | client.versions().update_draft(agent_id, UpdateDraftVersionInput { … }) |
| Stability | Generally available |
| Since | Caitlyn 5.115.0 |
| Retried by the SDKs | Yes, on 429, 503 and connection failures (Retries) |
Request
Section titled “Request”Member names are shown as they are in TypeScript and on the wire. Python and Rust use snake case (knowledge_base_id) and Go uses exported names (KnowledgeBaseID).
agentId is the method's first argument rather than an input field. ifMatch is a call option (TypeScript { ifMatch }, Go caitlyn.WithIfMatch, Rust .if_match(…); Python takes if_match=).
| Member | Type | Sent in | Description |
|---|---|---|---|
agentId (required) |
string | path | The agent whose draft is edited. 1 to 128 characters. |
ifMatch (required) |
string | header If-Match |
The draft's current ETag, from a Get or an earlier update. A stale value is refused with 412; a missing one with 428. 2 to 256 characters. |
identity |
string | body | The agent's identity statement, at most 4,000 characters. 0 to 4000 characters. |
instructions |
string | body | The agent's instructions, at most 100,000 characters. 0 to 100000 characters. |
model |
string | body | The model the agent answers with, from the agent models list. 1 to 256 characters. |
knowledgeBases |
list of KnowledgeBaseBinding | body | The knowledge bases the agent searches. Replaces the whole list. At most 50 items. |
attachments |
AttachmentSettings | body | Whether people may attach files in chat, and how many. |
contentSafety |
ContentSafety | body | The content safety setting applied to uploads and answers. |
deniedDocuments |
boolean | body | When true, the answer stream tells the caller which documents were withheld from them. Defaults to false. |
clear |
list of ClearableDraftField | body | Draft members to return to their default. |
Response
Section titled “Response”Status 200.
| Member | Type | Sent in | Description |
|---|---|---|---|
requestId |
string | header Caitlyn-Request-Id |
The request's identifier. The same value is in every error body; quote it to support. 1 to 128 characters. |
apiVersion |
string | header Caitlyn-Api-Version |
The server's model version, for example 5.120.0. The SDKs keep the latest value they have seen and refuse to call operations or send members newer than it. 5 to 32 characters. Pattern ^[0-9]+\.[0-9]+\.[0-9]+$. |
etag (required) |
string | header ETag |
The version's current ETag; send it as If-Match to change it. 2 to 256 characters. |
agentId (required) |
string | body | The agent the version belongs to. 1 to 128 characters. |
name (required) |
string | body | draft, production or the version's name. 1 to 63 characters. Pattern ^[a-z0-9][a-z0-9._-]{0,62}$. |
source |
VersionSource | body | How a pinned or published version was made. Absent on the draft. |
basedOn |
string | body | The version this one was made from, where there is one. 1 to 63 characters. Pattern ^[a-z0-9][a-z0-9._-]{0,62}$. |
matchesProduction |
boolean | body | True when this version's configuration is the one production currently answers with. Defaults to false. |
createdAt (required) |
timestamp (RFC 3339) | body | When the version was made; for the draft, when it was first created. |
note |
string | body | The note written when the version was pinned or published. 0 to 2000 characters. |
configuration (required) |
AgentConfiguration | body | The version's configuration. |
Every result also keeps the JSON it was read from, unknown fields included, as raw, and the response metadata (HTTP status, request ID, ETag).
Permissions
Section titled “Permissions”- Permission:
agents.update. - Checked on: the agent named by
agentId. The draft is checked too, so the caller also needs the agent's version access grant. - Who can call it: people (signed in, or a personal access token), API clients (machine sign-in).
The checks run in this order: who is calling, the workspace and the permission (each refused with 403), then the record (404, which is also what a record the caller may not see returns). Errors explains why.
Errors
Section titled “Errors”The SDKs raise the error class for the response's HTTP status, whether or not the operation lists it (Errors). The statuses this operation returns:
| Status | Error | Codes | When |
|---|---|---|---|
| 400 | ValidationError |
VALIDATION_FAILED, UNKNOWN_FIELD, UNKNOWN_FILTER, UNKNOWN_SORT, INVALID_CURSOR |
The request is malformed: a member is missing, the wrong type or out of range; it has a member, filter or sort field the operation does not define; or its cursor is not valid for this list. |
| 401 | UnauthenticatedError |
UNAUTHENTICATED |
No credential was sent, or it is expired or not valid. |
| 403 | PermissionDeniedError |
PERMISSION_DENIED, NOT_AVAILABLE_TO_CREDENTIALS, PRIMARY_WORKSPACE_REQUIRED |
The caller may not call this operation: it lacks the permission, its credential type may not call it, or the operation is served only on the organisation's primary workspace. Decided before any record is read. A record the caller may not see is 404 instead. |
| 404 | NotFoundError |
NOT_FOUND |
The record does not exist, or the caller may not see it. The two are deliberately the same, so identifiers cannot be probed. |
| 412 | PreconditionFailedError |
PRECONDITION_FAILED |
If-Match does not match the resource's current ETag: it changed since the caller read it. |
| 428 | PreconditionRequiredError |
PRECONDITION_REQUIRED |
The update sent no If-Match header; read the resource and send its ETag. |
| 429 | TooManyRequestsError |
RATE_LIMITED |
Too many requests; wait for Retry-After and try again. Retried by the SDKs. |
| 500 | InternalError |
INTERNAL |
An unexpected server failure. Not retried by the SDKs. |
| 503 | ServiceUnavailableError |
SERVICE_UNAVAILABLE |
A dependency is briefly unavailable, or the request's audit event could not be recorded; wait for Retry-After and try again. Retried by the SDKs. |
Examples
Section titled “Examples”Change the instructions and clear the identity
Section titled “Change the instructions and clear the identity”import { Caitlyn } from '@caitlyn-ai/sdk';
const caitlyn = new Caitlyn({ workspace: 'your-company.getcaitlyn.ai', auth: { mode: 'token', token: process.env.CAITLYN_TOKEN! } });
const result = await caitlyn.versions.updateDraft('GHT7YRWXPL', { instructions: 'Answer leave and pay questions. Quote the policy clause you relied on.', clear: ['identity'],}, { ifMatch: '"draft-41"' });console.log(result);import os
from caitlyn import Caitlynfrom caitlyn.auth import Token
caitlyn = Caitlyn(workspace="your-company.getcaitlyn.ai", auth=Token(os.environ["CAITLYN_TOKEN"]))
result = caitlyn.versions.update_draft( "GHT7YRWXPL", if_match='"draft-41"', instructions="Answer leave and pay questions. Quote the policy clause you relied on.", clear=["identity"],)print(result)package main
import ( "context" "encoding/json" "fmt" "log" "os"
caitlyn "github.com/caitlyn-ai/caitlyn-go")
func main() { ctx := context.Background() client, err := caitlyn.NewClient("your-company.getcaitlyn.ai", caitlyn.Token(os.Getenv("CAITLYN_TOKEN"))) if err != nil { log.Fatal(err) } result, err := client.Versions.UpdateDraft(ctx, "GHT7YRWXPL", &caitlyn.UpdateDraftVersionInput{ Instructions: caitlyn.Ptr("Answer leave and pay questions. Quote the policy clause you relied on."), Clear: []caitlyn.ClearableDraftField{caitlyn.ClearableDraftFieldIdentity}, }, caitlyn.WithIfMatch("\"draft-41\"")) if err != nil { log.Fatal(err) } out, _ := json.MarshalIndent(result, "", " ") fmt.Println(string(out))}use caitlyn_sdk::{Auth, ClearableDraftField, Client, UpdateDraftVersionInput};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let client = Client::new("your-company.getcaitlyn.ai", Auth::token(std::env::var("CAITLYN_TOKEN")?))?; let result = client .versions() .update_draft("GHT7YRWXPL", UpdateDraftVersionInput { instructions: Some("Answer leave and pay questions. Quote the policy clause you relied on.".into()), clear: Some(vec![ClearableDraftField::Identity]), ..Default::default() }) .if_match("\"draft-41\"") .await?; println!("{result:#?}"); Ok(())}The HTTP exchange:
PATCH /api/v3/agents/GHT7YRWXPL/versions/draft HTTP/1.1Host: your-company.getcaitlyn.aiAuthorization: Bearer <token>If-Match: "draft-41"Content-Type: application/json
{ "instructions": "Answer leave and pay questions. Quote the policy clause you relied on.", "clear": [ "identity" ]}HTTP/1.1 200ETag: "draft-42"Content-Type: application/json
{ "agentId": "GHT7YRWXPL", "name": "draft", "matchesProduction": false, "createdAt": "2026-11-02T03:04:05.000Z", "configuration": { "instructions": "Answer leave and pay questions. Quote the policy clause you relied on.", "model": "claude-sonnet-5-5" }}The draft changed since it was read
Section titled “The draft changed since it was read”import { Caitlyn, PreconditionFailedError } from '@caitlyn-ai/sdk';
const caitlyn = new Caitlyn({ workspace: 'your-company.getcaitlyn.ai', auth: { mode: 'token', token: process.env.CAITLYN_TOKEN! } });
try { const result = await caitlyn.versions.updateDraft('GHT7YRWXPL', { instructions: 'Answer leave questions.', }, { ifMatch: '"draft-40"' }); console.log(result);} catch (err) { if (!(err instanceof PreconditionFailedError)) throw err; console.error(err.status, err.code, err.detail, err.requestId);}import os
from caitlyn import Caitlyn, PreconditionFailedErrorfrom caitlyn.auth import Token
caitlyn = Caitlyn(workspace="your-company.getcaitlyn.ai", auth=Token(os.environ["CAITLYN_TOKEN"]))
try: result = caitlyn.versions.update_draft( "GHT7YRWXPL", if_match='"draft-40"', instructions="Answer leave questions.", ) print(result)except PreconditionFailedError as err: print(err.status, err.code, err.detail, err.request_id)package main
import ( "context" "encoding/json" "errors" "fmt" "log" "os"
caitlyn "github.com/caitlyn-ai/caitlyn-go")
func main() { ctx := context.Background() client, err := caitlyn.NewClient("your-company.getcaitlyn.ai", caitlyn.Token(os.Getenv("CAITLYN_TOKEN"))) if err != nil { log.Fatal(err) } result, err := client.Versions.UpdateDraft(ctx, "GHT7YRWXPL", &caitlyn.UpdateDraftVersionInput{ Instructions: caitlyn.Ptr("Answer leave questions."), }, caitlyn.WithIfMatch("\"draft-40\"")) if err != nil { var apiErr *caitlyn.PreconditionFailedError if errors.As(err, &apiErr) { fmt.Println(apiErr.Status, apiErr.Code, apiErr.Detail, apiErr.RequestID) return } log.Fatal(err) } out, _ := json.MarshalIndent(result, "", " ") fmt.Println(string(out))}use caitlyn_sdk::{Auth, Client, UpdateDraftVersionInput};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let client = Client::new("your-company.getcaitlyn.ai", Auth::token(std::env::var("CAITLYN_TOKEN")?))?; let outcome: Result<(), caitlyn_sdk::Error> = async { let result = client .versions() .update_draft("GHT7YRWXPL", UpdateDraftVersionInput { instructions: Some("Answer leave questions.".into()), ..Default::default() }) .if_match("\"draft-40\"") .await?; println!("{result:#?}"); Ok(()) } .await; match outcome { Err(caitlyn_sdk::Error::PreconditionFailed(err)) => eprintln!("{} {} {:?} {:?}", err.status, err.code, err.detail, err.request_id), other => other?, } Ok(())}The HTTP exchange:
PATCH /api/v3/agents/GHT7YRWXPL/versions/draft HTTP/1.1Host: your-company.getcaitlyn.aiAuthorization: Bearer <token>If-Match: "draft-40"Content-Type: application/json
{ "instructions": "Answer leave questions."}HTTP/1.1 412Content-Type: application/problem+json
{ "type": "https://build.caitlyn.ai/errors/PRECONDITION_FAILED", "title": "The resource has changed", "status": 412, "code": "PRECONDITION_FAILED", "detail": "The draft of agent GHT7YRWXPL changed since you read it. Read it again and reapply your change.", "requestId": "01JC9Q2W7R4M0H2T6N1S8V3B5Y"}
