Integrate document search into your application
Send files to DocSlurp, keep the returned IDs, and retrieve passages with citations. Use grounded chat when you want the service to generate an answer; use search when you already have an LLM or need a search UI.
| Start here | Best for |
|---|---|
| Node.js / TypeScript SDK | Application code, streams, deadlines, typed responses |
| CLI | Exploring documents, shell scripts, CI, agent tooling |
| Runnable Node examples | Upload → wait → search, SSE fallback, portable LLM context |
| HTTP recipe below | Other languages and direct API integrations |
| 20-second demo | Seeing the workflow before implementing it |
The contract in one minute
- Workspace: the corpus you search and the owner of pipeline settings. Upload without a configured workspace to create one, or supply an ID to append.
- Run: asynchronous processing for an ingestion request. Save its ID before waiting. Client timeouts do not cancel it.
- Document: a source file with an ID that citations can reference.
- Search: passages plus scores, citations, availability, usage, and a session ID. Retrieval scores are not confidence percentages.
- Chat: a generated answer with citations, availability, conversation IDs, usage, and runtime.
A completed run does not guarantee every document in the workspace is ready: other runs can still be indexing. Check response availability as well as run completion.
The complete HTTP flow
Requires Bash, curl, jq, an existing handbook.pdf, and a server key with ingest and search scopes. The script below stops on HTTP errors, stopped runs, and a bounded polling deadline. It stores the accepted IDs before polling and does not repeat an upload on timeout.
export DOCSLURP_API_KEY='your-server-api-key'
# Optional for another deployment:
# export DOCSLURP_URL='http://localhost:4321'
Save as ingest-and-search.sh, then run bash ingest-and-search.sh:
#!/usr/bin/env bash
set -euo pipefail
: "${DOCSLURP_API_KEY:?Set DOCSLURP_API_KEY}"
api_url="${DOCSLURP_URL:-https://docslurp.io}"
api_url="${api_url%/}"
curl --fail-with-body --silent --show-error --max-time 120 \
"$api_url/v1/ingest/bulk" \
-H "Authorization: Bearer $DOCSLURP_API_KEY" \
-F '[email protected]' > accepted.json
workspace_id="$(jq -er '.workspace.id' accepted.json)"
run_id="$(jq -er '.run.id' accepted.json)"
printf 'Workspace: %s\nRun: %s\n' "$workspace_id" "$run_id"
# Keep accepted.json: use these IDs to resume after a timeout.
deadline=$((SECONDS + 600))
while true; do
remaining=$((deadline - SECONDS))
if (( remaining <= 0 )); then
printf 'Wait expired; run %s may still be processing.\n' "$run_id" >&2
exit 1
fi
request_seconds=$((remaining < 30 ? remaining : 30))
progress="$(curl --fail-with-body --silent --show-error --max-time "$request_seconds" \
"$api_url/v1/runs/$run_id/progress" \
-H "Authorization: Bearer $DOCSLURP_API_KEY")"
state="$(jq -r '.run.controlState // .run.status' <<< "$progress")"
if [[ "$(jq -r '.run.status' <<< "$progress")" == 'failed' ]]; then printf '%s\n' "$progress" >&2; exit 1; fi
case "$state" in
paused|cancelled|dead-letter|failed) printf '%s\n' "$progress" >&2; exit 1 ;;
esac
if [[ "$(jq -r '.run.status' <<< "$progress")" == 'completed' ]]; then break; fi
sleep 2
done
jq -n --arg workspaceId "$workspace_id" --arg q 'What is the parental leave policy?' \
'{workspaceId: $workspaceId, q: $q, limit: 5}' |
curl --fail-with-body --silent --show-error --max-time 30 \
"$api_url/v1/search" \
-H "Authorization: Bearer $DOCSLURP_API_KEY" \
-H 'Content-Type: application/json' --data-binary @- > evidence.json
jq '{availability, sessionId, usage, results: [.results[] | {text, score, citation}]}' evidence.json
Inspect availability.status before using the result as complete evidence. To append on upload, add -F "workspaceId=$DOCSLURP_WORKSPACE_ID". Keep API credentials out of browser bundles. The base URL is the service origin; routes below already include /v1.
Add a grounded answer
Use the workspace ID saved in accepted.json:
jq -n --arg workspaceId "$(jq -r '.workspace.id' accepted.json)" \
--arg q 'Summarize the parental leave policy and cite your sources.' \
'{workspaceId: $workspaceId, q: $q}' |
curl --fail-with-body --silent --show-error --max-time 120 \
"${DOCSLURP_URL:-https://docslurp.io}/v1/chat" \
-H "Authorization: Bearer $DOCSLURP_API_KEY" \
-H 'Content-Type: application/json' --data-binary @- > answer.json
jq '{answer, citations, availability, sessionId, usage, runtime}' answer.json
Pass the returned sessionId in the next chat request to continue. Search does not generate an answer; chat does. Retrieval can incur embedding/AI costs even without answer generation.
Route map
| Operation | HTTP endpoint | SDK | CLI |
|---|---|---|---|
| Upload files | POST /v1/ingest/bulk (multipart files) |
upload |
upload |
| Ingest URLs | POST /v1/ingest/url |
ingestUrls |
Use SDK/HTTP |
| Run progress | GET /v1/runs/:id/progress |
getRun, waitForRun |
status, status --watch |
| Run events | GET /v1/runs/:id/events/stream |
streamRun |
Watch uses polling |
| List runs | GET /v1/runs |
Use HTTP | runs |
| Retrieve passages | POST /v1/search or GET /v1/search |
search |
search |
| Batch retrieval | POST /v1/search/batch |
searchMany |
search --queries-file |
| Grounded chat | POST /v1/chat |
chat |
chat |
| Read a document | GET /v1/documents/:id |
getDocument |
Use SDK/HTTP |
/v1/search/chat is also supported; the Node SDK and CLI use /v1/chat. Token streaming uses the separate compatible chat interface. See search contracts for request fields, availability, batch outcomes, and retrieval controls.
Make failures recoverable
| Event | Application response |
|---|---|
| Upload accepted with warnings | Store IDs and warnings; follow the existing run. |
| Wait deadline expires | Check the stored run ID later; do not assume the upload failed. |
| Failed/stopped run | Surface the run error/control state and inspect the dashboard. |
| SSE drops or ends | Poll with waitForRun; use one shared overall deadline. Example. |
| Empty / pending / partial corpus | Wait, show a partial-results state, or decline to answer according to your application’s policy. |
Batch response contains ok: false |
Handle each query separately; successes remain usable. |
Chat returns finishReason: "length" |
Treat the answer as truncated; inspect usage and decide whether another call is warranted. |
| HTTP 401 / 403 | Check key validity, deployment, scopes, and workspace access. |
| HTTP 429 / transient server error | Apply a bounded policy appropriate to the operation; do not blindly repeat billable calls. |
The SDK retries run/document reads and replayable uploads with an explicit idempotency key for selected transient HTTP statuses. It does not automatically retry streams, URL ingestion, search, batches, or chat. The CLI does not retry failed requests. Exact SDK policy.
Authentication and integration boundaries
- Create/revoke keys at API keys.
ingestpermits uploads/URLs,searchpermits retrieval/chat, andartifactspermits document reads. Run progress permits any of those scopes. - Use
Authorization: Bearer <key>. Keep service keys on the server. For your frontend, proxy requests through your authenticated backend and enforce your own workspace access policy. - Browser origin restrictions complement authentication; they do not turn a service key into a public key.
formatSearchContextsupplies evidence to any LLM. Keep document text separate from instructions and retain structured citations for source inspection.- Node SDK and CLI require Node.js 22+. Packages are ESM. No Python SDK is implied by these examples; other languages can call HTTP directly.
Primary sponsor
17th Street Labs is DocSlurp’s primary sponsor.