Ingestion
Routing, files, deduplication and what happens after content arrives.
Four ways in: connectors, file uploads, the ingest endpoint, and space conversation capture. All four converge on the same pipeline.
The pipeline
Everything after the perimeter scan happens in the background. The write returns as soon as the content is safely accepted.
Text and structured content
POST/api/v1/memory/ingest
Accepts text, markdown, html, json and code.
{
"content": "# Acme Q4 review\n\nDeal moved to Security Review.",
"content_type": "markdown",
"routing": { "mode": "auto" },
"metadata": { "source_system": "crm", "author": "jana@acme.com" }
}Send metadata liberally. It costs nothing at write time and gives you filtering
you will want later.
Routing
| Mode | Behaviour |
|---|---|
auto | Classifier picks the department |
manual | You supply vault_id |
hybrid | You pick the department, the classifier picks below it |
Auto-routing declines to guess below 0.70 confidence and files the content in
v_unassigned with needs_review: true.
Watch needs_review. Content in v_unassigned is still searchable memory-wide,
but it is invisible to any department-scoped query — and nobody goes looking in a
drawer marked "unassigned".
Pin what you already know:
{
"content": "...",
"routing": { "mode": "manual", "vault_id": "v_finance" }
}Files
POST/api/v1/memory/uploads
PDF, DOCX, XLSX, CSV, TXT, Markdown and images. Scanned PDFs and images go through OCR automatically.
curl https://api.uni-flow.ai/api/v1/memory/uploads \
-H "x-api-key: $CHORDIAN_API_KEY" \
-F "file=@q4-report.pdf"Optional query parameters space_id and vault_id tag and pin the file.
Returns status: "pending". Poll the upload or subscribe to
memory.ingest.completed rather than sleeping and hoping.
Don't pre-chunk
Send whole documents. Chunking happens server-side with the full document in context; splitting into sentences beforehand throws away the context that would have produced better chunks.
Deduplication
Re-ingesting an unchanged document short-circuits rather than creating a duplicate, so a connector re-sync is cheap and safe to run.
To deliberately replace a document, pass options.overwrite_id.
The perimeter
Every path runs the data perimeter before storage. The response reports what was masked:
{ "doc_id": "ing_9f2c8a1e", "dlp_matches": 2 }If the scanner cannot run, ingestion returns 503 with CHORD_DLP_UNAVAILABLE
and nothing is stored. Retry with backoff — this is fail-closed by design.
Re-indexing
POST/api/v1/memory/uploads/{upload_id}/reindex
Re-runs extraction and indexing. Use it after changing perimeter rules or department structure — existing content is not retroactively re-processed otherwise.