Templates #
All /templates endpoints: list, create, get, update, delete, patch schema, clone, categories.
The /templates/* endpoints let you create, version, clone, and retire
reusable extraction schemas. For the conceptual model see
Templates; for a worked walkthrough see
Working with templates.
GET /templates/
GET /templates/List templates owned by the authenticated account.
| Param | Type | Default | Notes |
|---|---|---|---|
category | enum | – | invoice, receipt, id_card, contract, custom. |
search | string | – | Case-insensitive prefix on name / description. |
limit | integer | 50 | 1–100. |
offset | integer | 0 | Standard offset pagination. |
curl -u "$DD_KEY:$DD_SECRET" \ 'https://api.datadistillers.com/api/v1/templates/?category=invoice&limit=25'
GET /templates/categories
GET /templates/categoriesReturns the canonical list of category strings:
curl -u "$DD_KEY:$DD_SECRET" \ https://api.datadistillers.com/api/v1/templates/categories
["invoice", "receipt", "id_card", "contract", "custom"]
Use this to populate UI selectors instead of hard-coding the list.
POST /templates/
POST /templates/Create a new template.
{
"name": "Acme invoice v1",
"description": "Standard US-format vendor invoices.",
"category": "invoice",
"tags": ["acme", "us-invoice"],
"fields": [
{ "key": "invoice_number", "label": "Invoice #", "data_type": "string", "required": true },
{ "key": "total", "label": "Total", "data_type": "currency",
"required": true,
"validation": { "min_value": 0 } }
],
"settings": { "confidence_threshold": 0.8, "ocr_engine": "default" },
"is_public": false
}
| Field | Constraints |
|---|---|
name | Min length 3. |
category | Defaults to custom. |
tags | String array. |
fields | Array of TemplateFieldDef. |
settings | auto_save, confidence_threshold (0–1), ocr_engine. |
is_public | Defaults to false. |
Returns 201 Created + TemplateResponse.
TemplateFieldDef
| Field | Required | Notes |
|---|---|---|
key | yes | ^[a-z0-9_]+$. Becomes the JSON key in the result. |
label | yes | Human-readable. |
data_type | yes | string, number, date, currency, table, boolean. |
required | no | Default false. |
description | no | Free-form. |
validation | no | { regex?, min_value?, max_value?, required? }. |
GET /templates/{id}
GET /templates/{id}Returns the full template:
{
"id": "tpl_x8y9z",
"user_id": "usr_…",
"name": "Acme invoice v1",
"slug": "acme-invoice-v1",
"description": "Standard US-format vendor invoices.",
"category": "invoice",
"tags": ["acme", "us-invoice"],
"schema_definition": { "fields": [...] },
"status": "active",
"version": 3,
"is_public": false,
"usage_count": 2841,
"settings": { "confidence_threshold": 0.8, "ocr_engine": "default" },
"created_at": "2026-04-01T00:00:00Z",
"updated_at": "2026-05-01T00:00:00Z"
}
PUT /templates/{id}
PUT /templates/{id}Replace template metadata: name, description, category, tags, status,
settings, public flag. Does not modify the schema; use
PATCH /templates/{id}/schema for that.
curl -u "$DD_KEY:$DD_SECRET" \
-X PUT https://api.datadistillers.com/api/v1/templates/tpl_x8y9z \
-H 'Content-Type: application/json' \
-d '{ "status": "archived" }'
All fields are optional. Anything you omit is left unchanged.
DELETE /templates/{id}
DELETE /templates/{id}Soft-deletes by default. Pass ?hard_delete=true to permanently remove.
curl -u "$DD_KEY:$DD_SECRET" \ -X DELETE 'https://api.datadistillers.com/api/v1/templates/tpl_x8y9z' curl -u "$DD_KEY:$DD_SECRET" \ -X DELETE 'https://api.datadistillers.com/api/v1/templates/tpl_x8y9z?hard_delete=true'
Soft-deleted templates can't be referenced by new jobs but existing jobs are unaffected. Hard-delete is irreversible and breaks historical references.
Returns 204 No Content on success.
PATCH /templates/{id}/schema
PATCH /templates/{id}/schemaIncrementally modify the field schema.
{
"add_fields": [
{ "key": "po_number", "label": "PO #", "data_type": "string" }
],
"update_fields":[
{ "key": "total", "label": "Grand total", "data_type": "currency",
"required": true, "validation": { "min_value": 0 } }
],
"remove_fields": ["legacy_notes"]
}
| Op | Behavior |
|---|---|
add_fields | Append. Returns 400 if a key already exists. |
update_fields | Overwrite by key. Returns 404 if any key is missing. |
remove_fields | Delete by key. Silently no-ops if missing. |
Each successful patch increments version. In-flight jobs continue against
the schema version they started under.
POST /templates/{id}/clone
POST /templates/{id}/cloneClone an existing template into a new draft.
curl -u "$DD_KEY:$DD_SECRET" \
-X POST https://api.datadistillers.com/api/v1/templates/tpl_x8y9z/clone \
-H 'Content-Type: application/json' \
-d '{ "new_name": "Acme invoice v2 (draft)", "include_settings": true }'
Body fields (all optional):
| Field | Default | Notes |
|---|---|---|
new_name | "{name} (clone)" | Name for the clone. |
include_settings | true | If false, settings reset to defaults. |
Returns 201 Created + TemplateResponse for the new draft (status
draft, version 1, fresh id and slug).
Errors
| Status | Cause |
|---|---|
400 | Validation failed (duplicate field key on add, invalid data_type, etc.). |
401 | Auth missing or invalid. |
403 | Template belongs to a different account. |
404 | template_id or a key referenced by update_fields doesn't exist. |
409 | Trying to delete a template currently in use by an in-flight job (hard-delete only). |
422 | Body shape didn't validate. |