Media & Blob Ingestion
Store documents, images, audio, and video as blobs together with their text, so recall can search them.
CortexDB stores binary content (PDFs and other documents, images, audio, video) as blobs. Recall searches text, so write each file together with its text: upload the bytes, then write an experience that references the blob and carries the text.
| What you write | What recall searches |
|---|---|
A text blob (text/plain, Markdown, JSON, …) | The file's text, as soon as the write returns. The server decodes it into the event's text |
Any blob plus a transcript | Your transcript, as soon as the write returns. The server uses it and skips its own processor |
Either way the event keeps its link to the file in source_blob_ids, and erasing the event also
deletes the blob once no other event uses it (see Forget).
1. Upload the bytes
curl -X POST http://localhost:3141/v1/blobs \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: $CORTEX_ACTOR" \
-H "Content-Type: application/pdf" \
--data-binary @report.pdf
# → { "blob_id": "blob_01HX...", "size_bytes": 48211, "content_type": "application/pdf", "sha256": "..." }The body is the file (any Content-Type is accepted as raw bytes). The size cap is 32 MiB —
larger uploads return 413. See Blobs API for the full shape.
2. Get the text
-
Documents (PDF, docx, xlsx, …): run them through a text extractor. If you run the Apache Tika sidecar, ask it directly. Tika usually has no host port, so run
curlon its Docker network (replacecortexnetwith yours):docker run --rm --network cortexnet -v "$PWD:/w" curlimages/curl -s \ -T /w/report.pdf -H "Accept: text/plain" http://tika:9998/tika > report.txt -
Images: OCR output, a caption, or alt text.
-
Audio and video: a transcript you already have, or one from your ASR service.
-
Plain-text files: nothing to do. Upload them with
Content-Type: text/plain; charset=utf-8and skip thetranscriptin step 3.
3. Reference the blob with its text
curl -X POST "http://localhost:3141/v1/experience?wait=indexed" \
-H "Authorization: Bearer $CORTEX_TOKEN" \
-H "X-Cortex-Actor: $CORTEX_ACTOR" \
-H "Content-Type: application/json" \
-d '{
"scope": "org:acme/user:alice",
"modality": "document",
"content": {
"kind": "blob_ref",
"blob_id": "blob_01HX...",
"transcript": "The Kestrel contract renews on 1 February."
},
"context": { "observed_at": "2026-05-15T10:42:00Z" }
}'The stored event reads back as content.kind: "text" with your transcript as its text, and recall
finds it as soon as the write returns. With the Python SDK, pass transcript= next to blob_id=:
blob = cx.upload_blob(open("report.pdf", "rb").read(), content_type="application/pdf")
cx.experience("org:acme/user:alice", blob_id=blob["blob_id"],
transcript=open("report.txt").read(), wait="indexed")Server-side processors
A blob written without a transcript is handed to the matching content processor in the
background, once processors are configured (CORTEX_IMAGE_*, CORTEX_AUDIO_*, CORTEX_DOCUMENT_*,
CORTEX_VIDEO_*; see Storage & Cluster). Until then
/v1/admin/ready reports content_processors.configured: [] and blobs are stored without text.
| Modality | Processor (once configured) | Needs a key? |
|---|---|---|
| Document (PDF, docx, …) | Tika / unstructured | No (a Tika sidecar) |
| Image | GPT-4o vision | Yes (OpenAI) |
| Audio | Whisper | Yes (OpenAI) |
| Video | ffmpeg keyframes → image processor | Yes (for the frames) |
Processing runs on a separate pipeline: ?wait=indexed returns once the event is indexed, not
when processing finishes. The server log shows content processing complete ... derived_text_len=N
for each blob. It runs even on a content-only instance, since it is not gated behind enrichment (see
Self-hosting defaults).
For text you want recall to find, send it with the blob as in step 3. A supplied transcript also
skips the processor call, which saves the vision or Whisper cost for images and audio.
Deleting a blob
curl -X DELETE http://localhost:3141/v1/blobs/blob_01HX... \
-H "Authorization: Bearer $CORTEX_TOKEN" -H "X-Cortex-Actor: $CORTEX_ACTOR"Blob storage backends (local / S3 / GCS / Azure) are configured under [blob_store] — see
Storage & Cluster.