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

# TypeScript SDK: Knowledge Management

> Insert text, upload files, poll async jobs from TypeScript.

## Insert text

For short content that fits in a JSON body (\< \~1MB):

```typescript theme={null}
const result = await client.insert({
  content: "ACME Corp reported $50M revenue in Q4 2025, up 23% YoY.",
  title: "ACME Q4 Earnings",
  tags: ["finance", "acme"],
  metadata: { source: "internal-slides" },
  wait: true,  // poll job to completion before returning (default)
});

result.job_id;           // "job_abc123"
result.facts_extracted;  // 4
```

### InsertOptions

| Field      | Type                      | Default  | Description                                        |
| ---------- | ------------------------- | -------- | -------------------------------------------------- |
| `content`  | `string`                  | required | The text to ingest.                                |
| `title`    | `string`                  | —        | Human-readable title. Shown in source citations.   |
| `tags`     | `string[]`                | —        | Tags for later filtering (future).                 |
| `metadata` | `Record<string, unknown>` | —        | Free-form metadata stored with the document.       |
| `wait`     | `boolean`                 | `true`   | If true, block until the extraction job completes. |

## Upload a file

PDFs, CSVs, markdown, plain text. Images are extracted from PDFs automatically.

<Tabs>
  <Tab title="Node">
    ```typescript theme={null}
    import { readFile } from "node:fs/promises";

    const buf = await readFile("./earnings.pdf");
    const upload = await client.uploadFile({
      file: buf,
      filename: "earnings.pdf",
      saveToMemory: true,
    });

    upload.upload_id;  // "upload_xyz"
    upload.status;     // "completed" (because wait defaults to true)
    ```
  </Tab>

  <Tab title="Browser / Blob">
    ```typescript theme={null}
    const input = document.querySelector<HTMLInputElement>("#file")!;
    const blob = input.files![0];
    if (blob) {
      await client.uploadFile({ file: blob, filename: blob.name });
    }
    ```
  </Tab>

  <Tab title="Bun">
    ```typescript theme={null}
    const file = Bun.file("./earnings.pdf");
    await client.uploadFile({
      file: await file.arrayBuffer() as unknown as Uint8Array,
      filename: "earnings.pdf",
    });
    ```
  </Tab>
</Tabs>

### Supported file types

* PDF (text + tables + images)
* CSV
* Markdown
* Plain text (.txt)
* DOCX

Max file size depends on your plan: free tier is **10MB**. See [user limits](/sdk-ts/client#getlimits) at runtime.

## Poll async jobs manually

If you set `wait: false` and want to poll yourself:

```typescript theme={null}
const { job_id } = await client.insert({
  content: "...",
  wait: false,
});

while (true) {
  const status = await client.getJobStatus(job_id!);
  if (status.status === "completed") break;
  if (status.status === "failed") throw new Error(status.error_details ?? "ingest failed");
  await new Promise((r) => setTimeout(r, 2000));
}
```

Or use the built-in `waitForJob`:

```typescript theme={null}
await client.waitForJob(job_id!, { pollIntervalMs: 1000, timeoutMs: 60_000 });
```

### JobStatus values

| `status`     | Meaning                          |
| ------------ | -------------------------------- |
| `pending`    | Job accepted, not started.       |
| `chunking`   | Splitting content into chunks.   |
| `extracting` | LLM extracting facts + entities. |
| `storing`    | Writing to Neptune + OpenSearch. |
| `completed`  | Done. Content is now queryable.  |
| `failed`     | See `error_details`.             |

## Upload polling

Uploads have their own status endpoint because the pipeline goes: upload → file processor → fact extraction → ready. `wait: true` handles polling automatically; to do it manually:

```typescript theme={null}
await client.waitForUpload(upload.upload_id);
```
