Skip to main content
POST
When context is of type=knowledge:
  1. Documents - Use the documents field for binary uploads that HydraDB should parse: PDFs, Office and iWork files, spreadsheets, images and plain text. See Supported file formats for the full list.
  2. App Sources - Use app_knowledge for pre-extracted JSON content (Slack threads, Notion pages, emails, tickets). Read more about ingesting knowledge from your apps.
When context is of type=memory Use memories for per-user content, scoped with collection. Set infer: true to let HydraDB extract preferences from raw signals, or infer: false to store the text verbatim. Read more about ingesting memories.
database and collection are the current field names (formerly tenant_id and sub_tenant_id). The old names remain accepted as deprecated aliases for full backward compatibility.

Upload in-memory text as a .txt file

If you already have text in memory, create a file-like object and upload it through documents as a text/plain .txt file.

Important form fields

  1. id must not contain a comma (,). The comma is reserved as the id separator on Ingestion Status (GET /context/status?ids=a,b), so an id containing a comma cannot be looked up unambiguously. This applies to every id you supply - document_metadata, app_knowledge, and memories items. Ingesting an item whose id contains a comma is rejected with a 400.
  2. 202 Accepted means queued, not indexed. Ingestion is asynchronous. A successful response only confirms your sources were accepted, not that they are ready to query. Before querying, poll GET /context/status with the returned IDs until each source reaches completed or errored. Alternatively, register a webhook for indexing.status_changed events (see Webhooks).

Supported file formats

Anything in this table can be uploaded through documents and HydraDB will read it. What HydraDB extracts differs by type, and it is worth knowing which one you are uploading: A scanned page with nothing readable on it, or a photo that happens to contain no text, will index as an empty document rather than fail.

Formats we cannot read

These are rejected the moment you upload them, before anything is queued. You get the answer in the upload response rather than minutes later.
.heic is the format iPhones use for photos by default, so it is the one people hit most often without realizing. On iOS you can change this under Settings > Camera > Formats > Most Compatible, which makes the camera save JPEGs instead. Or export the photo as JPEG or PDF before uploading.

How we decide the format

The file extension is what counts. report.pdf is treated as a PDF because of the .pdf, not because of what is inside it. If the filename has no extension at all, the Content-Type you send with that part is used instead, so a file with no extension in its name, sent as application/pdf, is accepted.
Renaming a file does not convert it. A .heic photo renamed to photo.pdf passes this check, because the check reads the name, then fails later during parsing and comes back as errored on GET /context/status. Upload files under their real extension.

Size limit

Each file in documents can be up to 50 MB. A larger file is rejected with 413 and a message naming the file, and no part of the request is processed. Split large documents, or upload them separately.

When a file is not supported

A rejected file does not stop the rest of your upload. Each file is checked on its own, so a batch of ten with one bad file still indexes the other nine. The request returns 202 as usual. The rejected file comes back with status: "failed" and error_code: "E1002", and every other file is queued normally:
results, success_count and failed_count sit inside data, not at the top level, like every other v2 response. The outer success: true means the request was accepted; data.success is what tells you whether every file in it was queued.
Do not poll GET /context/status for a file rejected with E1002. The file never entered the pipeline, so it has no status record, and looking it up returns FILE_NOT_FOUND rather than the format error you were given. The upload response is the only place E1002 appears. Read error_code on each item in data.results and act on it there.

Common use-cases and their configurations

Document metadata

Per-document metadata (id, metadata, additional_metadata, relations) can be passed alongside each uploaded document to control indexing, filtering, and display. The key list is closed - an item carrying any other key is rejected with a 400 naming it, rather than being silently dropped. See the field reference below.
Those four are the only keys accepted. Anything else - including title, type, url and timestamp - is rejected with a 400 naming the unsupported key, rather than being silently dropped.In particular, a document’s title is derived, not settable. It defaults to the uploaded filename and is returned as source_title on query results and title on /context/list. It cannot be overridden at ingest, and PATCH /context/{id}/metadata only merges additional_metadata and database_metadata. Use additional_metadata for your own display fields, or ingest through app_knowledge, whose items carry an explicit title.
graph_payload is a map of source id → graph that replaces LLM graph extraction for each keyed source. For type=knowledge, the key is a document_metadata id or an app_knowledge item id; for type=memory, the key is a memory id. Keyed sources are still chunked and embedded, so they stay searchable. See Bring Your Own Graph for the full guide.
Per-source replace mode. Each top-level key must match a document_metadata id or app_knowledge item id for type=knowledge, or a memory id for type=memory, in the same request; attach graphs to multiple sources at once. Extraction is skipped for keyed sources. Caps per graph: ≤ 5,000 entities, ≤ 10,000 relations, ≤ 500 relations per entity; over-cap returns 400. Graphs survive re-ingest (re-upload or connector re-sync re-applies the stored graph).

