Downloading Interaction Files

Request short-lived, single-use download URLs for an interaction's file attachments, and delete one attachment by id.

Clinical interactions in Hint can carry file attachments: lab report PDFs, uploaded documents, scanned forms, and files pushed by other partners. This guide shows how to turn those attachments into downloadable files.

The files array names the attachments

Every interaction response includes a files array. Each entry identifies one attachment:

{
  "id": "int-abc123",
  "type": "document",
  "files": [
    { "id": "Fah-3TWDJuQ1MSe-lBTiPh", "filename": "referral.pdf", "type": null },
    { "id": "Znd8CviNr5RERx6L-v5auf", "filename": "scan.tiff", "type": null }
  ]
}

This tells you what is attached, but it is not itself downloadable. The underlying storage keys are private, so you request a short-lived signed URL for a file through one of two endpoints. The file id in the files array is the same id you pass to the single-file endpoint below.

Each entry carries a type

Every entry in the files array carries a type, and its value is null. The key is always present. Read null as untyped.

One interaction type is the exception. A Lab Interaction records which document ordered the lab and which reported it, and its files carry that type. Lab Requisitions and Results covers both values.

Both download endpoints below resolve every file id, whatever its type. The delete endpoint takes a typed file only.

Send a file

files takes the documents you attach, and every interaction type accepts the same entry. Each entry is an object:

{
  "files": [
    { "url": "https://files.example.com/referral-000123.pdf", "filename": "referral.pdf" },
    { "url": "data:application/pdf;base64,JVBERi0xLjQKJVBFWEFNUExF", "filename": "scan.pdf" }
  ]
}

url is required, and it carries the document in one of two forms:

  • A data: URI carries the document inline.
  • An https URL points Hint at the document. Hint fetches it while it handles your call, so the URL has to resolve at that moment.

filename is the name the file is stored under. It is optional, and sending it is the reliable choice. When you leave it out Hint takes the name from the response's Content-Disposition, then from the URL's path, and derives a missing extension from the file's own bytes. A file Hint cannot name is rejected with a 422. For a data: URI the name comes from that last step every time, since the URI carries the bytes alone.

Hint accepts a URL string or an object carrying a url. It rejects anything else:

{
  "status": 422,
  "code": "unprocessable_entity",
  "message": "Files must be an array of URL strings or objects holding a url",
  "errors": { "files": ["must be an array of URL strings or objects holding a url"] }
}

A bare URL string also works, and Hint derives the whole name.

A Lab Interaction takes two further file fields that record what each document is. See Lab Requisitions and Results.

Clearing files with files: [] is legacy

files is the canonical file field on an interaction create or update, and it replaces the interaction's file list:

  • Leave files out and the interaction keeps its current files.
  • Send entries in files and they replace the interaction's whole untyped file list.
  • Send an empty files array and Hint removes every untyped file on the interaction, including the files a provider attached in the Hint UI and the files another partner pushed.
{
  "files": []
}

A file typed as a lab requisition or a lab result belongs to a separate list. It stays attached through files: [] and keeps appearing in the interaction's files array. Delete a single file is the endpoint that removes it.

❗️

files: [] clears every untyped file at once

files: [] removes every untyped file on the interaction, and it is the only removal for an untyped file. A typed file is removed through Delete a single file alone.

Send files: [] only when you mean to remove every untyped file. Send files when you mean to attach one, and call the delete endpoint when you mean to remove one.

Download every file on an interaction

List Interaction File Download URLs returns a signed URL for every file on the interaction, as a bare array:

GET /api/provider/interactions/{interaction_id}/files/download_urls
[
  {
    "id": "Fah-3TWDJuQ1MSe-lBTiPh",
    "filename": "referral.pdf",
    "type": null,
    "url": "https://practice-bucket.s3.amazonaws.com/public/pat-ab12C345DeF6/referral.pdf?X-Amz-Signature=EXAMPLE",
    "expires_at": "2026-07-01T12:05:00.000Z"
  },
  {
    "id": "Znd8CviNr5RERx6L-v5auf",
    "filename": "scan.tiff",
    "type": null,
    "url": null,
    "expires_at": null
  }
]

