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

# Resource ingestion

> Upload clinical PDFs and related images for OCR, then emit unstructured_report or clinical_note logs into patient state.

<Warning>
  This feature is in beta. Behavior and availability may change as we expand
  support.
</Warning>

Use **resource ingestion** when you have clinical **PDFs** or related **images** (scans, screenshots, print-to-PDF notes) that should become structured logs in Olira. You label the target log type at upload time; Olira stores the binary, runs text extraction / OCR, then emits an event log that flows into [patient state](/read-data/patient-state) like any other submission.

This path is for **document binaries**. For already-structured payloads, use [live logging](/send-data/how-logs-work) or [historical JSONL backfill](/send-data/historical-backfill). For continuous sensor Parquet, use [passive signal ingestion](/send-data/passive-signals).

## Supported materials

| Kind       | Content types                                                                   |
| ---------- | ------------------------------------------------------------------------------- |
| **PDF**    | `application/pdf`                                                               |
| **Images** | `image/png`, `image/jpeg`, `image/jpg`, `image/webp`, `image/tiff`, `image/gif` |

Max file size is **100 MB** per document. You choose the clinical target — the platform does **not** infer `log_type` or `document_type` / `note_type` from the filename.

## Target log types

| `log_type`            | Required labels        | When to use                                                                       |
| --------------------- | ---------------------- | --------------------------------------------------------------------------------- |
| `unstructured_report` | `document_type`        | Raw document blob for extraction (labs, imaging reports, discharge packets, etc.) |
| `clinical_note`       | `note_type` + `source` | Provider-authored note that should land as a `clinical_note` log                  |

**`document_type` values** (for `unstructured_report`): `clinical_note`, `radiology_imaging`, `operative_procedure`, `cardiology_diagnostic`, `pathology_report`, `lab_report`, `genomic_report`, `discharge_summary`, `patient_intake`, `external_records_package`.

**`note_type` values** (for `clinical_note`): `progress_note`, `history_and_physical`, `social_history`, `family_history`, `medical_history`, `surgical_history`, `psychosocial_history`, `financial_history`, `discharge_summary`, `consultation_note`, `other`.

