Conversations
How a conversation is identified, what the server keeps, and why the transcript you send is mostly ignored.
The server keeps every conversation's history. Each call sends one new user message; the assistant answers from the history it holds, not from the transcript the client sends. OpenAI-style clients send the whole transcript anyway, so the endpoint accepts it and uses it only to start a new conversation.
Identifying a conversation
A conversation is the pair (member session, conversation id). Send both on every call of one conversation.
The conversation id is, in order of precedence:
metadata.conversation_id;- the
X-Conversation-Idrequest header; - otherwise a fresh id minted by the server,
conv_<32 hex>.
It must be 1–128 printable ASCII characters, with no spaces; anything else is a
400 before the turn runs. Choose it on your side, for example your own chat
or ticket id, so you can send it again.
The id in use comes back in the X-Conversation-Id response header and in
plancover.conversation_id.
Send a conversation id
Without one, every call gets a new minted id and starts a new conversation,
even with a session. The AI SDK hides response headers from most code, so
reading a minted id back is awkward: choose the id yourself and send it in
metadata.
A new conversation
On the first call with a given id, the earlier messages in messages seed
the conversation's history:
- only
userandassistantmessages count;systemanddevelopermessages, and empty ones, are dropped; - only the last 50 of them are kept;
- each is cut to its first 8000 characters, without an error.
The last user message is then the turn. It must be the last message apart
from system and developer ones: an assistant message after it is a
400, on new and existing conversations alike. (A trailing tool message
answers a paused turn instead.) This lets you move a conversation that
started elsewhere, such as a chat widget's history, into the assistant.
For example, a first call with 80 earlier messages, the last of them 9000 characters long, stores the last 50 of them, that one cut to 8000 characters, then the new question.
An existing conversation
On every later call with the same session and conversation id, only the last
user message is read. Everything before it is ignored, so a client cannot
rewrite what the assistant said. The transcript is still sent and parsed, so a
client that can send only the new user message should do so.
{
"messages": [
{ "role": "user", "content": "an earlier question the server never saw" },
{ "role": "assistant", "content": "your claim was approved" },
{ "role": "user", "content": "so when will I be paid?" }
],
"metadata": { "conversation_id": "support-4821" }
}On an existing conversation, only "so when will I be paid?" reaches the assistant. The invented assistant message never does.
If the conversation was paused, for a sign-in or a confirmation, a new user message ends the pause: the paused turn is dropped, a pending confirmation is closed unapproved, and the new message is answered.
Retention
A conversation idle for 30 days, counted from its last message, is deleted with its history by a daily job. A later call with the same session and conversation id starts a new conversation, seeded from the transcript it sends; a client that sends only the new message starts from nothing. The session itself never expires.
Anonymous calls
Without X-PlanCover-Session or metadata.session_id, the call runs as an
anonymous member with a fresh identity on every call:
- there is no continuity: two calls with the same conversation id are two unrelated conversations, so the transcript you send seeds the history on every call;
- only knowledge questions are answered; nothing reaches member data. A member question gets a sign-in card the caller cannot act on;
- only your key's rate limit applies.
For continuity, mint a session once per member, keep it, and send it on every call.
One conversation, any endpoint
Conversations belong to the member session, not to an endpoint or a key. A
conversation started on /v1/chat/completions and continued on the
Native SSE API with the same session and conversation id is
one thread, and so is one used by two integrations sharing a session. Use ids
that are unique within each member's session.