Each element carries the same type the interaction reports, so a sync can group the signed URLs without reading the interaction again.

This is the call to make when you are syncing an interaction and want every attachment in one round trip.

Download a single file

Get Interaction File Download URL returns one element of that same shape. Use it when you already hold a file id (from an earlier sync, a queued job, or a retry) and want a fresh URL for just that file without re-signing the rest:

GET /api/provider/interactions/{interaction_id}/files/{id}/download_url
{
  "id": "Fah-3TWDJuQ1MSe-lBTiPh",
  "filename": "referral.pdf",
  "type": null,
  "url": "https://practice-bucket.s3.amazonaws.com/public/pat-ab12C345DeF6/referral.pdf?X-Amz-Signature=EXAMPLE",
  "expires_at": "2026-07-01T12:05:00.000Z"
}

Delete a single file

DELETE removes one file from an interaction. id is the file id from the interaction's files array, the same id the single-file download endpoint takes:

DELETE /api/provider/interactions/{interaction_id}/files/{id}

A 204 No Content with no body confirms the removal, and the file leaves the interaction's files array.

The endpoint removes a typed file, a lab_requisition or a lab_result, and it is the only removal for one. It rejects anything else:

StatusWhenWhat to do
404 Not Foundid matches no file on the interaction.Read the interaction again and take the id from its files array.
422 Unprocessable Entityid matches a file whose type is null: a file sent through files, or one stored before typed files. The file exists, which is why this is not a 404, and it belongs to the untyped list.Send files: [] on an update. It removes every untyped file on the interaction, this one included, and it is the only removal for an untyped file.

Downloads are PDF-only

Only files that resolve to application/pdf receive a signed url. The two download endpoints handle a non-PDF differently:

  • On download_urls, a non-PDF stays in the array with url and expires_at set to null. It is not omitted, so you can tell "this attachment is not downloadable" apart from "this attachment does not exist".
  • On download_url, a non-PDF is a 422 Unprocessable Entity. When you ask for a specific file, a refusal is the honest answer.

An id that does not match any file on the interaction returns 404 Not Found on download_url.

URLs are short-lived: sign at download time

The signed url expires roughly 5 minutes after it is issued (expires_at carries the exact time), and it is meant to be used once. Request it at the moment the user acts, then redirect them to (or fetch) the returned url right away.

Do not cache the URL, store it in your database, or precompute it during a scheduled sync. A URL signed during an overnight backfill will have expired long before anyone clicks it. Store the file id instead, and request a fresh URL for it whenever you need one - the same id returns a new URL every time you call.

sequenceDiagram
  participant App as Your app
  participant Hint as Hint Provider API
  App->>Hint: GET /interactions/{id}
  Hint-->>App: files [ { id, filename } ]
  App->>Hint: GET /interactions/{id}/files/{file_id}/download_url
  Hint-->>App: { url, expires_at }
  App->>App: Redirect user to url within 5 minutes

The same pattern holds wherever you put a file in front of a user: keep the file's id from the interaction's files array, and request its download_url at the moment they act.

Access and scope

These endpoints are practice-scoped like the rest of the Provider API, authorized with the practice's access token. A partner can download files on any interaction it can already read; the endpoints do not restrict downloads to files the calling partner uploaded. Files a Hint provider attached in the UI are in scope alongside files uploaded by partners. The delete endpoint follows the same scope, whoever attached the file, and it needs a key that can manage interactions: a read-only key is refused.

Availability

All three endpoints are available from API version 2026-07-01 onward. Clients pinned to an earlier version receive a 404 on every one of the three routes. See Initial Data Sync for the broader pattern of reconciling interaction data when you first connect a practice.


Did this page help you?