The preamble Parameter: Command's Version of a System Prompt
8 min read · updated August 11, 2026
preamble is Cohere’s v1 system prompt, and it differs from an OpenAI system message in two ways that matter: it is a top-level request parameter rather than a message, and it overwrites a default the model was expecting.
The field
In /v1/chat, preamble is a string that sits beside message and chat_history rather than inside them:
curl https://api.cohere.com/v1/chat \
-H "Authorization: Bearer $CO_API_KEY" \
-H "content-type: application/json" \
-d '{
"model": "command-r-plus-08-2024",
"preamble": "You are a support agent for a Dutch bike retailer. Answer only from the supplied documents. If the documents do not contain the answer, say so and offer to open a ticket. Never quote a price that is not in a document.",
"message": "Is the Gazelle Ultimate C8 in stock in Utrecht?",
"documents": [
{"id": "inv-441", "title": "Utrecht stock", "snippet": "Gazelle Ultimate C8: 0 units, restock 2026-08-19."}
]
}'Because it is a parameter and not a message, there is exactly one of it. You cannot send two, you cannot interleave one into the middle of a conversation, and there is no ordering question to get wrong — which removes a whole class of bug that exists in message-array APIs, where a second system message can be silently ignored or silently concatenated depending on the provider.
There is a default, and you replace it
This is the part that catches people. Command models are trained with a default preamble, and Cohere documents its content in the preambles documentation. It establishes the assistant persona and, importantly, the conventions the model uses for grounded generation. Set your own preamble and you do not append to that — you replace it.
The symptom is specific and confusing when you have not seen it before: a request that worked while you were prototyping without a preamble starts producing lower-quality citations, or a different formatting style, or a chattier tone, the moment you add three lines of task instruction. Nothing about your instruction caused it. The default that was doing quiet work is simply gone.
The practical rule that follows is to make a custom preamble additive in spirit: state the role, state the task constraints, and leave the general behaviour alone rather than trying to redefine it. A preamble that begins “You are a helpful assistant” is spending tokens restating what the model already is. A preamble that says “answer only from the supplied documents, and say so when they do not cover it” is telling it something it does not know.
Where it sits in the rendered prompt
Cohere’s Command models use a documented chat template with explicit turn delimiters, and the preamble is rendered into a system turn near the top — before the conversation and before any documents or tool definitions:
<BOS_TOKEN>
<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>{safety preamble}
{user preamble}<|END_OF_TURN_TOKEN|>
<|START_OF_TURN_TOKEN|><|USER_TOKEN|>{message}<|END_OF_TURN_TOKEN|>
<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>Two things follow from that placement. First, the safety preamble selected by safety_mode is rendered ahead of yours and is not something your text replaces — see what safety_mode actually swaps in. Second, in a long conversation your preamble is the furthest thing from the model’s current position in the sequence, which is the usual reason a carefully written instruction stops being obeyed around turn thirty.
The template is not something you should be hand-assembling against /v1/chat — the API does it for you. It is worth knowing because it explains behaviour, and because it is exactly what you would need if you ran the open-weights Command R checkpoint yourself.
In v2 it is a system message
/v2/chat has no preamble field. The same content goes in a message with the system role, at the head of the array:
{
"model": "command-a-03-2025",
"messages": [
{"role": "system", "content": "You are a support agent for a Dutch bike retailer. Answer only from the supplied documents."},
{"role": "user", "content": "Is the Gazelle Ultimate C8 in stock in Utrecht?"}
]
}Watch for the SDK layer here, because it is where the two versions get confused in practice. Cohere ships separate clients — a v1 client and a v2 client — and passing preamble to the v2 client is not a helpful alias for a system message; it is an unrecognised argument. Framework integrations add another layer of translation on top, and some of them assemble a system message from several sources, so what reaches the API is not always the string you wrote. When behaviour does not match the instruction, log the outgoing request body once. It is a two-minute check that resolves a surprising share of these.
This is a rename with consequences rather than a pure rename. The v1 guarantee that there is exactly one preamble is gone: a message array can hold two system messages, and putting one at the end of a long history is now expressible even if it is rarely what you want. The default-preamble behaviour is unchanged — supplying a system message still replaces the default rather than adding to it.
When a preamble stops being obeyed
The complaint is always the same shape — “it followed the instruction yesterday and now it does not” — and it has four distinct causes that are worth separating before rewriting the text.
- Distance. The preamble is at the top of the sequence and the conversation has grown. An instruction that was three hundred tokens from the generation point is now thirty thousand. The fix is not a stronger preamble; it is restating the constraint closer to the end, either by appending a short reminder to the final user message or by trimming the history.
- Conflict with the documents. A preamble saying “answer in one sentence” competes with twenty retrieved documents that visibly contain more than one sentence of relevant material. The model resolves the tension probabilistically, so you get one-sentence answers most of the time and a paragraph when the documents are rich. Instructions that fight the input win inconsistently.
- Conflict with the safety preamble. Rendered ahead of yours and not replaced by it. A preamble instructing the model to answer anything, under a strict safety mode, produces exactly the inconsistency you would expect from two instructions that disagree.
- The model changed. If the model name in the request is a bare alias rather than a dated snapshot, it can be a different model this week — prompts are tuned against a snapshot whether or not anyone intended them to be. This is the argument for pinning a dated version stated as a symptom rather than as a principle.
The diagnostic that separates them costs one request: send the same preamble with a two-turn history instead of the full one. If the instruction is obeyed, the problem is distance or accumulated context and no amount of rewording fixes it. If it is not, the preamble itself is the problem and you can iterate on it cheaply, without a forty-turn conversation in the way.
Writing one that survives long context
- Put the non-negotiable rule last. Recency within the preamble is cheap to arrange and the final line of a system turn is reliably better attended than the middle of one.
- State what to do when the answer is absent. With
documentsin play, the failure you actually care about is the model answering from parametric memory when the documents are silent. An explicit escape hatch — say you do not know, offer the next step — is worth more than any amount of “be accurate”. - Do not restate formatting the citations already carry. Asking for inline source markers in prose competes with the structured
citationsarray, and you end up post-processing two attribution schemes that disagree. - Count it. The preamble is billed as input on every single turn of the conversation, not once. A 600-token preamble on a forty-turn session is 24,000 input tokens spent restating the same paragraph.