Skip to content

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)

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.

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).

  • 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.

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.

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 Caitlyn
from 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.1
Host: your-company.getcaitlyn.ai
Authorization: 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 200
ETag: "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"
}
}
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, PreconditionFailedError
from 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.1
Host: your-company.getcaitlyn.ai
Authorization: Bearer <token>
If-Match: "draft-40"
Content-Type: application/json
{
"instructions": "Answer leave questions."
}
HTTP/1.1 412
Content-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"
}