Skip to content
← Integration quickstart

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

  1. 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.
  2. Run: asynchronous processing for an ingestion request. Save its ID before waiting. Client timeouts do not cancel it.
  3. Document: a source file with an ID that citations can reference.
  4. Search: passages plus scores, citations, availability, usage, and a session ID. Retrieval scores are not confidence percentages.
  5. 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. ingest permits uploads/URLs, search permits retrieval/chat, and artifacts permits 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.
  • formatSearchContext supplies 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 logo

17th Street Labs is DocSlurp’s primary sponsor.