Guides

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=user

The 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/optout

Opt-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/preview

Each 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}/enablement

A 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:

FailureAnswer
Personal instructions cannot be read503 instructions_unavailable
Enabled skills cannot be resolved503 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.

On this page