Ingram Cloud

Documentation

Files & vector stores

Files & vector stores

A vector store is a searchable index over your documents: upload files, attach them to a store, and the API chunks, embeds, and indexes them. Agents search the store with the hosted file_search tool; you can also query it directly. The surface mirrors the OpenAI Files and Vector Stores APIs, so an OpenAI client library pointed at the API works unchanged.

Ingest and query embeddings run on your project's OpenAI model key (bring your own or Ingram-hosted) and are metered like any model call. Each store picks its own embedder — including one you host.

Upload files

Files are uploaded once and reusable across stores. Multipart, like OpenAI:

# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/files \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -F purpose=assistants \
  -F file=@handbook.pdf

The response is the OpenAI File object (id, bytes, filename, purpose). PDFs and text formats (Markdown, HTML, JSON, CSV, source code, …) are indexable; other types upload fine but fail indexing with unsupported_file. Indexing reads a PDF's text layer — it does not OCR. A scanned or image-only PDF (no text layer) fails indexing with no_text_layer; OCR it before uploading. GET /v1/files lists uploads, GET /v1/files/{fid} and …/content fetch one back, DELETE /v1/files/{fid} removes it everywhere — including from any vector stores it was attached to.

To see what the indexer made of a file — the chunk text the model actually retrieves, not the bytes you uploaded — read GET /v1/vector_stores/{vsId}/files/{fid}/content. A file that failed indexing has no chunks and reads as an empty page.

Files that arrive inline in a conversation (a screenshot or PDF in a chat turn) live on the same surface: retrievable by id for auditability, but not listed — GET /v1/files enumerates uploads only.

Create a store and attach files

# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/vector_stores \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Support KB", "file_ids": ["file_abc"] }'

Attaching is asynchronous: the vector store file starts in_progress and moves to completed (searchable) or failed (last_error says why — unsupported_file, no_text_layer (a scanned/image-only PDF — OCR it first), invalid_file, server_error). A bulk import can partially succeed, so check each file's status/last_error (or the vector_store.file.failed events) rather than assuming the whole batch landed. The store object's file_counts tracks the fleet; its status is in_progress while anything is still indexing. The indexing queue is durable: files queue in in_progress and every one ends completed or failed — a redeploy or crash mid-indexing resumes where it left off. Attach more later via POST …/{vsId}/files ({"file_id": …}), or in bulk via POST …/{vsId}/file_batches ({"file_ids": […]}; cancel with POST …/file_batches/{batchId}/cancel).

Chunking follows OpenAI: auto (800-token chunks, 400 overlap) or {"type": "static", "static": {"max_chunk_size_tokens": …, "chunk_overlap_tokens": …}} per file.

Choose the embedder. embedding_model names the model that indexes and searches the store. Omit it for the platform default (openai.text-embedding-3-small); name any model your project's OpenAI model key can reach, of at most 1536 dimensions:

# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/vector_stores \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Support KB", "embedding_model": "text-embedding-3-large" }'

Set a base_url on that model key and the store embeds against your endpoint — an EU-hosted OpenAI-compatible embedder keeps the corpus and every query inside the region. The store object returns embedding_model.

The choice is frozen at create: no modify accepts it, because a different model means re-embedding the whole corpus. Create one store per embedder and attach the files again. Create verifies the model by embedding one probe string (metered like any call), so an unreachable model, a bad id, or one wider than 1536 dimensions fails right there with a 422 rather than failing every file you attach.

Attributes and filters. Each file can carry up to 16 typed attributes ({"region": "EU", "year": 2026}) which every search can filter with OpenAI's grammar — comparisons (eq, ne, gt, gte, lt, lte, in, nin) composed with and/or:

{ "type": "and", "filters": [
  { "type": "eq", "key": "region", "value": "EU" },
  { "type": "gte", "key": "year", "value": 2026 }
] }

Search directly

POST /v1/vector_stores/{vsId}/search is semantic search with no model in the loop — use the store as your retrieval layer and generate anywhere:

# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/vector_stores/vs_abc/search \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{ "query": "what is the refund window?", "max_num_results": 5 }'

Results come back as scored chunks (file_id, filename, score, content), OpenAI's search_results.page shape. filters and ranking_options.score_threshold narrow them.

Give an agent the knowledge base

Two config fields on the agent (or per-smith override):

  • enabled_hosted_tools includes "file_search" — the tool itself.
  • vector_store_ids lists the stores it may search.

Every channel the smith runs on — chat, Slack, Discord, email, schedules — gets the tool automatically. A store is tenant-wide by default; create it with a smith_id to scope it to one smith (that smith's token sees only tenant-wide stores and its own).

On the OpenAI-compatible surface, declare it per request instead — the run emits a file_search_call output item, and include: ["file_search_call.results"] returns the retrieved chunks. (One divergence from OpenAI today: file_search cannot be combined with client function tools in the same request.)

# Authorization: smith token (safe for your backend per end-user)
curl https://api.cloud.ingram.tech/v1/responses \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "What does the handbook say about refunds?",
    "tools": [{ "type": "file_search", "vector_store_ids": ["vs_abc"] }],
    "include": ["file_search_call.results"]
  }'

In the console

Build → Vector stores lists your stores; a store's page uploads files, shows per-file indexing status, and has a try-out search that runs the same /search the agent's tool uses. Attach stores to an agent from the agent's Knowledge section (visible once file_search is enabled).

Build → Files lists the files you've uploaded over the API (GET /v1/files) — filename, purpose, size, and status — the ones reusable by file_id on runs or as vector-store sources. Inline bytes sent on a single run aren't uploads and aren't listed here.

Lifecycle events

Indexing emits events: vector_store.file.completed and vector_store.file.failed per file, vector_store.created / .deleted for the store itself.