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

# Record Retrieval Guide

> Submit medical record retrieval requests, track them with webhooks, and download available records and provider invoices

## Overview

Use the Record Retrieval API to ask Tavrn to retrieve medical records, billing records, and provider invoices from a healthcare facility.

The basic flow is:

1. Create or select a case and, if needed, configure a Requestor profile.
2. Upload the signed HIPAA authorization form and any profile documents.
3. Create a retrieval request.
4. Track the request with webhooks and incremental polling.
5. Download records and provider invoices as they become available.

```mermaid theme={null}
flowchart TD
  A[Create or select case and optional Requestor] --> B[Upload authorization and profile documents]
  B --> C[Create retrieval request]
  C --> D[Track status and document availability]
  D --> E{Update}
  E -->|action required| H[Respond to action request]
  H --> D
  E -->|documents available| F[Download available files]
  F --> D
  E -->|still processing| D
  E -->|completed or cancelled| G[Check for later updates]
```

## What You'll Need

* API key with `retrievals:write` and `retrievals:read` scopes (plus `cases:write` if you'll create the case via the API in step 1)
* `requestors:read` if you'll list or read Requestor profiles; `requestors:write` if you'll create or configure one, including inline creation
* A signed HIPAA authorization PDF for the patient
* Patient name (date of birth if available)
* A facility ID from [facility search](#find-a-facility), or the facility name and address
* At least one record type to request

## Step-by-Step Guide

### 1. Create or Select a Case

Every new Enterprise API retrieval request needs a case. If you already have a case, skip to step 2 and use its `id`. Otherwise, create one:

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/cases \
  -H "Authorization: Bearer tavrn_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Jane Doe v. ACME Corp",
    "client_name": "Jane Doe"
  }'
```

Use the returned case `id` when you create the retrieval request.

You can also create the case in the same call as its first retrieval using
[`POST /v2/retrieval-requests`](#create-a-case-and-requestor-inline). Upload files
first using the retrieval upload endpoint, which does not require a case ID.

### 2. Upload the Authorization Form

Request an upload URL for the signed HIPAA authorization PDF:

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/cases/case_abc123/sign-upload \
  -H "Authorization: Bearer tavrn_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "file_name": "jane_doe_hipaa.pdf",
    "file_type": "application/pdf"
  }'
```

The case upload endpoint accepts `file_name` and `file_type` for a retrieval
attachment.

**Response:**

```json theme={null}
{
  "upload_id": "11111111-1111-4111-8111-111111111111",
  "upload_url": "https://upload-url.example.com/...",
  "expires_at": "2025-02-01T13:34:56Z"
}
```

Upload the file to the returned `upload_url`:

```bash theme={null}
curl -X PUT "https://upload-url.example.com/..." \
  -H "Content-Type: application/pdf" \
  --data-binary @jane_doe_hipaa.pdf
```

Save the `upload_id`. You will use it in `auth_form` when creating the retrieval request.

<Note>
  Upload supporting files the same way, one file per `sign-upload` call. Use the returned `upload_id` values in `attachments`.
</Note>

### 3. Create the Retrieval Request

This minimal example requests medical records for one patient from one facility:

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/cases/case_abc123/retrieval-requests \
  -H "Authorization: Bearer tavrn_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "patient": {
      "name": "Jane Doe",
      "date_of_birth": "1989-04-12"
    },
    "facility": {
      "name": "Example General Hospital",
      "address_line1": "100 Example Ave",
      "city": "Springfield",
      "state": "PA",
      "zip_code": "00000",
      "fax": "+1-555-010-1111"
    },
    "service_start_date": "2024-03-15",
    "service_end_date": "2024-09-30",
    "requested_records": [
      {
        "type": "medical_records"
      }
    ],
    "auth_form": {
      "upload_id": "11111111-1111-4111-8111-111111111111",
      "file_name": "jane_doe_hipaa.pdf"
    }
  }'
