File Search

The Responses API can answer questions grounded in documents and return inline citations back to the source. You attach documents (or knowledge-base stores) to a request as a file_search tool; the model decides when to search them, and the answer comes back with url_citation annotations pointing at the exact passages it used.

Search is agentic: the model chooses whether and when to search, rather than the server always injecting document text. A turn may search once, several times, or not at all, and citations only appear when the model actually grounds its answer in retrieved passages.

Three capabilities are exposed from a single file_search tool, selected by what you attach:

  • Search uploaded documents: searches only the files the user attached this conversation (scoped by attachments). Returns citable passages.

  • Search knowledge base(s): searches whole search stores (search_store_ids) with no per-document filter, for retrieval across a corpus the user did not upload this turn. Returns citable passages.

  • Read a whole document: pulls the full text of an attached document when the model needs broad context rather than specific passages. This grounds the answer but produces no citations.

Document content is backed by Pharia Data v2 (the document search service); the caller’s Authorization header is forwarded to it.


How It Works

  1. The client adds one file_search tool to tools[], listing uploaded attachments and/or search_store_ids.

  2. During generation the model may call to search the uploads, search the knowledge base, or read a full document, as many times as it needs.

  3. Each search runs server-side against the document search service and returns numbered, citable passages to the model.

  4. The model writes inline [1], [2] markers where it uses a source.

  5. The response carries a file_search_call output item (the search + its results) and url_citation annotations on the answer text, each mapping a marker to its source passage.

Searches that find nothing, or turns where the model chooses not to search, simply produce no citations; the request still succeeds.


Sending a Request

Add a single tool of type file_search to tools[]. Set stream: true for SSE.

curl -X POST "$BASE_URL/v1/responses" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-32b-tool",
    "input": "What does the report say about Q3 revenue?",
    "tools": [
      {
        "type": "file_search",
        "attachments": [
          {
            "searchStoreId": "ss_abc",
            "fileId": "file_123",
            "documentId": "doc_123",
            "fileName": "q3-report.pdf"
          }
        ]
      }
    ]
  }'

Tool fields:

Field Type Description

type

"file_search"

Required.

attachments

array

Uploaded documents to search/read. Each carries searchStoreId, fileId, documentId, fileName. Attachment keys accept camelCase or snake_case. Enables "search uploaded documents" and "read a whole document".

search_store_ids

array of string

Knowledge-base stores to search whole (no document filter). Enables "search knowledge base(s)". May be combined with attachments.

Notes:

  • When the model calls file_search / search_knowledge_base, it provides a query string argument (usually derived from the user question).

  • A file_search tool with neither attachments nor search_store_ids is a no-op.

  • The Authorization header is forwarded to the document service. Everything else is standard /v1/responses.

  • Graceful degradation: if document search is unavailable, the request still answers, without document grounding and without citations. No error is surfaced.


The Response

A grounded answer returns two kinds of output:

  1. A file_search_call output item: the search and its results (one per search the model ran).

  2. An assistant message whose output_text carries url_citation annotations, one per [n] marker the model wrote.

url_citation annotation

{
  "type": "url_citation",
  "start_index": 42,
  "end_index": 45,
  "title": "q3-report.pdf",
  "url": "/api/v1/search-stores/ss_abc/files/file_123/public-urls",
  "snippet": "the cited passage text, for hover/preview",
  "sherlock": {
    "search_store_id": "ss_abc",
    "file_id": "file_123",
    "document_id": "doc_123",
    "chunk_id": "chunk_7",
    "page_number": 3,
    "section_path": ["Section 2", "Q3"]
  }
}

The marker [n] in the answer maps to source number n. start_index/end_index are the marker’s character offsets in the text. The sherlock block is present only for keys the document service returned. Citation numbers stay stable and de-duplicated across multiple searches within one answer; the same passage keeps the same [n].

Streaming events

For a streamed file_search turn the notable events, in order, are:

  • response.output_item.added / response.file_search_call.in_progress / .searching / .completed / response.output_item.done: the file_search_call lifecycle (repeated if the model searches more than once).

  • response.output_text.delta …: the answer text.

  • response.output_text.annotation.added: one per [n] marker, carrying a url_citation (emitted before response.output_text.done).

  • response.completed, then [DONE].

Reading a whole document is transparent: it grounds the answer but emits no file_search_call and no citations.


Multi-Turn

Attachments and stores remain in scope across follow-up turns of the same conversation, so the model can keep answering from, and citing, an earlier upload without it being re-sent each turn. Citations are computed per turn: a follow-up that doesn’t search produces no citations, even when a document is still attached.

On reload there is no need to replay the stream; the file_search_call results and the url_citation annotations are persisted on the stored response and returned by the conversation/response retrieval endpoints.


Configuration

File search is controlled via environment variables with the FILE_SEARCH_ prefix, plus the agentic loop toggle (file search runs inside the agentic loop, so it must be enabled).

Variable Default Description

FILE_SEARCH_BASE_URL (alias SHERLOCK_API_URL)

Base URL of the document search service. When set, hosted file search is enabled.

FILE_SEARCH_MAX_RESULTS

8

Maximum passages returned per search and exposed as citable sources.

FILE_SEARCH_PAGE_LIMIT

100

Maximum pages fetched when reading a whole document.

FILE_SEARCH_MAX_PREFETCH_CHARS

120000

Safety cap on whole-document text fed to the model.

AGENTIC_LOOP_ENABLED

false

Must be true; file search executes inside the agentic loop.

When file search is disabled (or the agentic loop is off), a file_search tool on a request is ignored and the turn proceeds as a normal completion.