Conversations and memory
Persist stateful chat, compact long histories, and manage semantic facts.
Conversations persist exactly what a user received from successful chat turns. Semantic memory is an optional layer that distills durable facts into user or project graphs.
These are distinct:
- a conversation is an ordered transcript with a rolling summary;
- memory is a set of temporal facts retrieved across conversations.
Both are owner-scoped and require a user identity.
Create a conversation
curl -H "Authorization: Bearer $DEEPLINQ_TOKEN" \
-H "Content-Type: application/json" \
-X POST "$BASE_URL/v1/conversations" \
-d '{"title":"Architecture review"}'Optionally set project_id at creation. The project is validated and
authorized before the conversation is stored.
Add a chat turn
curl -H "Authorization: Bearer $DEEPLINQ_TOKEN" \
-H "Content-Type: application/json" \
-X POST "$BASE_URL/v1/chat/completions" -d "{
\"model\":\"gpt-5-mini\",
\"conversation_id\":\"$CONVERSATION_ID\",
\"messages\":[
{\"role\":\"user\",\"content\":\"Summarize the decision so far.\"}
]
}"The server replaces ad hoc history with its assembled state: rolling summary,
recent verbatim turns, and the caller's new user or tool messages.
conversation_id and project_id are mutually exclusive on the request.
Set "store":false when a successful turn must not be persisted.
Read and manage
GET /v1/conversations?limit=50
GET /v1/conversations/{id}?limit=100
PATCH /v1/conversations/{id}
DELETE /v1/conversations/{id}Patch title or archive state:
{"title":"Final architecture review","archived":true}Lists and reads are owner-only. Long histories compact asynchronously after the configured token trigger while retaining the newest turns verbatim.
Enable semantic memory
Semantic memory requires MEMORY_URL, MEMORY_TOKEN, and the distinct
MEMORY_SIDECAR_TOKEN. An organization administrator configures:
GET /v1/memory/settings
PUT /v1/memory/settings{
"memory_enabled": true,
"retention_days": 90
}An explicit null retention clears the TTL; omission preserves the current
value.
The read needs no role; the write requires org-admin. retention_days is
how long that person's own conversations survive, so any member of the
organization may read the policy and show it on a privacy screen. Changing it
is authority over every colleague and stays with administrators. The read takes
no parameter, derives the organization from the token, and mutates nothing.
A null retention_days means there is no automatic deletion period — render
it as that, not as a number. Deletion is asynchronous when a period is set:
expired conversations go through the same durable cleanup chain as an explicit
delete, so a thread may outlive its window briefly, and derived memory facts
that have other sources can remain.
Choose the model memory runs on
Memory infrastructure makes ordinary chat completions — extracting facts from an exchange, resolving an ambiguous entity, consolidating an ambiguous fact — and an application chooses which model serves them for all of its organizations:
GET /v1/application/memory-model
PUT /v1/application/memory-model
DELETE /v1/application/memory-model{ "model": "claude-haiku-4-5-20251001" }The response says which row answered:
{ "model": "claude-haiku-4-5-20251001", "source": "application" }source is application when the application has chosen a model,
platform when it has not and the engine-wide model applies, and
unconfigured — with model empty — when neither exists, in which case memory
falls back to each organization's own model grants. DELETE returns 204 and
the fallback resumes on the next memory call; deleting nothing is 404.
These three calls take the application credential, not an end-user token: the credential names the application, so there is no id in the path and no way to read or change another application's choice.
The model must be present in the global pricing catalog with chat modality; an
unpriced name or an embedding model is refused with 422. It does not have
to be enabled in the application's tenant-chat catalog, granted to any
organization, priced by the application, or served by a reachable connector —
this is infrastructure, not something the application sells. Usage is still
billed to the organization the memory work is for.
Explicitly remember a fact
curl -H "Authorization: Bearer $DEEPLINQ_TOKEN" \
-H "Content-Type: application/json" \
-X POST "$BASE_URL/v1/memories" -d '{
"content":"The user prefers concise release notes.",
"scope":"user"
}'The content is stored verbatim because this is an explicit user action. For
project scope, include project_id; reads require project read permission and
writes require project write permission.
GET /v1/memories?scope=user&limit=50&cursor=<opaque>
GET /v1/memories?scope=project&project_id=<id>
DELETE /v1/memories/{fact_id}?scope=userThe ID returned by explicit remember identifies the distillation episode. Fact IDs returned by listing are separate; use listed fact IDs for deletion.
User opt-out
GET /v1/memory/optout
POST /v1/memory/optout
DELETE /v1/memory/optoutOpt-out is self-service for the authenticated user. The read returns
{opted_out, organization_enabled, enabled} where enabled is
organization_enabled && !opted_out — so a client can restore a persisted
switch truthfully and explain an organization-disabled state instead of
guessing. An organization that disables memory preserves each person's stored
choice. Memory routes return 503 memory_unavailable when the sidecar is not
configured.
Personal response instructions
GET /v1/instructions
PUT /v1/instructions
POST /v1/instructions/previewEach authenticated end user keeps one response-preference row per
organization: a closed tone (default | concise | formal | explanatory)
plus free-form additional_instructions (at most 16 KiB UTF-8). GET
returns the default representation with updated_at: null before anything is
saved; PUT is a full replacement and returns the stored representation.
Saved preferences are rendered into ONE system block and injected into every
ordinary, streamed, RAG, and knowledge chat in that organization, in the
position shown under How a request is assembled.
They are never persisted into transcripts, summaries, compaction, or memory
facts. Agent runs and internal model calls receive no personal block.
POST /v1/instructions/preview demonstrates an UNSAVED draft with one real
governed completion over the fixed prompt
What are the best ways to improve my productivity? on the caller's first
permitted model, returning {prompt, model, answer, usage}. It spends tokens
like any chat call and persists nothing; with no usable model it answers
422 model_unavailable.
Skills
GET /v1/skills
POST /v1/skills/personal
GET /v1/skills/{kind}/{id}
DELETE /v1/skills/personal/{id}
PUT /v1/skills/{kind}/{id}/enablementA skill is one SKILL.md document in the Anthropic Agent Skills format,
instructions-only subset: YAML frontmatter with a required single-line
name and description, then a Markdown body. Each end user sees two
kinds — application (the catalog the organization's application curates
through GET/POST /v1/application/skills and
GET/PUT/DELETE /v1/application/skills/{id}, with strong-ETag
conditional writes) and personal (the caller's own immutable uploads;
editing is delete plus re-upload).
Every skill starts disabled. Enabling one is an explicit, hash-bound
consent: the PUT .../enablement body names the content_sha256 from the
current list or detail response, a stale hash answers 409 skill_changed,
and a catalog replacement that changes the document's content clears every
user's enablement of it. At most 8 skills and 64 KiB of combined
instructions may be enabled per user per organization
(409 skill_context_limit).
An enabled skill's Markdown body joins that user's model calls as one deterministic system block — ordinary, streamed, RAG, and knowledge chat in both dialects, plus one-shot agent invocations and every durable agent-run turn. See How a request is assembled for where it lands. Resolution is live and uncached, so a disable, a delete, or a catalog content change affects the very next call or durable turn, and nothing snapshots a skill onto a conversation, agent, run, or turn. No request field names a skill. Skill text is context, never capability — it grants no tools, datasets, network, or credentials, is screened by the same guardrails as every other input, and is never persisted into transcripts, summaries, or memory.
How a request is assembled
Everything above lands in one request, in one fixed order, for ordinary, streamed, RAG, and knowledge chat in both dialects:
[project instructions][skills][personal instructions][memory][summary][tail][new message]- project instructions, the project's own, when the conversation belongs to one. Knowledge chat's platform prompt still lands ahead of all of it.
- skills, the caller's enabled skills, application entries first, then personal, each sorted by name.
- personal instructions, the caller's saved response preferences.
- memory, retrieved semantic facts.
- summary and tail, the conversation's rolling compaction and its recent turns.
- new message, the request's own.
An agent turn uses the same idea with its own opening slot. It is persona, then skills, then memory, then the tail. That holds for one-shot invocation and for every turn of a durable run.
The order is also a refusal order
The personal-instruction and skill lookups run before any provider work, including the memory search, and they fail closed:
| Failure | Answer |
|---|---|
| Personal instructions cannot be read | 503 instructions_unavailable |
| Enabled skills cannot be resolved | 503 skills_unavailable |
Nothing is spent when either happens. This is deliberately not memory's
posture. A memory sidecar outage degrades the answer, while answering without
the constraints a person explicitly saved would misrepresent their chat. Memory
routes keep their own 503 memory_unavailable.
A one-shot agent invocation fails the same way before spend. A durable turn returns a retryable error to the worker and checkpoints nothing. A caller with no subject behind the credential performs no lookup and receives no block.
Conversation deletion is asynchronous. The cleanup job removes transcript state and asks the memory sidecar to forget linked episodes before final deletion. A memory purge failure retries and prevents a false successful erase. The retention sweep enqueues the same cleanup path.