```

**Response (abridged):**

```json theme={null}
{
  "id": "rr_abc123",
  "order_number": "T-ABC-12345",
  "case_id": "case_abc123",
  "requestor_id": "111111111111111111111111",
  "status": "received",
  "created_at": "2025-02-01T12:50:00Z",
  "updated_at": "2025-02-01T12:50:00Z"
}
```

Save the retrieval request `id`. Webhook events reference it as `retrieval_request_id`, and you use it to fetch status and download returned files.

Both creation endpoints accept `patient` and `requested_records`, plus exactly one
of a `facility_id` from search or a manual `facility` object:

| Endpoint | Case selection | Requestor selection |
| - | - | - |
| `POST /v2/cases/{case_id}/retrieval-requests` | Uses the case in the URL; inline `case` is not accepted | Supply `requestor_id`, supply an inline `requestor`, or omit both for the organization default |
| `POST /v2/retrieval-requests` | Supply exactly one of an existing `case_id` or an inline `case` object | Supply `requestor_id`, supply an inline `requestor`, or omit both for the organization default |

For organizations with duplicate detection enabled, creation can return `409 duplicate_request_detected` with a `duplicates` array. Review those matches before retrying with `"allow_duplicate": true` if the new request is intentional.

#### Find a facility

Search by facility name, alias, phone, fax, address, or NPI using an API key with
`retrievals:read`:

```bash theme={null}
curl 'https://enterprise.tavrn.ai/v2/facilities?q=Example&state=PA&limit=5' \
  -H "Authorization: Bearer tavrn_sk_live_..."
```

```json theme={null}
{
  "results": [
    {
      "id": "facility_example",
      "name": "Example General Hospital",
      "requirements": []
    }
  ]
}
```

Choose a result and replace the `facility` object in your creation body with
`"facility_id": "facility_example"`. Send exactly one of `facility_id` or
`facility`. If the API reports an invalid or expired facility selection, search
again and use the new ID.

Review any listed requirements before submitting. Each includes a label,
optional description, category (`input`, `document`, or `instruction`), and
applicable `record_types`; an empty `record_types` list means all record types.
Document requirements may include `template_download_url` for a blank template.
The link is valid for 1 hour; repeat the search for a fresh link.

Search text must be 3–200 characters after trimming, with at least 3 alphanumeric
characters. `state` is optional; `limit` is 1–10 and defaults to 10. Results are not
paginated. On `429`, wait for `Retry-After`; on `503`, try again later.

#### Select a Requestor

A Requestor is a retrieval profile within your organization: its name, letter of
representation, RFC form, affidavits, correspondence settings, and provider invoice
approval limit. To use an existing profile, add its ID to the retrieval body:

```json theme={null}
{
  "requestor_id": "111111111111111111111111"
}
```

Omitting both `requestor_id` and `requestor` keeps the existing organization
default. Each retrieval retains the selected ID. Configuration updates to that
profile apply to its linked retrievals.

Find available profiles with
[`GET /v2/requestors`](/api-reference/v2/endpoint/requestors/list-requestors),
or read one with
[`GET /v2/requestors/{requestor_id}`](/api-reference/v2/endpoint/requestors/get-requestor).
Both require `requestors:read`. List responses contain `results`, `next`, and
`previous`; `limit` defaults to 20 and has a maximum of 100. Each profile
includes `is_default`. An unavailable profile ID returns `404`.

If you need a different name, packet documents, correspondence settings, or
invoice limit, you can create a reusable profile before submitting the
retrieval. Give it a nonblank name using
[`POST /v2/requestors`](/api-reference/v2/endpoint/requestors/create-requestor),
which requires `requestors:write`:

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/requestors \
  -H "Authorization: Bearer $TAVRN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Example Firm — Records Team",
    "provider_invoice_limit": 150
  }'
```

Omitted document and correspondence settings start empty. An omitted invoice
limit uses \$100. Creating a profile does not change the default profile.
Each successful POST creates another profile. Save the returned `id` and use it
as `requestor_id` for this and later retrievals. You can also supply an inline
`requestor` on either retrieval creation endpoint; it requires `requestors:write`
and cannot be combined with `requestor_id`.

#### Upload Requestor Documents

To include a letter of representation in retrieval packets, upload it to the
selected profile. A profile without its own document does not use the default
profile's document.

