The Experience Envelope
The single structured payload for everything CortexDB ingests — messages, observations, tool results, documents, and triples.
The Experience Envelope is one structured shape used to ingest all forms of agent experience — messages, observations, tool results, documents, and raw triples — into lossless event-sourced memory. One shape covers every data type, so ingestion isn't fragmented across formats.
CortexDB separates the payload into functional slots: identity (who), content (the data), context (temporal/spatial grounding), and operational directives (how to process it).
Minimal envelope
{
"scope": "org:acme/dept:eng/user:alice",
"modality": "conversation",
"content": { "kind": "message", "role": "user", "text": "Just got off a call with Priya at Acme." }
}Required fields
Only scope, modality, and content are required. context (and context.observed_at) and
idempotency_key are optional — a write without them returns 202. (Older docs marked observed_at
and idempotency_key as required; they are not.)
Identity slots
Up to three identities; each defaults to the previous for the common "narrating yourself" case.
| Slot | Default | Capability needed when set explicitly |
|---|---|---|
| Caller | Implicit (from token) | always |
| Observed actor | = caller | scope.write.on_behalf_of when ≠ caller |
| Subject | = observed_actor | scope.write.about_other when ≠ observed_actor |
This supports bots writing on behalf of a user (observed_actor) and writing memories about other
entities (subject).
Content kinds
content is a discriminated union — pick the shape that matches your data:
| Kind | Shape | Use for |
|---|---|---|
message | { role, text, media[] } | Conversational turns |
text | { text } | Free-form text observations |
json | { data } | Structured tool outputs, sensor readings |
blob_ref | { blob_id } | Reference an already-uploaded blob |
triple | { subject, predicate, object } | Direct fact insertion (object needs type+datatype+value) |
The role enum is enforced: user, assistant, tool, system (anything else → 422).
Modalities and extraction
| Modality | Triggers extraction? | Notes |
|---|---|---|
conversation | yes | Fact + episode extraction |
document | yes | Document chunking + entity extraction |
tool_result | yes | JSON inspection |
observation | yes | Generic |
feedback | no | Supporting evidence only |
imported | yes (with mapping) | Used by /v1/import/* |
The modality enum is not enforced — unknown values are stored verbatim but don't trigger structured
extraction. (Extraction into Facts/Beliefs also requires enrichment to be configured — see
Self-hosting defaults.)
Context
{
"observed_at": "2026-05-15T10:42:00Z",
"source_recorded_at": "2026-05-15T10:41:58Z",
"location": { "city": "Bangalore" },
"preceded_by": ["evt_01HX..."],
"intent": "deal_status_update",
"labels": ["acme", "renewal"]
}All context fields are optional. preceded_by chains experiences by CortexDB event id (evt_…) so the
episode builder recognizes a sequence.
Idempotency
idempotency_key (≤ 64 chars) is optional but recommended for safe retries. Same key + same body →
no-op; same key + different body → 409 IDEMPOTENCY_CONFLICT. Use a deterministic key per
source-system message (e.g. slack:C123:T456:1747293720). Idempotency records persist across restarts, so
a retry after a crash replays the same event_id (replayed_from_idempotency: true) rather than
duplicating. Keys are global (not per-scope) and expire after ~24 h.
Operational directives
{
"directives": {
"extract": ["facts", "beliefs"],
"consolidate_into": "org:acme/dept:eng",
"confidence_floor": 0.7,
"ttl_for_belief_layer": "P30D",
"embed": "eager"
}
}| Field | Notes |
|---|---|
extract | Subset of facts, entities, beliefs, episodes, understanding |
consolidate_into | Merge derived layers into a different scope (requires scope.write on both) |
confidence_floor | Drop derived records below this confidence |
ttl_for_belief_layer | ISO-8601 duration. Stored but not enforced yet — records do not auto-expire |
embed | eager / lazy / none |
FAQ
Does the envelope block the write path? No — it returns 202 after the capture phase; extraction and
consolidation run asynchronously.
Can it handle tool outputs? Yes — the json content kind ingests structured tool outputs / sensor readings.
How does it handle identity? Three slots (caller, observed actor, subject) let bots act on behalf of a user or write about other entities.
See also
- Lifecycle — the six atomic operations of memory.
- Scopes — how scopes partition memory.
POST /v1/experience— the full API reference.