Haypile
Reference

REST API

The daemon's HTTP API on localhost:11500, plus the MCP endpoint.

The daemon serves JSON over HTTP on localhost:11500. It listens on localhost only and has no auth in v1; do not expose it beyond the machine. Requests from other hosts or browser origins are refused with 403.

POST /api/query

Search. This is the endpoint your scripts want.

curl -s -X POST localhost:11500/api/query \
  -d '{"query": "termination clause", "tag": "", "limit": 5}'

Request:

FieldTypeMeaning
querystringRequired. Search text
tagstringOptional. Restrict to folders with this tag
limitintOptional. Max results, default 10, cap 100
modestringOptional. search (default) returns only results that clear the relevance floors, possibly none. answer falls back to the nearest chunks instead of returning empty; it is what hay ask uses for retrieval

Response:

{
  "results": [
    {
      "path": "/Users/you/Documents/contracts/msa.pdf",
      "page": 4,
      "chunk": 11,
      "snippet": "Either party may terminate...",
      "text": "Either party may terminate this Agreement...",
      "score": 0.0322
    }
  ]
}

page is 1-based and present for paginated formats; 0 means the format has no pages and chunk is the citation. snippet is the short display excerpt; text is the chunk's full text, for callers that answer or reason from the result.

POST /api/ask

Retrieval plus a streamed answer from your local LLM, as server-sent events. This is what hay web uses; point curl -N or an SSE client at it.

curl -sN -X POST localhost:11500/api/ask \
  -d '{"question": "what notice period does the vendor agreement require?"}'

Request:

FieldTypeMeaning
questionstringRequired. The question to answer
tagstringOptional. Restrict retrieval to folders with this tag
limitintOptional. Passages given to the model, default 6, cap 20

The response is text/event-stream with four event types, in order:

EventDataMeaning
sourcesarray of results with labelThe retrieved passages, always first
tokenJSON stringOne piece of the answer, repeated
error{"message": "..."}Generation failed; the stream ends after this
done{}Always last on success

Requires an OpenAI-compatible server, same as hay ask; when none answers, the response is 503 before any stream starts, so clients can branch on the status code.

GET /api/chunk

The passage behind a citation, with its neighbors for context. The web UI's "read in place" view.

curl -s "localhost:11500/api/chunk?path=/Users/you/Documents/msa.pdf&chunk=11&window=1"
ParamMeaning
pathRequired. Source file path as returned by /api/query
chunkRequired. Chunk ordinal from the result
windowNeighboring chunks on each side, 0 to 5, default 1

GET /api/health

Liveness plus identity. Clients use db to confirm they are talking to the daemon for the right index.

{ "ok": true, "version": "0.3.1", "db": "/Users/you/.haypile/haypile.db", "model": "bundled/all-MiniLM-L6-v2" }

GET /api/status

Everything hay status shows: the health fields plus uptime, sources, counts, pending indexing jobs, and the measured outbound connection count.

FieldMeaning
uptime_secondsSeconds since the daemon started
sourcesIndexed folders, each with path, tag, files, chunks
files, chunksTotals across all sources
pending_jobsWatcher changes queued for re-indexing
outbound_connectionsThe daemon's live non-listening TCP connections, measured
outbound_notePresent only when the count could not be measured; explains why
indexingPresent only while an add pass runs: phase (extracting or embedding), files_done/files_total, bytes_done/bytes_total, chunks_done/chunks_total

indexing is what hay add polls to draw its live progress bar; scripts can poll it the same way.

POST /api/shutdown

Asks the daemon to exit gracefully; this is what hay stop calls. Responds {"stopping": true}, finishes in-flight requests, closes the index cleanly, and removes the runtime file on the way out.

GET /api/sources

Indexed folders with counts.

POST /api/sources

Index and watch a folder or file. Runs synchronously and returns the indexing stats.

curl -s -X POST localhost:11500/api/sources \
  -d '{"path": "/Users/you/Documents", "tag": "personal"}'

DELETE /api/sources

Un-index and un-watch. Body: {"path": "..."}. Returns {"removed": true} or false if the path was not indexed.

GET /api/browse and POST /api/pick

Helpers for the web UI's folder picking; scripts rarely need them. GET /api/browse?path=/abs/dir lists a directory's subfolders and indexable files (defaults to the home directory). POST /api/pick?kind=folder|file opens the native OS file dialog and returns the chosen path; 204 means the user canceled, 501 means the platform has no dialog helper.

POST /mcp

The MCP endpoint (Streamable HTTP transport, JSON-RPC 2.0). Supports initialize, tools/list, tools/call, and ping. Tools:

ToolArgumentsReturns
search_documentsquery (required), tag, limitCited passages as text
list_sourcesnoneIndexed folders with counts

Point any MCP client at http://localhost:11500/mcp, or use hay mcp-stdio for stdio-transport clients.

Errors

Non-200 responses carry {"error": "message"}. The codes in use:

CodeWhen
400Malformed request
403Request from another host or browser origin
404/api/browse on an unreadable path, /api/chunk for a chunk that does not exist
405Wrong method, e.g. GET /mcp
409/api/pick while a dialog is already open
503/api/ask when no LLM endpoint answers
500Server fault

MCP tool failures come back inside the JSON-RPC result with isError: true so agent models can read and react to them.

On this page