---
title: "Start turning an existing document into a template"
method: POST
path: "/v1/imports"
tags: ["Imports"]
---

# Start turning an existing document into a template

`POST /v1/imports`

## Request

- Base URL: `https://api.galleyrender.com`
- URL: `https://api.galleyrender.com/v1/imports`
- Auth: HTTP bearer

Paid plans only. Takes a PDF three ways: a multipart `file` part, a JSON `file_url` the API fetches (public https only), or JSON `{ "upload": true }`, which answers with a one-time upload link and an import in `awaiting_upload`. Answers in under a second with the import in `uploaded`; the work happens after, so poll `GET /v1/imports/{id}`. The same bytes from the same account inside 24 hours are the same import and answer `200` with `existing: true` unless `force` is true. A feature that is switched off on the deployment answers `503 import_disabled`.

## Request body

- ImportRequest: The JSON body of `POST /v1/imports`: exactly one of `file_url` or `upload: true`. A multipart `file` is the third way in.
  - `file_url` string: A public https URL to a PDF. Fetched by the API under the same egress policy as template assets, up to 20 MB and 10 seconds.
  - `upload` true: Ask for a one-time upload link instead of sending the file.
  - `options` object
    - `auto` boolean: Take the recommended answer to every question instead of asking.
    - `name` string: The name the template is saved under, unless `save` is given another.
    - `hint` string: What you expect the document to be, such as `invoice`. A prior for the classifier, never a command.
  - `force` boolean: Import the same bytes again inside 24 hours instead of returning the earlier import.

## Response `200`

The same bytes were imported by this account in the last 24 hours; this is that import, with `existing: true`.

- 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

- `201`: The import. With `upload: true`, `status` is `awaiting_upload` and `upload` carries the link.
- `400`: `invalid_request` — none or more than one of `file`, `file_url`, `upload`, or a bad `options` field. `asset_blocked` — a `file_url` the egress policy refuses.
- `401`: `authentication_error`.
- `403`: `plan_required` with `details.feature: "imports"` — the plan carries no imports (Free) or the period's count is used; `details.import_limit`, `imports_used`, `required_plan`, `required_plan_price_usd` and `feature_docs_url` say which. `permission_error` — the account is not in a closed beta of the feature.
- `413`: `import_unsupported` with `details.reason: "too_large"` — over 20 MB.
- `422`: `import_unsupported` — `details.reason` is `unsupported_type`, `docx_not_yet` or `pptx_not_yet`.
- `429`: `rate_limited` — requests faster than the account's per-minute rate limit (wait `Retry-After` seconds), or an import limit named by `details.scope`: `imports_hourly` (the hour's starts are spent; `details.retry_after_seconds`) or `imports_in_flight` (the account already has as many imports being worked as its plan allows).
- `503`: `import_disabled` — document import is not switched on for this deployment.

---

[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)