Request an upload URL with
[`POST /v2/requestors/sign-upload`](/api-reference/v2/endpoint/requestors/sign-upload),
then PUT the PDF bytes to the returned `upload_url`:

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/requestors/sign-upload \
  -H "Authorization: Bearer $TAVRN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "file_name": "letter-of-representation.pdf", "file_type": "application/pdf" }'

curl -X PUT "https://upload-url.example.com/..." \
  -H "Content-Type: application/pdf" \
  --data-binary @letter-of-representation.pdf
```

Use the returned `upload_id` in the profile create or update body. Profile
documents must be readable, unencrypted PDFs with a `.pdf` extension and
`application/pdf` type, of at most 25 MiB. Upload bytes within 15 minutes; the
upload ID expires after one hour. These upload IDs are for Requestor documents.
Authorization forms and retrieval attachments use the
[retrieval upload endpoint](/api-reference/v2/endpoint/retrieval-requests/sign-upload).

For example, attach the uploaded letter to an existing profile with
[`PATCH /v2/requestors/{requestor_id}`](/api-reference/v2/endpoint/requestors/update-requestor):

```bash theme={null}
curl -X PATCH https://enterprise.tavrn.ai/v2/requestors/111111111111111111111111 \
  -H "Authorization: Bearer $TAVRN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "letter_of_rep": { "upload_id": "11111111-1111-4111-8111-111111111111" }
  }'
```

Read responses include document metadata such as `file_name`, `page_count`, and
`uploaded_at`.

#### Create a Case and Requestor Inline

Use `POST /v2/retrieval-requests` to create the case, Requestor, and retrieval in
one request. This request requires `retrievals:write`, `cases:write`, and
`requestors:write`.

First upload the signed authorization through
[`POST /v2/retrieval-requests/sign-upload`](/api-reference/v2/endpoint/retrieval-requests/sign-upload).
For profile documents such as the letter of representation, follow
[Upload Requestor Documents](#upload-requestor-documents).
Both take `file_name` and `file_type`; PUT each file to its returned `upload_url`.
Use each upload ID with the matching document field. Upload the bytes within
15 minutes and use the `upload_id` within one hour.

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/retrieval-requests \
  -H "Authorization: Bearer $TAVRN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "case": {
      "title": "Jane Doe — Example Matter",
      "client_name": "Jane Doe"
    },
    "requestor": {
      "name": "Example Firm — Records Team",
      "provider_invoice_limit": 150,
      "letter_of_rep": {
        "upload_id": "11111111-1111-4111-8111-111111111111"
      }
    },
    "patient": {
      "name": "Jane Doe",
      "date_of_birth": "1990-01-01"
    },
    "facility": {
      "name": "Example Clinic",
      "address_line1": "123 Example Street",
      "city": "Springfield",
      "state": "IL",
      "zip_code": "62701"
    },
    "requested_records": [{ "type": "medical_records" }],
    "auth_form": {
      "upload_id": "22222222-2222-4222-8222-222222222222",
      "file_name": "authorization.pdf"
    }
  }'
```

The response includes the new `case_id`, `requestor_id`, and retrieval `id`.

Upload files before creating the retrieval. POST creation is not idempotent: each
successful request with an inline `case` or `requestor` creates a new one. Retain
the returned IDs and use `case_id` and `requestor_id` for subsequent retrievals.

Use `case_id` instead of `case` for an existing case, and `requestor_id` instead of
`requestor` for an existing profile. Exactly one case choice is required. Do not
send both alternatives for either resource. The case-scoped creation endpoint
continues to use the case in its URL and does not accept an inline case.

#### Update Requestor Settings

Use [`PATCH /v2/requestors/{requestor_id}`](/api-reference/v2/endpoint/requestors/update-requestor)
to update a profile's settings for linked retrievals. It requires `requestors:write`
and at least one field. Omitted fields remain unchanged.

