Assistant Config API
Version-control instructions and conversation stages, and push them from CI. The API sets the same assistant fields as the dashboard, with the same length limits and publish-snapshot lifecycle. Per-request injection addresses those same instruction fields and screens the resulting text with the publish safety gate.
The two instruction fields are customInstructions (System Instructions) and
openingInstructions (opening / greeting instructions). Ragtime compiles these
fields into the assistant’s prompt alongside its platform rules and configured
capabilities.
Authentication is a project API key (Dashboard → Integrations → API keys) sent as a bearer token. Two scopes govern access:
| Scope | Grants |
|---|---|
assistant:read |
GET the configuration |
assistant:write |
PATCH the configuration |
These are deliberately separate from ingest, so a content-ingestion credential
can never rewrite the prompt that governs the assistant’s behaviour. See
API Keys for issuing and scoping keys.
GET /api/v1/projects/{projectId}/assistant
Section titled “GET /api/v1/projects/{projectId}/assistant”curl https://your-ragtime-instance.com/api/v1/projects/{projectId}/assistant \ -H "Authorization: Bearer rt_live_..."{ "assistant": { "customInstructions": "You are a calm, precise guide…", "openingInstructions": "Greet the user and ask what they are working on.", "openingInstructionsLiteral": false, "openingInstructionsUseRag": false, "conversationStages": null, "updatedAt": "2026-07-30T09:12:44.000Z" }}This returns the editable configuration. Public links serve the published snapshot, which may differ from the current draft.
PATCH /api/v1/projects/{projectId}/assistant
Section titled “PATCH /api/v1/projects/{projectId}/assistant”Partial by design — only the fields you send are written, so a pipeline that owns the instruction fields does not have to round-trip settings it does not manage.
curl -X PATCH https://your-ragtime-instance.com/api/v1/projects/{projectId}/assistant \ -H "Authorization: Bearer rt_live_..." \ -H "Content-Type: application/json" \ -d '{ "customInstructions": "You are a calm, precise guide…", "openingInstructions": "Greet the user and ask what they are working on.", "conversationStages": { "firstStageId": "intake", "stages": [ { "id": "intake", "name": "Intake", "purpose": "Find out what the user is trying to achieve", "instructions": "Establish what the user is trying to achieve…", "actionIds": [], "moves": [{ "id": "m1", "toStageId": "capabilities", "when": "They are ready to scope capabilities" }] }, { "id": "capabilities", "name": "Capabilities", "instructions": "One relevance threshold applies to the whole list…", "actionIds": [], "moves": [] } ] } }'Accepted fields: customInstructions, openingInstructions,
openingInstructionsLiteral, openingInstructionsUseRag, conversationStages.
Anything else is rejected rather than ignored, so a typo’d field in a deploy
script fails loudly instead of silently doing nothing. An empty body is also
rejected. A successful response uses the same { "assistant": ... } shape as
GET.
Validation:
customInstructions≤ 7,500 characters;openingInstructions≤ 2,000. These are the same limits used by the dashboard and instruction injection, defined in$lib/shared/limits.- A stage has these fields:
id,name,purpose(optional),instructions,actionIdsandmoves. A move hasid,toStageIdandwhen, the condition for moving on. Unknown fields in a stage or a move are a 400, like unknown fields in the body. - Stages have limits: a stage’s
nameis at most 100 characters, itspurposeand each move’swhenat most 500, and itsinstructionsat most 2,000. A stage is added on top of the full main instructions, so it is a refinement and not a second prompt. Going over a limit is a 400. - Structural problems are errors, not warnings: a missing first stage or a move to a stage that does not exist is a 400. (The dashboard can show these next to the editor; an API caller has no editor to look at.)
conversationStages: null, or an emptystageslist, clears the stages and returns the assistant to single-prompt behaviour.conversationStages.enabled: falsekeeps the stages but turns them off. Leave it out for on.- Each stage can carry a
purpose, a sentence on what the stage is for. The dashboard sends one, and the model reads it as part of the handoff tool. actionIdslists action IDs. The dashboard offers the actions whose Start says “When the AI decides”.
The stage review
Section titled “The stage review”Stages are part of the draft, like the instructions: visitors get them at the
next publish. A PATCH that includes conversationStages reviews the stage text
against the main instructions before it writes, so you see the verdict on the
draft. The
review looks for text that conflicts with the main instructions, tries to
subvert the assistant’s rules, or is harmful. The result is stored with the stages
as review and comes back in GET. You cannot set it yourself; the server
ignores any review you send and writes its own.
A flagged review does not fail the request. The stages are saved but stay off
until a later review is clear, and the assistant runs as a single prompt with
all its actions meanwhile. If the review cannot run, status is unavailable:
the stages stay off when the check itself failed, and stay as you set them when
no AI provider is configured to run it.
Read conversationStages.review after a PATCH to see whether your stages will
run. Test them with the Ask API and draft: true.
Publishing runs the review again against the instructions it publishes, and
freezes the stages with that verdict.
Saving conversationStages runs the stage review, a model call on your organization’s credits, so a PATCH that changes stages counts against the organization’s rate limit and the key’s monthly credit cap. Changing only the instructions spends nothing. Every change is written to the audit log with the key’s id.
The MCP server offers the same as get_assistant and configure_assistant.
Safety and publishing
Section titled “Safety and publishing”Saved instructions are screened when publishing, whether authored through the
API or dashboard. PATCH saves the draft; it does not run the publish gate.
The gate checks for attempts to subvert the assistant’s rules and for harmful
content. It does not review writing style or prompt quality. Flagged instructions
block publishing.
Publish verdicts are cached on the assistant against a hash of the two instruction fields. Unchanged text reuses that verdict. Request-time injection uses a separate cache, described below.
Saving and publishing are separate. A successful PATCH updates the editable
configuration and recompiles its cached prompt. Public links continue serving
their published snapshot until you publish again or refresh that snapshot,
using the same publishing path as dashboard changes. See
Preview & Publishing.
Per-request instruction injection
Section titled “Per-request instruction injection”For instructions that vary by screen or step, an instructionOverrides payload
addresses the same two fields:
{ "instructionOverrides": { "customInstructions": { "value": "Help the user scope capabilities for the current screen.", "mode": "replace" }, "openingInstructions": { "value": "Ask which capability they want to work on.", "mode": "replace" } }}Send this with a chat request, rather than as a PATCH body. Enable
Deployment → Allow injection first. Omitted fields retain their configured
values. replace sets a field; append adds text to its configured value.
Each injected value has the shared field limit: 7,500 characters for
customInstructions, 2,000 for openingInstructions. In append mode, that
limit applies to the supplied text; the safety check evaluates the combined
result, including the configured text. Both resulting fields are screened
together, even when only one is overridden.
Injection does not provide an unrestricted system-prompt field or bypass the published assistant’s platform rules and capabilities. For live traffic, the published snapshot remains the base configuration; request overrides do not save a draft or publish a new snapshot.
Text chat and realtime use the same validation and screening path. All supplied
fields must validate and the resulting pair must be screened before any override
is applied. A flagged result returns 422 with an explanation and refuses
the request. It does not silently fall back to published instructions or apply
only part of the payload. For realtime, refusal happens before the sideband
connects.
If the safety service is unavailable, the request proceeds, matching the publish gate’s behavior. An unavailable result is not a safety pass and is not cached.
Screening cache
Section titled “Screening cache”Successful screenings are cached by organization and the hash of the resulting instruction pair for 30 days. Repeated text reuses an unexpired pass, including when an application switches between a fixed set of stages. New text or an expired pass requires another check.
This cache stores the hash and timestamps, not instruction text or findings. Flagged results are returned to the caller and are not cached. Keep the resulting instructions stable when possible to avoid repeated screening calls. See Chat Injection for embedding and transport examples.
Injection errors
Section titled “Injection errors”| Status | Meaning |
|---|---|
400 |
An instruction field has an invalid value or mode, or exceeds its length limit. |
403 |
Instruction injection is disabled for the deployment. |
422 |
The resulting instruction pair was flagged by the safety gate; the request is refused. |
Fix the payload before retrying a 400 or 422. Sending the same flagged text
again triggers another screening rather than retrieving a cached refusal.
Configuration API errors
Section titled “Configuration API errors”Errors use the standard v1 shape, with a stable code:
{ "code": "internal", "error": "Configuration saved but the system prompt could not be recompiled.", "retryable": true, "hint": "Send the same PATCH again: the save is idempotent and retries the recompile."}| Status | Code | Meaning |
|---|---|---|
400 |
invalid_request |
Malformed JSON, an unknown field, or a value that fails validation. |
500 |
internal |
The configuration was saved but the system prompt could not be recompiled. |
Authentication, scope (assistant:read / assistant:write), project and rate-limit refusals work the same on every endpoint. See Errors and Rate limits.
A 500 here means the stored draft is current but its compiled prompt is
stale. Retry the PATCH rather than treating it as a no-op. The published
snapshot remains unchanged.
Testing your prompts
Section titled “Testing your prompts”Putting instructions in code is worth doing mainly because it lets you assert on them. Two tests worth writing for any assistant that describes a UI:
- Content tests — the instructions you serve for each step name the controls that exist and do not name ones that do not. These catch a regression at edit time.
- A transcript test — ask a question late in a step, not on the first turn, and assert the answer still names the real controls. This is the one that catches context decay; a first-turn test passes even when the material is no longer reaching the model on turn five.
Related
Section titled “Related”- Conversation Stages: choose stages, instructions, actions, and move-on conditions.
- Dynamic Instructions — what belongs in these fields, and when to send instructions per request instead.
- API Keys — issuing keys and choosing scopes.