REST API

Templates API

API v115 August 2026·5 min read

Full reference for Doclift's Templates API: list, view, create, update, delete, publish and unpublish.

The template object

  • id: unique identifier
  • title, description: required strings
  • category: custom, fillable_form, or workflow. Only custom templates can be created or edited through this API. See Writes only reach custom templates
  • orientation: portrait or landscape
  • published: whether the template can be used to generate documents
  • content: the HTML body (custom templates only)
  • margin_top, margin_bottom, margin_left, margin_right: integers, in millimeters, minimum 5
  • variables: see the Variables API
  • created_at, updated_at: ISO-8601

Writes only reach custom templates

Every mutating action on this resource (create, update, delete, publish, unpublish, and every action under /variables) resolves the template through a scope restricted to category: "custom". create also hard-codes category to "custom" server-side, whatever value the body carries: the classic API cannot bring a fillable_form or a workflow template into existence.

A fillable_form or workflow id answers 404 on write

Calling update, delete, publish, unpublish, or any /variables endpoint with the id of a fillable_form or workflow template returns the same 404 {"error": "Template not found"} as an id that does not exist at all. There is no way to tell the two apart from the response. See Fillable forms for that family, and note that workflow templates have their own dedicated authoring surface.

Reads are not restricted the same way: GET /api/v1/templates and GET /api/v1/templates/:id list and show templates of all three categories once published.


List templates

GET/api/v1/templates
cURL
curl https://app.doclift.io/api/v1/templates \
  --header "X-Api-Key: <your-api-key>"
200 OK · application/json
[
  {
    "id": 100011,
    "title": "Invoice",
    "description": "Monthly invoice template"
  },
  {
    "id": 100012,
    "title": "Subscription form",
    "description": "Uploaded PDF form"
  }
]

Returns your organization's published, non-archived templates (every member's, not only the calling key's own) of all three categories, in a bare array, id/title/description only. content is never present in this shape.

ParameterWhereNotes
pagequeryoptional, default 1

Paginated at 30 per page. See Pagination. A page past the last one answers 200 with an empty array.

StatusBodyWhen
200 arrayalways, including an empty account or an out-of-range page
403 {"error": "..."}missing, unknown, or disabled API key

Show a template

GET/api/v1/templates/:id
cURL
curl https://app.doclift.io/api/v1/templates/100011 \
  --header "X-Api-Key: <your-api-key>"
200 OK · application/json
{
  "id": 100011,
  "title": "Invoice",
  "description": "Monthly invoice template",
  "content": "<h1>Invoice</h1><p><variable>client_name</variable></p>",
  "created_at": "2024-01-15T10:30:00+01:00",
  "updated_at": "2024-01-15T10:30:00+01:00",
  "variables": [
    {
      "title": "client_name",
      "description": "Name of the client",
      "field_type": "text",
      "allowed_values": []
    }
  ]
}

variables here carries only title, description, field_type, and allowed_values: no id, no seed_value. Works identically for fillable_form and workflow templates once published: the field shape is the same, content included (empty for a fillable_form, since its layout comes from the uploaded PDF).

StatusBodyWhen
200 templatepublished, not archived, in your organization
404 {"error": "Template not found"}unpublished, archived, or another organization's template
403 {"error": "..."}missing, unknown, or disabled API key

Create a template

POST/api/v1/templates

Always creates a custom template. Any category sent in the body is ignored.

cURL
curl https://app.doclift.io/api/v1/templates \
  --request POST \
  --header "X-Api-Key: <your-api-key>" \
  --header "Content-Type: application/json" \
  --data '{
    "template": {
      "title": "Invoice",
      "description": "A template for invoices",
      "orientation": "portrait",
      "content": "<h1>Invoice</h1><p><variable>client_name</variable></p>",
      "margin_top": 10,
      "margin_bottom": 10,
      "margin_left": 15,
      "margin_right": 15,
      "variables_attributes": [
        {
          "title": "client_name",
          "description": "Name of the client",
          "field_type": "text",
          "seed_value": "John Doe"
        }
      ]
    }
  }'