| Setting | Behavior |
| - | - |
| `name` | A nonblank override, or `null` to use the organization name. |
| `provider_invoice_limit` | Nonnegative USD amount. `null` uses \$100; `0` prevents automatic payment of positive provider costs. |
| `letter_of_rep`, `rfc_form` | An `upload_id` replaces the document; `null` removes it. |
| `affidavits.declared_families` | Replaces the declaration; `[]` records an explicit answer of none, including on a new profile. Omitting the declaration without uploading exact-family documents leaves it unanswered or preserves the previous answer. Families are `records`, `billing`, and `radiology`. When omitted, supplied document families are added to the current declaration. |
| `affidavits.documents` | Replaces all exact-family documents. Each entry has `family` and `upload_id`; one entry per family. `[]` removes them all. If `declared_families` is supplied too, it must include every uploaded family. |
| `affidavits.generic` | A fallback affidavit upload, or `null` to remove it. Updating exact-family documents leaves it unchanged. |
| `correspondence` | Omitted settings stay unchanged; `null` clears all overrides. A supplied `notice` replaces the notice; supplied `billing_request` fields merge. |

Read responses include `effective_provider_invoice_limit`, so you can see the
limit used when `provider_invoice_limit` is `null`. The API does not delete
profiles, change the organization default, or reassign an existing retrieval
to another profile.

### 4. Track Status

