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

# 3. Teach from a document

> Upload a PDF, follow the learning until it is done, then ask a question and receive an answer that points to the file.

This walkthrough shows how to give an agent a document. After you read it, you can upload a file, follow the learning, upload more files in the right order, and show the user which file an answer came from.

You have a handbook, a contract, or a report. You want the agent to know it. Upload the file once. Every thread of that agent can then use it.

## Step 1: check the limits

```bash theme={null}
curl https://app.sapientpriors.com/api/v1/session \
  -H "Authorization: Bearer $SP_API_KEY"
```

`max_upload_bytes` is the largest file you can send. The default is 10 MB. One file at a time per agent. At most 2,000 pieces per file.

## Step 2: upload the file

```bash theme={null}
curl -X POST https://app.sapientpriors.com/api/v1/agents/agent_01J.../threads/thread_01J.../observe-file \
  -H "Authorization: Bearer $SP_API_KEY" \
  -F "file=@employee-handbook.pdf" \
  -F "strategy=structure"
```

`strategy=structure` splits the text at headings, which suits a handbook. Use `auto` when you are not sure.

**Success:** status `202`. The file was converted and split. Learning starts now.

```json theme={null}
{
  "observation_id": "obs_01J...",
  "accepted": true,
  "turns_total": 64,
  "deferred": false,
  "figures": 3,
  "words_total": 14200,
  "duplicate": false,
  "filename": "employee-handbook.pdf",
  "doc_format": "pdf",
  "source_id": "src_01J..."
}
```

* `turns_total: 64`: the handbook became 64 pieces. Each piece becomes one observation.
* `source_id`: save it. Answers point to the file with this id.
* `deferred: true` means a scanned document that is too long to convert inside one request. Priors accepted it and continues in the background. Poll the status.

**If it fails:**

* `413 document_too_large`: the file is larger than 10 MB. Split the PDF.
* `413 document_too_many_turns`: more than 2,000 pieces. Split the file, or set `max_chars=4000`.
* `413 document_too_many_pages` or `document_too_large_for_ocr`: the scan has more pages, or is larger, than text recognition accepts. Split the file.
* `409 document_uploaded_elsewhere`: an upload of this file in another thread stopped partway. Finish it there with `finish-learning`, or send `allow_duplicate=true` to read it again here.
* `400 encrypted_document`: remove the password.
* `400 document_needs_ocr`: the pages are scanned, and text recognition is not available. Export a PDF that contains text.
* `429 rate_limit_exceeded`: only one upload every 10 seconds per subject. Wait `Retry-After` seconds.
* `502 document_service_unavailable`, `ocr_failed`, or `document_pages_unexpected`: the document converter or text recognition failed. The file is fine. Wait, then try again.
* `503 ocr_busy`: text recognition is busy with other uploads. Try again in a few minutes.

## Step 3: follow the learning

```bash theme={null}
curl https://app.sapientpriors.com/api/v1/agents/agent_01J.../threads/thread_01J.../observe/obs_01J.../status \
  -H "Authorization: Bearer $SP_API_KEY"
```

```json theme={null}
{"status": "processing", "turns_total": 64, "turns_processed": 31, "queue_depth": 0}
```

Call again every two seconds. A document of 64 pieces takes a few minutes. Stop when `status` is `committed`.

**If it stops:**

* `failed`: read `error`. Usually one piece could not be read. Call `POST .../threads/{thread_id}/sources/{source_id}/finish-learning` to finish it from the stored copy, which reads and charges only what is left. See [Sources](/docs/concepts/sources#finish-an-upload-that-stopped).
* `interrupted`: a Priors restart paused it. It resumes by itself. Keep polling.

## Step 4: upload a second file

You do not need to wait for the first file. An agent reads one file at a time, so a second file sent now is accepted with `"queued": true` and starts by itself when the first one is `committed`. Plain text and Markdown work too:

```bash theme={null}
curl -X POST .../observe-file -F "file=@expense-policy.md" \
  -H "Authorization: Bearer $SP_API_KEY"
```

Ten files mean ten uploads, one after another, at most one every 10 seconds. If you upload the same file twice by mistake into this thread, the second call returns `202` at once with `"duplicate": true` and costs nothing. Into another thread of this agent it returns `409 document_already_ingested` instead, because what the first read learned is already searchable from every thread. Its `document_state` is `completed` when the first upload already finished, or `ingesting` when the first upload is still running and your call joined it.

## Step 5: ask a question and receive a citation

```bash theme={null}
curl -X POST https://app.sapientpriors.com/api/v1/agents/agent_01J.../threads/thread_01J.../chat \
  -H "Authorization: Bearer $SP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"turn_id": "0f6e3b2a-9c1d-4e7f-8a5b-2d4c6e8f0a1b", "message": "How many days of parental leave do we get?"}'
```

**Success:** status `200`.

```json theme={null}
{
  "message": "Sixteen weeks of paid parental leave, and up to a further eight unpaid.",
  "citations": [
    {"source_id": "src_01J...", "label": "employee-handbook.pdf", "media_type": "application/pdf",
     "href": "/api/v1/agents/agent_01J.../threads/thread_01J.../sources/src_01J...", "verified": false}
  ]
}
```

`citations[0].href` downloads the handbook. Show it as a link next to the answer. `verified` is `false` here, because the handbook reached the answer through a memory, not as a file attached to this message. That is the normal case for documents.

With `observe-and-respond` instead of `chat`, the same information arrives as `used_source_ids`.

## Step 6: see which files the agent holds

```bash theme={null}
curl "https://app.sapientpriors.com/api/v1/agents/agent_01J.../sources?limit=20" \
  -H "Authorization: Bearer $SP_API_KEY"
```

This lists every file the agent learned from. Use it to show users what their agent knows.

## What this walkthrough showed

* One upload gives one `observation_id`, and you poll it in the same way as for `observe`.
* Files belong to the agent. Upload them in any thread. Every thread can point to them.
* Upload files one after another for one agent. For different agents, three uploads can run at the same time in your organization.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.