Payload schemas: [clinical note](/reference/log-types/lab-clinical#clinical-note), [unstructured report](/reference/log-types/lab-clinical#unstructured-report).

## Pipeline

```
Upload binary → commit → textize / OCR → emit event log → patient state / views
```

1. **Upload** — request a presigned URL, PUT the file bytes (with matching `Content-Type`), then commit.
2. **Textize** — for born-digital PDFs (Word→PDF, EHR print-to-PDF), Olira prefers the embedded text layer when quality checks pass (chars/page, letter ratio, nonempty pages).
3. **Vision OCR** — scanned PDFs, sparse text layers, and images fall back to vision OCR.
4. **Emit** — Olira writes an `unstructured_report` or `clinical_note` event log and continues through the normal graph / extraction path.

There is **no human confirmation** on the live document path. Commit starts OCR immediately.

## Two ways to send resources

### Live upload (single document)

Best for ongoing document arrival from your backend. Requires **`sdk:event-log`**.

<CodeGroup>
  ```python Python theme={null}
  from datetime import datetime, timezone
  from olira import OliraClient

  client = OliraClient(api_key="YOUR_API_KEY")

  handle = client.upload_document(
      patient_id="8a4fde23-0f1b-4c2a-9d7e-b36c1a5f0e82",
      path="progress-note.pdf",
      log_type="unstructured_report",
      document_type="clinical_note",
      timestamp=datetime(2026, 7, 15, 14, 30, tzinfo=timezone.utc),
      idempotency_key="emr-note-88421",
  )
  doc = handle.wait()  # or handle.poll()
  print(doc.status, doc.event_log_id, doc.ocr_page_count)
  ```

  ```csharp C# theme={null}
  using Olira;

  using var client = new OliraClient(apiKey: "YOUR_API_KEY");

  var handle = client.UploadDocument(
      patientId: "8a4fde23-0f1b-4c2a-9d7e-b36c1a5f0e82",
      path: "progress-note.pdf",
      logType: DocumentLogType.UnstructuredReport,
      documentType: "clinical_note",
      timestamp: new DateTimeOffset(2026, 7, 15, 14, 30, 0, TimeSpan.Zero),
      idempotencyKey: "emr-note-88421");
  var doc = handle.Wait();  // or handle.Poll()
  Console.WriteLine($"{doc.Status} {doc.EventLogId} {doc.OcrPageCount}");
  ```
</CodeGroup>

For a clinical-note target:

<CodeGroup>
  ```python Python theme={null}
  handle = client.upload_document(
      patient_id="8a4fde23-0f1b-4c2a-9d7e-b36c1a5f0e82",
      path="h-and-p.pdf",
      log_type="clinical_note",
      note_type="history_and_physical",
      source="ehr_integration",
      timestamp=datetime(2026, 7, 15, 14, 30, tzinfo=timezone.utc),
      idempotency_key="emr-note-88422",
  )
  ```

  ```csharp C# theme={null}
  handle = client.UploadDocument(
      patientId: "8a4fde23-0f1b-4c2a-9d7e-b36c1a5f0e82",
      path: "h-and-p.pdf",
      logType: DocumentLogType.ClinicalNote,
      noteType: "history_and_physical",
      source: "ehr_integration",
      timestamp: new DateTimeOffset(2026, 7, 15, 14, 30, 0, TimeSpan.Zero),
      idempotencyKey: "emr-note-88422");
  ```
</CodeGroup>

Under the hood the SDK calls:

1. `POST /v1/documents:upload-url`
2. Presigned `PUT` of the binary
3. `POST /v1/documents/{id}:commit`
4. `GET /v1/documents/{id}` while you poll

Method reference: [upload\_document](/reference/sdk/documents/upload-document), [get\_document](/reference/sdk/documents/get-document).

### Historical package (bulk PDFs + logs)

Best for onboarding backfills that mix JSONL-style patient/log rows with PDF binaries. Requires **`sdk:historical-ingest`**.

```python theme={null}
from olira import OliraClient
from olira.models import IngestDocument, IngestRecord, IngestLogSpec

client = OliraClient(api_key="YOUR_API_KEY")

job = client.create_ingestion_job(
    records=[
        IngestRecord.log(
            IngestLogSpec(
                event_type="symptom_report",
                patient_id="MRN-104",
                timestamp="2026-03-01T09:00:00Z",
                payload={"instrument": "esas_r", "symptoms": [{"name": "fatigue", "score": 4}]},
            )
        ),
    ],
    documents=[
        IngestDocument(
            path="discharge-summary.pdf",
            patient_id="MRN-104",
            log_type="unstructured_report",
            document_type="discharge_summary",
            timestamp="2026-03-01T10:00:00Z",
            idempotency_key="MRN-104:doc:discharge-1",
        ),
    ],
    idempotency_key="onboarding-docs-2026-q1",
    require_confirmation=True,
)
```

The SDK allocates upload URLs (`POST /v1/ingestion/jobs:begin`), PUTs each PDF, builds a `manifest.jsonl` that includes `type=document` rows, then creates the historical job. After you **confirm**, OCR and document log materialization run inside the historical workflow before view backfill. See [create\_ingestion\_job](/reference/sdk/ingestion/create-ingestion-job) and [Backfilling historical data](/send-data/historical-backfill) for confirmation, cancel, and progress polling.

## Document statuses

| Status           | Meaning                                                   |
| ---------------- | --------------------------------------------------------- |
| `pending_upload` | Upload URL issued; binary not yet verified                |
| `uploaded`       | Object landed (intermediate)                              |
| `ocr_running`    | Textize / OCR in progress                                 |
| `ocr_complete`   | Text extracted; event log emit in progress (non-terminal) |
| `log_emitted`    | Event log written (`event_log_id` set) — success          |
| `ocr_failed`     | OCR or emit failed (`error` may explain)                  |

Terminal statuses: `log_emitted`, `ocr_failed`.

## What you need

1. **API key** with **`sdk:event-log`** (live upload) or **`sdk:historical-ingest`** (package jobs).
2. **Patient** already in your org (or created in the same historical package via patient rows).
3. A PDF or supported image file, plus the correct `log_type` labels.
4. A stable **`idempotency_key`** so retries do not create duplicate document resources. This is a separate key from the one on [`log_batch` / `log_fhir`](/send-data/how-logs-work#retries-and-idempotency) (`log()` has no key) — document uploads have their own idempotency scope.

## After emit

Once status is `log_emitted`, treat the result like any other log: inspect it in Console observability, read it via state / log query APIs, and expect downstream extraction and view updates according to your org configuration. Re-using the same `idempotency_key` after a document already left `pending_upload` returns a conflict — pick a new key for a new file.