[Subscribe to retrieval request webhooks](/guides/v2/webhooks#retrieval-request-events) for status updates, action requests, and available records or invoices:

| Event | When it fires |
| - | - |
| `retrieval_request.status_changed` | A public status or timeline change, including another update within the same stage |
| `retrieval_request.action_required` | An action request is opened or reopened for your firm |
| `retrieval_request.documents_available` | A new record or provider invoice is available |

When an event arrives, use its retrieval request ID to fetch the current request:

```bash theme={null}
curl https://enterprise.tavrn.ai/v2/retrieval-requests/rr_abc123 \
  -H "Authorization: Bearer tavrn_sk_live_..."
```

Use `status` for the overall request state and `stage` to show its progress in Tavrn.

**`status` values:**

| Status | Meaning |
| - | - |
| `received` | Request submitted and queued |
| `processing` | Tavrn is working the request with the facility (including handling any provider invoice) |
| `action_required` | Tavrn needs something from your firm before it can continue |
| `completed` | Records were received and are available for download |
| `cancelled` | Request was cancelled before completion |

`completed` and `cancelled` are final statuses. Both trigger a `retrieval_request.status_changed` event. Continue incremental polling to pick up later document changes.

<Note>
  When `status` is `action_required`, read `pending_action_request` for what your firm needs to provide. [Respond through the API](#responding-to-action-requests) or follow `platform_url` to handle it in Tavrn. After a response, the request returns to `processing`.
</Note>

**`stage` values:**

| Stage | Label | Meaning |
| - | - | - |
| `submitted` | Submitted | Request received by Tavrn |
| `locating_contact` | Retrieving Contact | Finding the best way to reach the facility |
| `contact_found` | Contact Retrieved | Facility contact confirmed; request about to be sent |
| `request_sent` | Correspondence Sent | Request sent to the facility (fax, mail, email, or portal) |
| `confirming_receipt` | Confirming Receipt | Following up to confirm the facility received the request |
| `acknowledged` | Request Acknowledged | Facility confirmed receipt and is processing the request |
| `invoice_received` | Invoice Received | The facility sent an invoice; Tavrn is processing it |
| `action_required` | Action Required | Your firm needs to respond (see note above) |
| `completed` | Completed | Records are available for download |
| `cancelled` | Cancelled | Request was cancelled |

`stage_label` and `stage_description` carry the exact text shown on the dashboard for the current stage. Not every request passes through every stage — for example, a request whose facility contact is already known goes straight to `request_sent`.

**Example response (abridged):**

```json theme={null}
{
  "id": "rr_abc123",
  "status": "processing",
  "action_required_reason": null,
  "stage": "acknowledged",
  "stage_label": "Request Acknowledged",
  "stage_description": "Your request is in progress with the provider. We follow up with them regularly and will post updates here.",
  "timeline": [
    {
      "stage": "submitted",
      "stage_label": "Submitted",
      "state": "completed",
      "occurred_at": "2025-02-01T12:50:00Z",
      "description": "Request submitted for processing.",
      "checkpoints": [],
      "action_request": null
    },
    {
      "stage": "locating_contact",
      "stage_label": "Retrieving Contact",
      "state": "completed",
      "occurred_at": "2025-02-01T14:02:00Z",
      "description": "We are searching for the best way to send out requests for this provider.",
      "checkpoints": [
        { "text": "Call completed at 2:14 PM to the facility.", "occurred_at": "2025-02-01T19:14:00Z" }
      ],
      "action_request": null
    },
    {
      "stage": "contact_found",
      "stage_label": "Contact Retrieved",
      "state": "skipped",
      "occurred_at": null,
      "description": null,
      "checkpoints": [],
      "action_request": null
    },
    {
      "stage": "request_sent",
      "stage_label": "Correspondence Sent",
      "state": "completed",
      "occurred_at": "2025-02-02T10:30:00Z",
      "description": "We've sent out your request via fax and will be following up with the provider to check in on progress.",
      "checkpoints": [],
      "action_request": null
    },
    {
      "stage": "confirming_receipt",
      "stage_label": "Confirming Receipt",
      "state": "skipped",
      "occurred_at": null,
      "description": null,
      "checkpoints": [],
      "action_request": null
    },
    {
      "stage": "acknowledged",
      "stage_label": "Request Acknowledged",
      "state": "current",
      "occurred_at": "2025-02-04T09:14:21Z",
      "description": "Your request is in progress with the provider. We follow up with them regularly and will post updates here.",
      "checkpoints": [],
      "action_request": null
    },
    {
      "stage": "completed",
      "stage_label": "Completed",
      "state": "pending",
      "occurred_at": null,
      "description": null,
      "checkpoints": [],
      "action_request": null
    }
  ]
}
```

Each `timeline` entry has a `state`: `completed` (happened), `current` (where the request is now), `pending` (standard step not reached yet), or `skipped` (standard step that was not needed). `checkpoints` list individual attempts within a step, such as phone calls. `action_request` is set on `action_required` steps and includes what was asked, whether your firm has responded (`awaiting_response`, `answered`, or `resolved`), and the response text.

<Note>
  `timeline` is returned by `GET /v2/retrieval-requests/{id}` and list queries with `include=details`. Summary list results include `stage`, `stage_label`, and `stage_description` but omit the timeline.
</Note>

**Filtering the list by stage:**

```bash theme={null}
curl "https://enterprise.tavrn.ai/v2/retrieval-requests?stage=request_sent,acknowledged" \
  -H "Authorization: Bearer tavrn_sk_live_..."
```

`stage` accepts one or more comma-separated values and can be combined with `status` and `case_id`.

### Incremental polling

Use `public_updated_since` with the list endpoint to reconcile status, action request, and document updates alongside webhooks:

```bash theme={null}
curl 'https://enterprise.tavrn.ai/v2/retrieval-requests?public_updated_since=1970-01-01T00%3A00%3A00.000Z&include=details&limit=100' \
  -H 'Authorization: Bearer tavrn_sk_live_...'
```

1. Start with your saved checkpoint, or the epoch timestamp above for an initial sync.
2. Upsert each result by request ID. `include=details` adds the timeline, open action request, and released document metadata.
3. Follow each `next` URL as returned until it is `null`.
4. After processing the final page, save its `next_public_updated_since` for the next round.

The lower bound is inclusive, so a request may appear in successive rounds. Compare `public_updated_at` when updating a cached request. The final page supplies `next_public_updated_since` even when no requests were returned.

For an organization-wide sync, leave status, stage, and case filters unset so completed and cancelled requests stay in scope. You can divide a large initial sync into fixed creation-date windows, with a checkpoint for each window.

Choose a polling interval that fits your integration. Changes during a round may appear in that round or the next. If a cursor expires or polling returns `503`, resume from the last saved checkpoint. Fetch an individual request when you need current costs.

### 5. Download Records and Invoices

List documents when you receive `retrieval_request.documents_available` or need current files. Provider invoices can become visible before or after request completion:

```bash theme={null}
curl https://enterprise.tavrn.ai/v2/retrieval-requests/rr_abc123/documents \
  -H "Authorization: Bearer tavrn_sk_live_..."
```

**Response:**

```json theme={null}
{
  "results": [
    {
      "id": "rrdoc_001",
      "retrieval_request_id": "rr_abc123",
      "category": "records",
      "record_types": ["medical_records"],
      "file_name": "example_general_hospital_records.pdf",
      "file_size": 2456789,
      "file_type": "application/pdf",
      "page_count": 134,
      "created_at": "2025-02-05T15:22:10Z"
    },
    {
      "id": "65c12ab34de56f7890123456",
      "retrieval_request_id": "rr_abc123",
      "category": "invoice",
      "record_types": [],
      "metadata": {
        "provider_fee_amount": 85
      },
      "file_name": "example_general_hospital_invoice.pdf",
      "file_size": 45123,
      "file_type": "application/pdf",
      "page_count": 1,
      "created_at": "2025-02-05T15:22:11Z"
    }
  ],
  "next": null,
  "previous": null
}
```

This endpoint returns all current documents in one response. `next` and `previous` are always `null`. Use `category` to distinguish `records`, provider `invoice` documents, and `cost_summary` PDFs.

Each invoice entry includes `metadata.provider_fee_amount`: the invoice amount in **USD**, not cents, that contributes to [`costs.provider_fees`](/guides/v2/schemas#retrievalrequestcosts). `0` is a valid amount. Record and cost summary entries omit `metadata`. The embedded `documents` array on `GET /v2/retrieval-requests/{id}` and the document list use the same entries. A `documents_available` event's `document_ids` and `invoice_ids` match their `id` values.

An invoice can appear in the list and costs before it is downloadable. In that
case, its download route returns `404`. The invoice `documents_available` webhook
fires when the file becomes downloadable.

Get a download URL for each document:

```bash theme={null}
curl https://enterprise.tavrn.ai/v2/retrieval-requests/rr_abc123/documents/rrdoc_001/download \
  -H "Authorization: Bearer tavrn_sk_live_..."
```

**Response:**

```json theme={null}
{
  "download_url": "https://download-url.example.com/...",
  "expires_in": 3600
}
```

Download the file from `download_url` before it expires.

#### Request cost PDF

Once a request is `completed` and its billing is finalized, its documents include
an entry with `id` and `category` both set to `cost_summary`. The PDF shows Tavrn
credits and fees, provider fees, and the total. It always includes a Tavrn credits
line, showing `0 credits × $0.00 = $0.00` when the request has no credit entries.

```bash theme={null}
curl https://enterprise.tavrn.ai/v2/retrieval-requests/rr_abc123/documents/cost_summary/download \
  -H "Authorization: Bearer tavrn_sk_live_..."
```

The response contains `download_url` and `expires_in: 3600`. Fetch the URL to
download the PDF. The endpoint returns `404` while the document is unavailable.

The entry appears in the request GET response and list results with
`include=details`, but is excluded from `documents_summary` counts. Refetch the
request or document list to check availability: billing finalization does not
trigger a `documents_available` webhook or advance `public_updated_at`.

#### Invoice Availability Notifications

Subscribe to `retrieval_request.documents_available` through
[Webhooks](/guides/v2/webhooks) to learn when a document becomes downloadable.

When a provider invoice becomes downloadable, the request's `public_updated_at`
advances and the event identifies the available invoice:

```json theme={null}
{
  "id": "rr-1027bfb56ac889adef3b3b8266d758322502c10b6b64499224218c9e3e2919f0",
  "type": "retrieval_request.documents_available",
  "created_at": "2026-10-08T12:03:00.000Z",
  "data": {
    "retrieval_request_id": "507f1f77bcf86cd799439011",
    "case_id": "507f1f77bcf86cd799439012",
    "public_updated_at": "2026-10-08T12:03:00.000Z",
    "document_ids": ["507f1f77bcf86cd799439015"],
    "invoice_ids": ["507f1f77bcf86cd799439015"]
  }
}
```

For an invoice, its ID appears in both `document_ids` and `invoice_ids`. Use it as `{document_id}` on the download route, and fetch the request for current document and cost details.

<Note>
  Retrieval documents are separate from case documents you upload for other workflows. Their metadata is available from `/v2/retrieval-requests/{id}/documents`, the request GET response, and list queries with `include=details`. Use the retrieval document download endpoint to access the files.
</Note>

## Optional Fields

The minimal request works with patient name, a facility ID or facility name and address, at least one record type, and an authorization form. Add these fields when you have them:

| Field | Notes |
| - | - |
| `patient.address`, `patient.phone`, `patient.email`, `patient.gender`, `patient.ssn` | Helps the facility verify the patient |
| `facility.phone`, `facility.fax` | Helps Tavrn contact the facility |
| `service_start_date`, `service_end_date` | Default date range for requested records |
| `notes` | Instructions for the team handling this request |
| `submitter_email` | Email of a user in your organization to attribute the request to — they receive retrieval notifications for it. Must match an existing user in your organization (otherwise the request fails with a `400 validation_error`). If omitted, the request is attributed to the API key's creator |
| `attachments` | Additional uploaded files, such as cover letters or patient ID copies |
| `allow_duplicate` | After a `409 duplicate_request_detected` response, set to `true` to submit despite the returned matches |

## Requested Records

Use `requested_records` to describe what you want from the facility.

**Medical records only:**

```json theme={null}
{
  "requested_records": [
    { "type": "medical_records" }
  ]
}
```

**Medical records plus itemized billing:**

```json theme={null}
{
  "requested_records": [
    { "type": "medical_records" },
    { "type": "billings", "deliverable_format": "itemized" }
  ]
}
```

**Certified medical records:**

```json theme={null}
{
  "requested_records": [
    { "type": "medical_records", "requirements": ["certification"] }
  ]
}
```

A request may use only one attestation kind — `certification` or `affidavit` — across all of its `requested_records`. Mixing them (on the same or different lines) returns a `400 validation_error`; submit a separate request for records that need the other kind.

**Ongoing care:**

Set `service_end_date` to `null` to request records through the present.

```json theme={null}
{
  "requested_records": [
    {
      "type": "medical_records",
      "service_start_date": "2024-01-01",
      "service_end_date": null
    }
  ]
}
```

<Note>
  Some values, including `rfc`, `abstract_medical_records`, and `requirements`, are available only when enabled for your organization. See [Permission-gated features](/guides/v2/schemas#permission-gated-features).
</Note>

See the [RequestedRecord schema](/guides/v2/schemas#requestedrecord) for the full list of record types, billing formats, and advanced patterns.

## Responding to Action Requests

When Tavrn needs something from your firm, the request's `status` becomes `action_required` and `GET /v2/retrieval-requests/{id}` returns the open action request in `pending_action_request`:

```json theme={null}
{
  "id": "rr_abc123",
  "status": "action_required",
  "action_required_reason": "payment_needed",
  "pending_action_request": {
    "id": "car_abc123",
    "description": "The provider's invoice of $85.00 exceeds your approval limit. Do you approve payment?",
    "document": {
      "file_name": "provider_invoice.pdf",
      "file_size": 45123,
      "file_type": "application/pdf",
      "download_url": "https://s3.amazonaws.com/...",
      "expires_in": 3600
    }
  }
}
```

`document` is a file Tavrn attached to the action request (for example, a provider invoice or no-records letter), or `null`. Its `download_url` expires after `expires_in` seconds; fetch the request for a fresh URL. When an action request opens or reopens, [`retrieval_request.action_required`](/guides/v2/webhooks#retrieval-request-events) includes its ID. Fetch the request to read the message and any attachment; incremental polling also captures the update.

### Text-only response

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/retrieval-requests/rr_abc123/action-response \
  -H "Authorization: Bearer tavrn_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Approved. Please pay the $85.00 invoice.",
    "requester_email": "paralegal@yourfirm.com"
  }'
```

**Response:**

```json theme={null}
{
  "id": "car_abc123",
  "retrieval_request_id": "rr_abc123",
  "status": "responded",
  "responded_at": "2025-02-03T16:05:00Z"
}
```

`message` is required (up to 10,000 characters). `requester_email` is optional and records who at your firm answered. The endpoint needs the `retrievals:write` scope.

### Response with an attachment

Upload the file under the retrieval request's case, then pass its `upload_id`:

```bash theme={null}
# 1. Get a presigned upload URL for the request's case
curl -X POST https://enterprise.tavrn.ai/v2/cases/case_abc123/sign-upload \
  -H "Authorization: Bearer tavrn_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "file_name": "signed_hipaa_authorization.pdf", "file_type": "application/pdf" }'

# 2. Upload the file to the returned upload_url
curl -X PUT "<upload_url>" \
  -H "Content-Type: application/pdf" \
  --data-binary @signed_hipaa_authorization.pdf

# 3. Answer the action request with the upload_id
curl -X POST https://enterprise.tavrn.ai/v2/retrieval-requests/rr_abc123/action-response \
  -H "Authorization: Bearer tavrn_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Signed authorization attached.",
    "attachment": { "upload_id": "11111111-1111-4111-8111-111111111111" }
  }'
```

The upload must belong to the same case as the retrieval request. PUT the file to the signed URL within 15 minutes, then use its `upload_id` within one hour. Each `upload_id` can be used once.

### How Tavrn handles your response

| Action request | What to send | What happens |
| - | - | - |
| Missing HIPAA authorization | A signed authorization **PDF** as `attachment` (required) | The PDF replaces the request's authorization form and Tavrn restarts the request. A text-only answer or a non-PDF file is rejected with `400` |
| Invalid HIPAA authorization | A corrected, signed **PDF** as `attachment`, or a text update | A PDF replaces the authorization form and Tavrn restarts the request. A non-PDF file is rejected with `400` |
| Payment approval (`action_required_reason: payment_needed`) | A clear approval or decline in `message`, e.g. `Approved to pay $85.00` | Tavrn pays only when your answer clearly approves this invoice at the quoted amount. A decline, a lower cap, or a question is reviewed by the Tavrn team |
| No records found | Confirm the provider's statement, or explain what to correct | A confirmation completes the request with the provider's no-records statement. A correction, dispute, or attachment is reviewed by the Tavrn team |
| Missing portal information (e.g. SSN last 4, sex, address) | The requested values in `message` | Tavrn fills the provider portal and resubmits. An answer it cannot read is rejected with `400` |
| Anything else | Your answer in `message`, with an `attachment` if asked for a document | The Tavrn team reviews your answer and continues the request |

A rejected response (`400`) leaves the action request open so you can retry. After a successful response the request leaves `action_required` (usually back to `processing`), and the timeline's `action_request` shows `status: "answered"` with your `response_text`. Responding again after that returns `409`, as does responding when there is no open action request.

## Support messages

Send a question or instruction with `POST /v2/retrieval-requests/{retrieval_request_id}/messages`. Requires `retrievals:write` and, for CMS-linked requests, support messaging enabled on the connection.

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/retrieval-requests/rr_abc123/messages \
  -H "Authorization: Bearer tavrn_sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "message": "Please confirm whether the provider has accepted this request.",
    "requester_name": "Jane Doe",
    "requester_email": "jane@example.com",
    "contact_preference": "email"
  }'
```

| `contact_preference` | Behavior |
| - | - |
| `email` (default) | Email receipts and replies. Supply `requester_email` when explicitly selecting this option. |
| `in_app_only` | Log published replies without email. Automated replies are currently disabled. |
| `do_not_contact` | Process requested work without receipts or replies. Only Litify-originated submissions keep the initial email receipt. |

Messages and published replies are logged without changing retrieval status. Drafts and private notes are excluded.

`message` is required (maximum 10,000 characters). Upload attachments under the request's case, then include `attachment: { "upload_id": "..." }`.

Identical submissions within 10 minutes return the existing conversation with `deduplicated: true`. Concurrent submissions return `409` (`support_message_in_progress`); retry shortly.

Success confirms submission, not completion of the requested work.

## Cancelling a Request

You can cancel a request before it completes:

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/retrieval-requests/rr_abc123/cancel \
  -H "Authorization: Bearer tavrn_sk_live_..."
```

Cancellation is final. Completed requests cannot be cancelled.

## Next Steps

* [Set up webhooks](/guides/v2/webhooks#retrieval-request-events)
* [View all retrieval request endpoints](/api-reference/v2/endpoint/retrieval-requests/create-retrieval-request)
* [Data Models & Schemas](/guides/v2/schemas)
* [Errors](/guides/v2/errors)
