Skip to content
← Documentation

NODE.JS / TYPESCRIPT / TERMINAL

Your documents.
Your next API call.

Upload a file. Watch it index. Retrieve the passage and its source. Bring the evidence into your application, your search UI, or the LLM you already use.

20-second walkthrough · Illustrative data and compressed timing, not a speed benchmark. Five-second GIF
Read the video transcript

Upload handbook.pdf with docslurp upload --watch. Follow extraction, embedding, and indexing progress. Search for parental leave in the returned workspace. A sample result cites handbook.pdf, page 12. Use the same upload, waitForRun, and search sequence from Node.js. Primary sponsor: 17th Street Labs.

01 / FIRST SUCCESS

From a file to cited results.

You need Node.js 22+, a document, and a server API key with ingest and search scopes. Use ESM: save JavaScript as .mjs, or set "type": "module". The SDK includes TypeScript declarations and has no runtime npm dependencies.

Install and run
Terminal
npm install @docslurp/sdk
export DOCSLURP_API_KEY='your-server-api-key'
# Save the example below as quickstart.mjs, then:
node quickstart.mjs ./handbook.pdf "What is the leave policy?"
quickstart.mjs
Node.js 22+
import { createReadStream } from "node:fs";
import { createDocSlurpClient } from "@docslurp/sdk";

// Usage: node quickstart.mjs ./handbook.pdf "What is the leave policy?"
const [filename, q = "What are the key points in this document?"] = process.argv.slice(2);
if (!filename) throw new Error("Pass a document path: node quickstart.mjs ./handbook.pdf");
if (!process.env.DOCSLURP_API_KEY) throw new Error("Set DOCSLURP_API_KEY first.");

const client = createDocSlurpClient();
const { workspace, run, warnings } = await client.upload(createReadStream(filename));
// Save these IDs before waiting: processing can outlive this process.
console.error(JSON.stringify({ workspaceId: workspace.id, runId: run.id }));
for (const warning of warnings ?? []) console.error(warning);
await client.waitForRun(run.id, { timeoutMs: 600_000 });

const evidence = await client.search({ workspaceId: workspace.id, q, limit: 5 });
if (evidence.availability?.status !== "complete") {
  console.error("Check corpus availability:", evidence.availability);
}
// Keep the complete response: citations, availability, and search session ID.
console.log(JSON.stringify(evidence, null, 2));

With no workspace configured, an upload creates one. To append to an existing corpus, set DOCSLURP_WORKSPACE_ID or pass workspaceId. An upload does not update the client's default workspace automatically.

02 / TRY IT FROM THE SHELL

Get evidence without writing a client.

Upload → watch → search
CLI
npm install --global @docslurp/cli
export DOCSLURP_API_KEY='your-server-api-key'
docslurp upload ./handbook.pdf --watch

# Copy the workspace ID printed by the upload:
export DOCSLURP_WORKSPACE_ID='returned-workspace-id'
docslurp search 'What is the leave policy?'
docslurp search 'What is the leave policy?' --context > evidence.txt
docslurp search 'What is the leave policy?' --json > evidence.json

Interactive watches update document/page progress in place; redirected output contains successive snapshots. If the wait expires, resume with docslurp status RUN_ID --watch --timeout 600. The run may still be processing.

OutputUse it for
Default searchReadable results with 200-character previews and page citations.
search --contextFull numbered source passages to pass into an LLM.
search --jsonStructured results, availability, usage, and the search session ID.
chat --jsonAnswer, citations, conversation ID, usage, and runtime.

--json is supported by search and chat; upload/status print human output. Full command reference and shell recipes →

03 / KEEP YOUR MODEL

Retrieval that fits your stack.

Iterate hits directly with for await (const hit of client.search(input)), or await the full response for availability and session metadata. Both forms share one request when you reuse the returned search. Hits arrive after the JSON response, not as a server stream. Search returns evidence without generating an answer. Format it for your current model adapter, or use the structured citations to build a source viewer. Retrieval can still incur embedding/AI charges.

Build a cited context
Node.js
import { createDocSlurpClient, formatSearchContext } from '@docslurp/sdk';

// Set DOCSLURP_API_KEY and DOCSLURP_WORKSPACE_ID first.
const client = createDocSlurpClient();
const evidence = await client.search({ q: 'How do retries work?', limit: 5 });
if (evidence.availability?.status !== 'complete') {
  throw new Error('Wait for the corpus to finish indexing.');
}
if (!evidence.results.length) throw new Error('No supporting evidence.');

const context = formatSearchContext(evidence);
console.log(context);
console.log(evidence.usage); // Tokens + recorded AI cost.
// Pass context to your LLM as untrusted source evidence.
// Keep evidence.results to map citations back to their documents.

Ask your model to cite numbered passages and acknowledge missing evidence. Treat document text as untrusted source data. Keep evidence.sessionId for diagnostics and evidence.results to resolve the citations.

evidence.usage includes inputTokens, outputTokens, totalTokens, and costCents in USD. costSource distinguishes provider-reported, estimated, mixed, unknown, or no recorded AI usage. This covers retrieval only, not ingestion, storage, your own model, or an invoice. Fractional cents are preserved. Native chat returns its answer-generation usage in usage.

For independent questions, searchMany accepts 1–20 queries with up to four concurrent searches. Ordered outcomes retain per-query errors. Batch retrieval example →

04 / ANSWERS WITH SOURCES

Or ask DocSlurp to answer.

Continue from the configured client above. Keep the session ID to ask follow-ups about the same conversation, and inspect availability before presenting an answer as complete.

Ask, inspect, follow up
Node.js
const first = await client.chat({ q: 'Explain the retry policy with sources.' });
console.log(first.answer, first.citations, first.availability);
console.log(first.usage, first.runtime);

const next = await client.chat({
  q: 'Which failures should never be retried?',
  sessionId: first.sessionId,
});
console.log(next.answer);

Await each follow-up in a conversation. SDK chat returns a promise; for streamed tokens, use the compatible chat API.

05 / SHIP THE INTEGRATION

Know what happens when it waits.

BehaviorContract
DeadlinesSDK requests default to 30 seconds. Run waits and streams default to five minutes. Override with timeoutMs; CLI uses seconds.
CancellationPass an AbortSignal to stop the local request. This does not cancel server processing.
Live updatesstreamRun yields authenticated SSE events. Use polling if streaming drops. Runnable fallback.
RetriesSDK run/document reads and replayable uploads with an idempotency key retry selected transient HTTP errors. Streams, search, batch, chat, and URL ingestion are sent once.
Corpus readinessavailability distinguishes empty, pending, partial, and complete. A completed run can coexist with other pending uploads.
KeysKeep server keys on your backend. Browser origin restrictions do not make a service key safe to publish.

Configure another deployment with DOCSLURP_URL (the service origin, without /v1). Explicit options override environment values. Per-call workspace IDs override client defaults.

06 / GO DEEPER

The details, when you need them.

Pre-release: pin the package version you validate. SDK and CLI are Apache-2.0 licensed.