---
title: "Import templates"
method: POST
path: "/api/v1/templates/import"
tags: ["Template Management"]
---

# Import templates

`POST /api/v1/templates/import`

## Request

- Base URL: `https://api.imagestep.dev`
- URL: `https://api.imagestep.dev/api/v1/templates/import`
- Auth: API key in header `Authorization`

Takes a list of template documents — what GET /api/v1/templates/{id} returns, one per template —
and creates each as a new template at version 1: history and slug are not carried over (the slug
is made from the name, with a numeric suffix when it is taken). The list rows of
GET /api/v1/templates carry no html, so they are not an export.

Each entry is checked like a create; one that fails is reported in `errors` and the rest still
land, so a `201` can carry refusals — read `errors`.
The whole import is refused (`422 resource_limit_exceeded`) if it would take the account past 200 templates.

## Headers

- `Idempotency-Key` string

## Request body

- TemplateData[]
  - `id` string: Template id (`tpl_…`; a built-in's is `builtin-template-…`). A version read by `id@version` carries that spelling here.
  - `name` string: Display name (required on create; at most 200 characters)
  - `slug` string: URL-safe handle, unique per account: lowercase letters, digits and hyphens. Generated from the name when omitted; one you send must be free. `builtin-` is reserved.
  - `description` string: Free text for people; not rendered. `""` on a PUT empties it.
  - `html` string: Body HTML (required on create; ≤ 256 KB as UTF-8). Scripts never run; every request it makes is blocked at render time except `data:` URIs and this product's own asset host.
  - `css` string: Stylesheet (≤ 128 KB as UTF-8). Like the html, it loads nothing but `data:` URIs and this product's own asset host; any other `@import` or `url()` is blocked at render time.
  - `width` integer: Viewport / output width in px (required on create; 1–4096)
  - `height` integer: Viewport / output height in px (required on create; 1–4096)
  - `variables` string[]: Placeholder names (at most 100, each letters, digits, `_` and `.`). Derived from the html/css when omitted. A render fills `{{ name }}` HTML-escaped and `{{{ name }}}` raw; a name the row does not carry renders as nothing.
  - `version` integer: Current version; every PUT increments it and keeps the old one readable as `id@version`
  - `builtIn` boolean: Whether this is a built-in template (read-only; PUT and DELETE are 403)
  - `createdAt` string, date-time: When the template — or, read as `id@version`, that version — was created
  - `updatedAt` string, date-time: When the current version was saved; absent on a version read as `id@version`

## Response `201`

What landed, and one line per entry that did not

## Other responses

- `400`: `invalid_param` — the body is not a non-empty list
- `409`: Conflict — `error.code` is `idempotency_key_reuse` (the key already means a different request) or `request_in_progress` (the first request under this key has not settled yet; retryable).
- `422`: `resource_limit_exceeded` — the entries would take the account past 200 templates; nothing was imported
- `429`: Rate limited — `error.code` is `rate_limited` and `retryable` is true. `Retry-After` says how many seconds to wait.

---

[API](https://skmtc.dev/imagestep/apis/imagestep-api.md) · [All operations](https://skmtc.dev/imagestep/apis/imagestep-api/llms.txt) · [OpenAPI document](https://skmtc.dev/imagestep/apis/imagestep-api/revisions/5e8e922a3085?raw)
