Update Staged Agent Settings
Update this agent's staged (not necessarily published) prompt, LLM provider binding, conversation storage, evaluation, widget, and custom-variable settings. Reflects unpublished staged state - see GET /agents/{agent_id}/versions/latest for what's actually live.
Authorization
OAuth2AuthorizationCodeBearer In: header
Path Parameters
uuidQuery Parameters
trueRequest Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
The Versioned-surface subset of what PATCH /agents/{id} used to accept in one schema - system_prompt, first_message, llm_provider_id, conversation_storage_enabled, evaluation settings, widget settings, and custom_variables. Every field independently optional, same omitted-means-don't-touch contract as every other patch schema in this file. voice is deliberately not here - see PATCH /agents/{id}/stage/voice's own schema instead.
conversation_storage_enabled is the owner-facing off switch the conversation-persistence design spec's "on by default" scope decision rests on - it is the mitigation that makes storing full transcripts by default acceptable, so it has to be writable from this route.
evaluation_enabled, extraction_field_defs, and evaluation_criteria_defs
are independently optional - each may be sent alone or omitted. This
works because Agent.extraction_field_defs and Agent.evaluation_criteria_defs
are non-nullable JSONB columns with an empty-list default: the database
never distinguishes "unset" from "empty", so an omitted field here
unambiguously means "don't touch it" and an explicit [] unambiguously
means "clear it" - there is no third state to disambiguate, so no
model_validator is needed to detect a partial submission.
widget_enabled, widget_public, widget_accent_color, widget_orb_color_1,
and widget_orb_color_2 follow the same independently-optional pattern
as the evaluation-settings group above - none is ambiguous between
"omitted" and "cleared", since all three color columns are genuinely
nullable and the two booleans default False rather than needing a
distinct "unset" state.
widget_allowed_domains follows the list-field pattern instead (like
extraction_field_defs/evaluation_criteria_defs above): the column is a
non-nullable JSONB with an empty-list default, so an omitted field here
unambiguously means "don't touch it" and an explicit [] unambiguously
means "clear/unrestrict it" - no validator needed.
widget_style_overrides follows the same list-field pattern as
widget_allowed_domains (non-nullable JSONB dict, empty-dict default -
omitted means "don't touch it", {} means "reset to the widget's own
built-in defaults"), with an added key-allowlist validator since this
dict's keys are a styling convention shared with two renderers (see
WIDGET_STYLE_OVERRIDE_KEYS above), not free-form.
widget_embed_key is deliberately absent from this schema: it is never
owner-supplied, only generated/rotated via its own dedicated action
route (POST /agents/{agent_id}/stage/widget/regenerate-key) - an
owner-chosen value would be guessable, defeating its purpose as an
unguessable public identifier.
custom_variables follows the same list-field pattern as
widget_allowed_domains (non-nullable JSONB dict, empty-dict default -
omitted means "don't touch it", {} means "clear everything"), with
an added key-grammar validator: names must be plain identifiers
(letters, digits, underscore only), since a "." or "[" would be
ambiguous with the {{config.name}} path syntax the value is
interpolated into at workflow run time.
llm_provider_id binds (or rebinds) the agent's LLM provider. Omitting it leaves the setting unchanged; sending null restores inheritance from the platform runtime default.
llm_provider_id selects the LLM provider by id among the providers visible to the agent's organisation (platform-offered plus its own); a null id restores inheritance. llm_model is the vendor model id; an explicit null resets it to the provider's default, and changing the provider without sending a model resets it too, since a model id belongs to the provider that serves it. Provider and model form one routing decision: the model is checked against the route the org's binding gives the provider, and both are written in one transaction.
See docs/superpowers/specs/2026-09-05-agent-config-versioning-design.md's API surface section: this is the "stage" half of what PATCH /agents/{id} used to do in one call, the other half (deployment_id, a2a_open, a2a_discoverable) staying on that original endpoint since neither is part of the Versioned surface.
Response Body
application/json
application/json
application/json
application/json
application/json
curl -X PATCH "https://example.com/v1/agents/497f6eca-6276-4993-bfeb-53cbbbba6f08/stage/agent" \ -H "Content-Type: application/json" \ -d '{}'{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "name": "string", "deployment_id": "6ef0ac85-9892-4664-a2a5-58bf2af5a8a6", "llm_provider_id": "01f100ab-eef6-496a-9799-f8fbf49c6288", "llm_model": "string", "org_id": "a40f5d1f-d889-42e9-94ea-b9b33585fc6b", "event_org_id": "38381290-8ff7-413c-a0d2-e750a21c5122", "system_prompt": "string", "first_message": "string", "a2a_open": true, "a2a_discoverable": true, "conversation_storage_enabled": true, "voice": { "pipeline_mode": "string", "language": "string", "silence_end_call_timeout_secs": 0, "cascade": { "stt_provider_id": "d4a67d44-bb59-4313-9013-67e80ee06719", "stt_model": "string", "tts_provider_id": "548bbff5-1048-4ad8-a0df-cd333e015434", "tts_model": "string", "tts_voice_id": "11bfaa1c-5a0e-4a01-aa9f-9c79a298dae1", "enable_interruptions": true, "min_words_before_interruption": 0, "vad_confidence": 0, "vad_start_secs": 0, "vad_stop_secs": 0, "vad_min_volume": 0, "turn_model": "string", "eagerness": "string", "turn_timeout_secs": 0, "asr_keywords": [ "string" ], "filter_background_speech": true, "input_audio_format": "string", "interruption_ignore_terms": [ "string" ], "transcribe_on_disabled_interruptions": true }, "s2s": { "realtime_provider_id": "15347105-df5d-4c64-b3e4-5148a8ebb81c", "realtime_model": "string", "decision_provider_id": "c9798dc0-90d8-4341-8778-6ff2649d592b", "decision_model": "string", "voice_id": "2235c634-e4db-4ad6-aa15-066fb69b1c8f", "turn_detection_type": "string", "turn_threshold": 0, "prefix_padding_ms": 0, "silence_duration_ms": 0, "eagerness": "string" } }, "has_workflow": false, "state_schema": {}, "evaluation_enabled": true, "extraction_field_defs": [ { "name": "string", "type": "string", "description": "string", "enum_values": [ "string" ] } ], "evaluation_criteria_defs": [ { "name": "string", "description": "string" } ], "pii_redaction_enabled": true, "widget_enabled": true, "widget_public": true, "widget_embed_key": "string", "widget_accent_color": "string", "widget_orb_color_1": "string", "widget_orb_color_2": "string", "widget_allowed_domains": [ "string" ], "widget_placement": "string", "widget_style_overrides": { "property1": "string", "property2": "string" }, "custom_variables": { "property1": "string", "property2": "string" }, "max_conversation_duration_seconds": 0, "max_duration_message": "string", "conversation_retention_days": 0, "workflow_run_io_retention": 30, "recording_enabled": true, "audio_retention_days": 0, "client_events": [ "string" ], "chat_mode_enabled": true, "dtmf_enabled": true, "visitor_thread_window_minutes": 0, "visitor_memory_enabled": true, "visitor_memory_scope": "string", "visitor_memory_days": 0, "visitor_memory_include_soft_links": true, "emotion_enabled": false, "emotion_engine": "string", "attachments_enabled": false, "vision_mode": "auto", "attachment_retention": "conversation", "vision_analysis_provider_id": "38a9be8b-83a5-41fc-b444-d5d6911c3198", "vision_analysis_model": "string", "vision_analysis_instructions": "string", "max_attachments_per_message": 5, "max_attachments_per_conversation": 20, "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z", "can_modify": false, "run": { "token": "string", "run_id": "string", "expires_at": "2019-08-24T14:15:22Z", "deadline_at": "2019-08-24T14:15:22Z" }, "model_warnings": []}{ "detail": "string", "code": "string"}{ "detail": "string", "code": "string"}{ "detail": "string", "code": "string"}{ "detail": "string", "code": "string"}