---
title: "Ingest Episode"
method: POST
path: "/ingestion/episode"
tags: ["Ingestion Pipeline"]
---

# Ingest Episode

`POST /ingestion/episode`

## Request

- Base URL: `https://api.getmetacognition.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getmetacognition.com/ingestion/episode`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/metacognition/apis/tex-api/docs/ingestion/episode/post.ts> (a function with typed inputs; its response is checked against the schema)

Store a conversation episode as remembered turns (tex.episodes.store).

The messages go through the SAME path as POST /ingestion/memory
(conversations.remember): each message is a turn in the active tier
(recallable at once) and a durable turn node, in one active-memory
episode that is later consolidated into an EPISODE_SUMMARY — which is
what GET /episodes lists. It used to be forwarded to the ingestion
service's /ingest/episode, which stored it as a document: not recallable
until that job ran, never an episode, never listed.

Request shape unchanged. Mapping onto turns:
- ``content`` (or ``text``) -> turn text; ``role`` user/human -> user,
  assistant/ai/agent/bot/model -> assistant, any other role -> an
  assistant turn prefixed with the role ("[system] ...");
- ``timestamp`` -> the turn's capture time (same contract as remember,
  natural language read in ``timezone``, default UTC); omitted -> the
  arrival time, +1 ms per message, so order and identity are kept;
- the episode is ``episode_id``, else the session id sent, else a new id
  (returned as ``episode_id``);
- ``scope``: legacy tenant dict or "<kind>:<value>", checked exactly as
  /ingestion/memory checks it.
``metadata`` and ``options`` (preference extraction etc.) belonged to the
document pipeline and are accepted but not used.

## Request body

- EpisodeIngestRequest: Episode ingestion request.
  - `episode_id` string, nullable: Episode identifier
  - `messages` ChatMessage[], required: Chat messages
    - `role` string, required: Message role
    - `content` string, required: Message content (alias: text)
    - `timestamp` string, nullable: Message time: ISO 8601 (e.g. 2026-01-01T09:30:00Z) or epoch seconds/milliseconds, or natural language; default the arrival time
    - `metadata` object: Message metadata (not stored)
  - `scope` union, required: Tenant scope dict (legacy) OR hierarchical write-scope string "<kind>:<value>" (org/user then come from the auth context)
    - string
    - Scope: Multi-tenant scope.
      - `org_id` string, required: Organization ID
      - `user_id` string, nullable: User ID
      - `session_id` string, nullable: Session ID
  - `session_id` string, nullable: Explicit session id, honored on the string-scope path (the SDK sends it alongside a string scope; the legacy dict carries its own)
  - `metadata` EpisodeMetadata: Episode metadata.
    - `agent_name` string, nullable: Agent name
    - `episode_type` string, nullable: Episode type
    - `topics` string[]: Topics
    - `custom` object: Custom metadata
  - `options` EpisodeOptions: Episode processing options.
    - `extract_preferences` boolean: Extract preferences
    - `temporal_tracking` boolean: Temporal tracking
    - `auto_create_schema` boolean: Auto-create schema
    - `generate_embeddings` boolean: Generate embeddings
  - `timezone` string, nullable: The caller's timezone (IANA name, e.g. Europe/Berlin, or +HH:MM) for natural-language turn timestamps such as "yesterday 3pm"; default UTC

## Response `200`

Successful Response

- EpisodeIngestResponse: Episode ingestion response. An episode is stored as remembered turns (POST /ingestion/memory), so the turn fields below are the same as ``IngestMemoryResponse``'s.
  - `job_id` string, required: Per-call observability id (there is no background job)
  - `episode_id` string, required: The episode the messages were stored in: the request's episode_id, else its session id, else a new id. Store more messages under it to continue the episode.
  - `status` string, required: "stored": every message is recallable now
  - `preferences_detected` integer, nullable: Not computed (always null); kept for compatibility
  - `estimated_time` integer, nullable: Not used (always null); kept for compatibility
  - `message` string, nullable: What happened, in words
  - `turn_ids` string[]: Turn id of each stored message part; retire one with DELETE /ingestion/memory/turns/{turn_id}
  - `active_fragment_ids` string[]: Active-tier fragment ids written
  - `turn_times` TurnTime[]: Each message's resolved capture time
    - `turn_index` integer, required
    - `original` unknown, required
    - `timestamp` string, required: Capture time, ISO 8601 UTC
    - `timestamp_ms` integer, required
    - `source` 'explicit' | 'relative' | 'arrival', required: explicit time / natural language resolved against arrival / not understood
    - `uncertain` boolean, required: True when the capture time is only the arrival time
  - `not_stored_forgotten` string[]: turn_ids not stored because their owner had forgotten them
  - `usage` Usage: Token usage for a metered API call. Surfaced on every metered response so developers can see the cost of each call inline — mirrors OpenAI / Anthropic ``usage`` objects so the Tex bill is denominated in the same unit they already track for their LLM provider. Tokens are counted server-side with the ``cl100k_base`` encoding (matches ``text-embedding-3-large``).
    - `tokens_in` integer: Tokens in the request payload
    - `tokens_out` integer: Tokens in the response payload

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/metacognition/apis/tex-api.md) - [All operations](https://skmtc.dev/metacognition/apis/tex-api/llms.txt) - [OpenAPI document](https://skmtc.dev/metacognition/apis/tex-api/revisions/3bef47e50f2e?raw)
