GetAgentVersion
Gets one version of an agent, with its full configuration and its ETag.
version is draft, production or the name of a pinned or published version. Production is
readable by everyone with access to the agent; the draft and pinned versions only by the people
and groups on the agent's version access grant, and anyone else gets 404.
| HTTP | GET /api/v3/agents/{agentId}/versions/{version} |
| TypeScript | caitlyn.versions.get({ … }) |
| Python | caitlyn.versions.get(…) |
| Go | client.Versions.Get(ctx, &caitlyn.GetAgentVersionInput{…}) |
| Rust | client.versions().get(GetAgentVersionInput { … }) |
| 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).
| Member | Type | Sent in | Description |
|---|---|---|---|
agentId (required) |
string | path | The agent's identifier. 1 to 128 characters. |
version (required) |
string | path | draft, production or a version name. 1 to 63 characters. Pattern ^[a-z0-9][a-z0-9._-]{0,62}$. |
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.read. - Checked on: the agent named by
agentId. The version inversionis checked too:productionneeds only access to the agent; the draft and pinned versions also need 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. |
| 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”Read the draft before editing it
Section titled “Read the draft before editing it”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.get({ agentId: 'GHT7YRWXPL', version: 'draft' });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.get(agent_id="GHT7YRWXPL", version="draft")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.Get(ctx, &caitlyn.GetAgentVersionInput{ AgentID: "GHT7YRWXPL", Version: "draft", }) if err != nil { log.Fatal(err) } out, _ := json.MarshalIndent(result, "", " ") fmt.Println(string(out))}use caitlyn_sdk::{Auth, Client, GetAgentVersionInput};
#[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() .get(GetAgentVersionInput { agent_id: "GHT7YRWXPL".into(), version: "draft".into(), }) .await?; println!("{result:#?}"); Ok(())}The HTTP exchange:
GET /api/v3/agents/GHT7YRWXPL/versions/draft HTTP/1.1Host: your-company.getcaitlyn.aiAuthorization: Bearer <token>HTTP/1.1 200ETag: "draft-41"Content-Type: application/json
{ "agentId": "GHT7YRWXPL", "name": "draft", "matchesProduction": false, "createdAt": "2026-11-02T03:04:05.000Z", "configuration": { "identity": "You are the HR assistant for Harbour Freight staff.", "instructions": "Answer leave and pay questions from the HR policy knowledge base.", "model": "claude-sonnet-5-5", "knowledgeBases": [ { "knowledgeBaseId": "KB4T7YQ2WL", "numberOfResults": 8, "minimumScore": 0.3 } ] }}A version name that does not exist, or that the caller may not see
Section titled “A version name that does not exist, or that the caller may not see”import { Caitlyn, NotFoundError } 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.get({ agentId: 'GHT7YRWXPL', version: 'v99' }); console.log(result);} catch (err) { if (!(err instanceof NotFoundError)) throw err; console.error(err.status, err.code, err.detail, err.requestId);}import os
from caitlyn import Caitlyn, NotFoundErrorfrom caitlyn.auth import Token
caitlyn = Caitlyn(workspace="your-company.getcaitlyn.ai", auth=Token(os.environ["CAITLYN_TOKEN"]))
try: result = caitlyn.versions.get(agent_id="GHT7YRWXPL", version="v99") print(result)except NotFoundError 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.Get(ctx, &caitlyn.GetAgentVersionInput{ AgentID: "GHT7YRWXPL", Version: "v99", }) if err != nil { var apiErr *caitlyn.NotFoundError 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, GetAgentVersionInput};
#[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() .get(GetAgentVersionInput { agent_id: "GHT7YRWXPL".into(), version: "v99".into(), }) .await?; println!("{result:#?}"); Ok(()) } .await; match outcome { Err(caitlyn_sdk::Error::NotFound(err)) => eprintln!("{} {} {:?} {:?}", err.status, err.code, err.detail, err.request_id), other => other?, } Ok(())}The HTTP exchange:
GET /api/v3/agents/GHT7YRWXPL/versions/v99 HTTP/1.1Host: your-company.getcaitlyn.aiAuthorization: Bearer <token>HTTP/1.1 404Content-Type: application/problem+json
{ "type": "https://build.caitlyn.ai/errors/NOT_FOUND", "title": "Not found", "status": 404, "code": "NOT_FOUND", "detail": "Agent GHT7YRWXPL has no version v99.", "requestId": "01JC9Q2W7R4M0H2T6N1S8V3B5X"}