201 Created · application/json
{
  "id": 100013,
  "title": "Invoice",
  "description": "A template for invoices",
  "content": "<h1>Invoice</h1><p><variable>client_name</variable></p>",
  "category": "custom",
  "orientation": "portrait",
  "published": false,
  "margin_top": 10,
  "margin_bottom": 10,
  "margin_left": 15,
  "margin_right": 15,
  "created_at": "2024-01-15T10:30:00+01:00",
  "updated_at": "2024-01-15T10:30:00+01:00",
  "variables": [
    {
      "id": 200003,
      "title": "client_name",
      "description": "Name of the client",
      "seed_value": "John Doe",
      "field_type": "text",
      "allowed_values": []
    }
  ]
}
FieldTypeRequiredNotes
titlestringyes
descriptionstringyes
contentstringnoHTML body
orientationstringnoportrait or landscape, default portrait
margin_topintegerno>= 5, default 10
variables_attributesarraynosee Variables API

This is the one response that exposes seed_value together with id

The create/update/publish/unpublish responses render every variable with id, title, description, seed_value, field_type, and allowed_values. No other endpoint returns id and seed_value on the same variable object.

StatusBodyWhen
201 template, detail viewsuccess
422 {"errors": ["..."]}validation failure (e.g. blank title)
403 {"error": "..."}missing, unknown, or disabled API key

Update a template

PATCH/api/v1/templates/:id

category is not accepted here. It cannot be changed after creation, not even between two writes on the same custom template.

cURL
curl https://app.doclift.io/api/v1/templates/100013 \
  --request PATCH \
  --header "X-Api-Key: <your-api-key>" \
  --header "Content-Type: application/json" \
  --data '{
    "template": {
      "title": "Updated invoice",
      "variables_attributes": [
        { "id": 200003, "_destroy": true },
        { "title": "invoice_number", "field_type": "text" }
      ]
    }
  }'
200 OK · application/json
{
  "id": 100013,
  "title": "Updated invoice",
  "description": "A template for invoices",
  "content": "<h1>Invoice</h1><p><variable>client_name</variable></p>",
  "category": "custom",
  "orientation": "portrait",
  "published": false,
  "margin_top": 10,
  "margin_bottom": 10,
  "margin_left": 15,
  "margin_right": 15,
  "created_at": "2024-01-15T10:30:00+01:00",
  "updated_at": "2024-01-15T11:02:00+01:00",
  "variables": [
    {
      "id": 200004,
      "title": "invoice_number",
      "description": null,
      "seed_value": null,
      "field_type": "text",
      "allowed_values": []
    }
  ]
}

Same body fields as create (minus category). Inside variables_attributes: a hash with no id creates a variable, {"id": ..., "_destroy": true} deletes the matching one, any other hash with an id updates it.

StatusBodyWhen
200 template, detail viewsuccess
422 {"errors": ["..."]}validation failure
404 {"error": "Template not found"}not custom, archived, or another organization's
403 {"error": "..."}missing, unknown, or disabled API key

Delete a template

DELETE/api/v1/templates/:id
cURL
curl https://app.doclift.io/api/v1/templates/100013 \
  --request DELETE \
  --header "X-Api-Key: <your-api-key>"

204 No Content. This archives the template rather than erasing it: it stops appearing in the list and can no longer be generated from. There is no hard delete through the API.

StatusBodyWhen
204 nonesuccess
404 {"error": "Template not found"}not custom, already archived, or another organization's
403 {"error": "..."}missing, unknown, or disabled API key

Publish / unpublish a template

PUT/api/v1/templates/:id/publish
PUT/api/v1/templates/:id/unpublish
cURL
curl https://app.doclift.io/api/v1/templates/100013/publish \
  --request PUT \
  --header "X-Api-Key: <your-api-key>"

Both return 200 OK with the same detail view as create/update, reflecting the new published value. Both are idempotent: publishing an already-published template (or unpublishing an already-unpublished one) still answers 200, never an error.

StatusBodyWhen
200 template, detail viewsuccess, including a no-op call
404 {"error": "Template not found"}not custom, archived, or another organization's
403 {"error": "..."}missing, unknown, or disabled API key