---
title: "Put a PDF on an upload link"
method: POST
path: "/v1/imports/uploads/{token}"
tags: ["Imports"]
---

# Put a PDF on an upload link

`POST /v1/imports/uploads/{token}`

## Request

- Base URL: `https://api.galleyrender.com`
- URL: `https://api.galleyrender.com/v1/imports/uploads/{token}`
- Auth: none required

No API key: the token in the path is the credential. It comes from `upload.url` or `upload.curl` on an import made with `upload: true`, works once, and dies after 15 minutes. A file that is refused (not a PDF, over 20 MB) does not spend it.

## Path parameters

- `token` string, required

## Response `202`

Accepted; the import proceeds.

- Import: One document being turned into a template. It never carries a filename or a word of the document, except a question's `evidence.text` (at most 200 characters of what you uploaded) and the draft, which is read with `GET /v1/imports/{id}/draft`.
  - `object` 'import', required
  - `id` string, required
  - `status` 'awaiting_upload' | 'uploaded' | 'reading' | 'analysing' | 'awaiting_answers' | 'answered' | 'synthesising' | 'checking' | 'ready' | 'saved' | 'failed' | 'refused', required: `awaiting_upload` waits for a file at the upload link. `awaiting_answers` waits for you. `ready` is the moment to look at the preview; nothing is a template until `saved`. `failed` and `refused` are final and are not counted against the plan.
  - `kind` 'pdf' | 'null', nullable: The file type. PDF only for now.
  - `pages` integer, nullable
  - `bytes` integer, nullable
  - `document_kind` string, nullable: What the analysis took the document to be: `invoice`, `letter`, `other`, and so on.
  - `closest_starter` string, nullable: The starter template whose data contract the draft borrows, when one fits. See the docs for what that promises.
  - `language` string, nullable: The document's language, a BCP 47 tag.
  - `questions` Question[], required: At most three, and only while `status` is `awaiting_answers` or after: empty when the document needed none.
    - `id` string, required
    - `kind` 'field_or_fixed' | 'table_rows' | 'which_is' | 'document_kind' | 'keep_image', required
    - `prompt` string, required
    - `options` object[], required
      - `value` string, required
      - `label` string, required
      - `recommended` boolean: The answer taken when nobody answers.
    - `evidence` object, nullable
      - `text` string, nullable: At most 200 characters of the region, as printed in your document.
      - `page` integer, nullable
      - `box` number[], nullable: `[x0, y0, x1, y1]` in points from the page's top left.
      - `crop` string, uri, nullable: An image of the region, signed for 15 minutes.
  - `answers` Answer[], required
    - `question_id` string, required
    - `value` string, required: One of the question's option values.
    - `path` string: The field the paragraph becomes, for `field` and `field_with_default`.
    - `answered_by` 'agent' | 'user' | 'auto', required: `auto` is the recommendation, taken because nobody answered or `options.auto` was set.
  - `delayed_until` string, date-time, nullable: When an import waiting to start will be picked up, if the service's daily capacity is used. `null` when nothing holds it.
  - `check` Check: What Galley checked before calling the import `ready`. Present from `ready` on. The numbers are measured against the uploaded document and are there to be read, not trusted blindly: `review_recommended` says whether to compare the preview with the original before saving.
    - `checks` string[]: The checks that ran, in order.
    - `review_recommended` boolean: `true` when a check still missed after the one automatic revision. Look at the preview before you save.
    - `review_reasons` string[]
    - `verdict` 'accept' | 'revise' | 'reject' | 'null', nullable: The visual judge's verdict on the final draft.
    - `differences` object[]
      - `page` integer
      - `where` string
      - `what` string
      - `severity` 'minor' | 'major' | 'critical'
    - `layout_score` number[]: Per page, 0 to 1: how closely the preview's layout correlates with the original's.
    - `text_recall` number, nullable: The share of the document's words that the preview prints, 0 to 1.
    - `page_count` object
    - `overflow` object, nullable
    - `recall` object
    - `coverage` object, nullable
    - `layout` object, nullable
    - `judge` object, nullable
    - `repairs` integer: Automatic revisions made: 0 or 1.
    - `repaired_for` string[]
  - `notes` string[], required: Things worth knowing, in plain sentences: a font that was substituted, a question that took its recommendation.
  - `fonts_substituted` object[], required
    - `from` string, required: The face the document used.
    - `to` string, required: The bundled face the template uses instead.
  - `draft_summary` object, nullable: Field paths and counts, never values. `null` until `ready`.
    - `name` string, nullable
    - `fields` string[]
    - `string_keys` integer
    - `expected_pages` integer, nullable
  - `preview_url` string, uri, nullable: A PDF of the draft rendered from its example, signed for 15 minutes. `null` until `ready`.
  - `preview_png_url` string, uri, nullable: Page 1 of the preview as a PNG at 1x, signed for 15 minutes, for a client that shows an image rather than a PDF. `null` until `ready`, and when none was stored.
  - `preview_expires_at` string, date-time, nullable
  - `template` object, nullable: The template this import was saved as. `null` until `saved`.
    - `id` string
    - `name` string
    - `ref` string
  - `usage` object, nullable: Model tokens used, summed over the stages.
    - `input_tokens` integer
    - `output_tokens` integer
    - `cache_read_input_tokens` integer
    - `stages` integer
  - `error` object, nullable: Why a `failed` or `refused` import ended: an error envelope with `type` `import_failed`, `import_unsupported` or `import_refused`, and `details.reason` or `details.cause` naming which. Never any text of the document.
    - `type` 'import_failed' | 'import_unsupported' | 'import_refused'
    - `message` string
    - `docs_url` string, uri
    - `details` object
  - `existing` true: Present, and true, only when this is an earlier import returned for the same bytes.
  - `upload` object: Present only on the response that made the link; the token is shown once.
    - `url` string, uri, required: A page where a person can drop the file. The token rides in the fragment.
    - `curl` string, required: A `curl` command that uploads `document.pdf` to the link.
    - `expires_at` string, date-time, required
  - `created_at` string, date-time, required
  - `completed_at` string, date-time, nullable
  - `expires_at` string, date-time, nullable: When the source and its page images are deleted: 7 days after the import finished.

## Other responses

- `400`: `invalid_request` — no `file` part.
- `410`: `link_expired` — unknown, expired or already used.
- `413`: `import_unsupported` with `details.reason: "too_large"`.
- `422`: `import_unsupported` — not a PDF.
- `503`: `import_disabled`.

---

[API](https://skmtc.dev/galleyrender/apis/galley-render-api.md) · [All operations](https://skmtc.dev/galleyrender/apis/galley-render-api/llms.txt) · [OpenAPI document](https://skmtc.dev/galleyrender/apis/galley-render-api/revisions/a85d68468cd2?raw)
