> ## Documentation Index
> Fetch the complete documentation index at: https://docs.okrapdf.com/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript SDK

> Use @okrapdf/sdk for document sessions, files, collections, and workflows.

## Install

```bash theme={null}
npm install @okrapdf/sdk
```

```ts theme={null}
import { createOkra, OkraClient } from '@okrapdf/sdk';
import { doc } from '@okrapdf/sdk/doc';
```

`createOkra(...)` returns an `OkraClient`. Use it when you want workflow defaults or middleware-style options. Use `new OkraClient(...)` when you want the direct class.

## Upload and ask

```ts theme={null}
import { createOkra } from '@okrapdf/sdk';

const okra = createOkra({ apiKey: process.env.OKRA_API_KEY });

const session = await okra.sessions.create('./report.pdf', {
  wait: true,
  model: 'anthropic/claude-haiku-4.5',
});

const { answer, sources } = await session.prompt('What is total revenue?');

console.log(answer);
console.log(sources);
```

`sessions.create(...)` accepts local file paths in Node, URLs, `Blob`, `ArrayBuffer`, or `Uint8Array`. Use `sessions.from(documentId)` to attach to an existing document.

## Read pages and entities

```ts theme={null}
const status = await session.status();
const pages = await session.pages({ range: '1-3' });
const tables = await session.entities({ type: 'table' });

console.log(status.phase, pages.length, tables.nodes.length);
```

## Structured output

```ts theme={null}
import { z } from 'zod';

const Invoice = z.object({
  vendor: z.string(),
  total: z.number(),
  currency: z.string().optional(),
});

const result = await session.prompt('Extract invoice fields', {
  schema: Invoice,
});

console.log(result.data);
```

## Streaming

```ts theme={null}
for await (const event of session.stream('Summarize in 3 bullets')) {
  if (event.type === 'text_delta') process.stdout.write(event.text);
  if (event.type === 'done') console.log('\nfinal:', event.answer);
}
```

## Passive file assets

Use `files.upload(...)` when you want to store a PDF first and decide later whether to parse, host, or run a workflow.

```ts theme={null}
const file = await okra.files.upload('./invoice.pdf');

console.log(file.id);
console.log(file.urls.bytes);
```

## Host and URL building

The SDK exports a deterministic URL builder for document-derived assets:

```ts theme={null}
const d = doc(session.id, { fileName: 'report.pdf' });

console.log(d.pg[1].png());
console.log(d.pg[1].md());
console.log(d.full.md());
```

For clean hosted PDF deployments, call the REST [Host API](/api-reference/host/create-hosted-pdf) or upload through `files.upload(...)` and publish with `/v1/host`.

## Invoice extraction workflow

```ts theme={null}
const file = await okra.files.upload('./invoice.pdf');

const run = await okra.runInvoiceExtraction({
  inputs: [file.id],
  quality: 'standard',
});

console.log(run.runId, run.exports.csvUrl, run.exports.jsonUrl);
```

## Dynamic agent workflows

The stable SDK surface currently exposes built-in workflows such as invoice extraction. Custom workflow authoring lives in the MCP and REST workflow surfaces:

* Use [MCP](/mcp/overview) when an agent should draft, run, and inspect a workflow.
* Use `POST /v1/workflows` and `POST /v1/runs` when your app wants to create and run a workflow directly.

## Collections

```ts theme={null}
const stream = okra.collections.query(
  'col-abc123',
  'Extract revenue, net income, and EPS.',
);

for await (const event of stream) {
  console.log(event);
}
```

## API surface

```ts theme={null}
createOkra({ apiKey, baseUrl?, sharedSecret?, fetch?, workflow? }) => OkraClient

okra.sessions.create(sourceOrDocId, { wait?, model?, upload?, waitOptions? }) => session
okra.sessions.from(documentId, { model? }) => session

okra.files.upload(input, options?)
okra.files.get(fileId)
okra.files.list(options?)
okra.files.delete(fileId)

session.status()
session.wait()
session.pages({ range? })
session.page(pageNumber)
session.entities({ type?, limit?, offset? })
session.prompt(query, { schema?, model? })
session.stream(query, options?)
session.publish()
session.shareLink({ role, expiresInMs, maxViews, label })
```
