Skip to main content
You already have a model you like. What you do not have is a way to make it start a session knowing who it is working with: how they frame problems, which mode they are in this week, and how they write. So every session starts from a blank slate, and the user re-explains themselves. A ConditioningPack is that context, composed on demand and shaped to drop straight into a system prompt. It works with any model, because it is text. Get one from POST /v1/condition/session.
200 application/json

What it is

ConditioningPack, everything an agent needs to answer as this subject, assembled at call time (in practice, a system prompt plus the two ingredients that went into it, so you can use the whole thing or pick it apart). VoiceProfile, how the subject writes, measured from their own words (in practice, four numbers: message length, formality, technical density, and how much evidence is behind them). Basin, the mode of thinking the subject is currently in (in practice, the cluster their recent positions fall into). See ManifoldState. The pack is composed, not stored. Each call reads the current identity state and builds fresh text, which is why there is no route to fetch “the pack” by id and why expiresAt exists.

Where it comes from

Four reads go into one prompt. Every ingredient degrades independently. A brand-new subject still gets a usable prompt, just a thinner one, rather than an error or a fabricated profile.
userId and orgId are required by the request body but they are advisory provenance only. The API key decides which organization and which identity the call acts as, and the subject selector is the X-Exo-Subject header. If the body disagrees with the credential, the credential wins.

Field reference

expiresAt is worth reading carefully. The pack does not stop working when it passes: the text stays valid. What it tells you is that the identity underneath may have moved, so it is time to recompose. Cache until then, and recompose after, rather than treating it as an expiry you must handle.

The VoiceProfile, and its maturity gate

The voice profile is returned on the identity view, GET /v1/recall/identity:
The whole object is withheld while evidenceCount is zero. You get null, not a profile of defaults. That is deliberate: a confidently wrong voice is worse than no voice, because a model told to write casually for someone who writes formally is more jarring than a model with no instruction at all. The same gate governs userVoiceHint on the pack. Null hint means not enough evidence yet, and the right response is to leave your model’s default voice alone rather than substituting a guess.

What conditioning actually changes

Illustrative, not measured. The same question, asked of the same model, with and without the pack in the system slot. Question: should we add a retry wrapper around the batch ingest call? Unconditioned
There are several approaches to retry handling. You could implement exponential backoff with jitter, use a circuit breaker pattern, or rely on your queue’s built-in redelivery if one is available. The right choice depends on your idempotency guarantees and your latency budget. Would you like me to walk through the tradeoffs of each?
Conditioned
No. The queue already redelivers, and you told me last week you had pulled a retry wrapper out for exactly that reason. Adding one back gives you two retry layers with different backoff, which is how you get duplicate jobs under load. Send Idempotency-Key on the batch instead and let redelivery do the work.
The difference is not tone. The conditioned answer commits to a position, references the subject’s own prior decision, and skips the menu of options, because the prompt carried their basin, their recent prompts and their voice. That is what the pack buys: less context re-established per session, and answers shaped like the ones this person actually wants.

The scaffold variant

POST /v1/condition/scaffold is for agents that assemble their own prompts and want the identity header only, wrapped around a single prompt rather than composed into a full system message.
200 application/json
focalNodes is always empty in v1. The scaffold is composed from identity, not from a retrieval pass, so there are no focal nodes to report yet. If you need the nodes behind an answer, read cognition.focalNodeIds from POST /v1/retrieve. Treat a future non-empty list as additive.
The subject has not written enough for a voice profile to exist, so evidenceCount is still zero and the profile is withheld entirely rather than defaulted. Ingest more of their own writing, not agent narration about them: the profile is measured from the subject’s messages.
The identity manifold has not run a discovery pass for this subject yet, so there is no basin to name. The prompt is composed from whatever exists, so it comes back without that line rather than with a placeholder. See ManifoldState for what a pass needs.
Once per session is the intended shape: compose at session start, inject, and reuse for the session. expiresAt is the signal for a long-lived process. Recomposing per message spends read units for text that has not changed, since composition reads stored state rather than recomputing identity.
Both condition routes are read-shaped and take the read scope. They compose from stored state and write nothing, which is also why they return 200 synchronously rather than a job id: an agent’s session-start hook needs the text immediately.
Yes. The pack is plain text with no model-specific formatting. Put systemPrompt in whatever your provider calls the system slot. One identity, any model.

Limits

  • focalNodes on the scaffold route is always empty in v1.
  • usage.llmInputTokens and usage.llmOutputTokens are null, because composition makes no model call.
  • There is no route that returns a previously composed pack. Packs are built per call and not stored.
  • orgRecentContext identifies members by internal id, not by name or email. Resolve names through GET /v1/team/members, which needs an admin key.
  • The voice profile has four measurements and no free-text style description.

Next

Conditioning your own agent

Wiring the pack into a session, end to end.

Compose a session prompt

Request fields, the usage block and the scope it needs.