Some important notes

  • Async indexing. 202 Accepted means HydraDB queued the work, not that content is searchable. Poll Ingestion Status until indexing_status reaches graph_creation (searchable) or completed (graph-ready).
  • Multipart, not JSON. This endpoint uses multipart/form-data. Stringify all JSON arrays (metadata, app_knowledge, memories) before placing them in the form field.
  • Declare hot schema fields upfront. Put frequently filtered fields in metadata, define them in database_metadata_schema with enable_match: true, and use additional_metadata for free-form display/bookkeeping fields. Define filterable fields when creating the database via Create Database. Additive schema updates exist, but newly added dense/sparse metadata lanes are not backfilled into existing Milvus collections.
  • Memory vs knowledge. Use type: "memory" for memory ingestion, listing, and deletion. Use type: "all" on POST /query when results should combine both. The multipart field name for memories is always memories.
  • Collection defaulting. Omitting collection writes to the default collection. List available collections with List Collections.
Related Resources

Authorizations

Authorization
string
header
required

API key sent as a Bearer token: "Bearer prefix.secret"

Body

multipart/form-data

Content type: 'knowledge' or 'memory' | Database (canonical name for the tenant scope) | Collection (canonical name for the sub-tenant scope) | Deprecated alias for database | Deprecated alias for collection | Upsert existing content (true/false/1/0) | Knowledge files to ingest (repeatable; type=knowledge) | Per-document metadata as a JSON array (type=knowledge). Per item: metadata <= 16 KiB, additional_metadata <= 1 KiB. | App-knowledge items as a JSON array (type=knowledge). Per item: metadata <= 16 KiB, additional_metadata <= 1 KiB, optional acl principal list (PRO-1684). | Memory items as a JSON array (type=memory). Per item: metadata <= 16 KiB, additional_metadata <= 1 KiB. | Optional bring-your-own-graph payload as JSON

database
string
required
app_knowledge
string

App-knowledge items as a JSON array (type=knowledge). Per item, metadata is capped at 16 KiB and additional_metadata at 1 KiB, measured on the compact JSON encoding of the whole map in UTF-8 bytes (keys and punctuation count). The deprecated tenant_metadata / document_metadata spellings are accepted here and held to the same caps. Over-cap returns 400 with the actual byte count. Each item may also carry acl, a list of principals (user_email:<email>, a bare email, group:<provider>:<id>, domain:<domain>, or __public__) restricting who may retrieve it; omit it to leave the document unrestricted, and send an empty list to restrict it to nobody. A malformed principal rejects the whole request with 400.

collection
string
document_metadata
string

Per-document metadata as a JSON array (type=knowledge). Per item, metadata is capped at 16 KiB and additional_metadata at 1 KiB. Both caps are measured on the compact JSON encoding of the whole map in UTF-8 bytes, so keys, quotes, commas and braces count toward the budget. Over-cap returns 400 with the actual byte count.

documents
file
graph_payload
string
memories
string

Memory items as a JSON array (type=memory). Per item, metadata is capped at 16 KiB and additional_metadata at 1 KiB, measured on the compact JSON encoding of the whole map in UTF-8 bytes (keys and punctuation count). Over-cap returns 400 with the actual byte count.

sub_tenant_id
string
deprecated
tenant_id
string
deprecated
type
enum<string>
default:knowledge
Available options:
knowledge,
memory
upsert
string
default:true

Response

Accepted

data
object
Example:
error
object

Error message, empty string on success.

Example:
meta
object
Example:
success
boolean

Whether the request succeeded.

Example:

true