Templates
Template CRUD and document generation
The 3-shape create+generate response
POST /v1/templates returns different shapes depending on whether the request included a generate field:
generateabsent — a plainTemplateDto(unchanged, backward compatible).generatepresent and generation succeeds —{ template, generation }.generatepresent but generation fails at runtime —{ template, generationError }— the template is still persisted; only the immediate generation attempt failed.
The most-hit error codes on template creation/generation are NO_PLACEHOLDERS (a template body with only literal text, no function calls), UNKNOWN_FUNCTION (a body references a function outside the v1 catalog), and GENERATION_FAILED (a runtime evaluation error, e.g. a missing required getParam).
| Method | Path | Summary |
|---|---|---|
| GET | /v1/templates | List templates |
| POST | /v1/templates | Create a template (optionally generate immediately — feature 1b) |
| POST | /v1/templates/generate | Ad-hoc/debug generate — generate directly from a raw body, no persistence (feature 1a) |
| GET | /v1/templates/{templateId} | Get a template by id |
| PUT | /v1/templates/{templateId} | Update a template |
| DELETE | /v1/templates/{templateId} | Delete a template |
| POST | /v1/templates/{templateId}/generate | Generate a document from a template |
/v1/templatesList templates
| Param | In | Type | Required | Description |
|---|---|---|---|---|
| name | query | string | No | — |
| status | query | `active` | `archived` | No | — |
| tags | query | string | No | Comma-separated list of tags |
| cursor | query | string | No | — |
| limit | query | integer | No | — |
Responses
object
{
"items": [
{
"templateId": "tpl_9f3a1c2e",
"tenantId": "tenant-acme-01",
"name": "Order Confirmation",
"body": "Hello <getParam('name')>, order #<createSeq('orderNo')>",
"tags": [
"orders",
"email"
],
"validated": true,
"status": "active",
"createdAt": "2026-07-23T09:00:00.000Z",
"updatedAt": "2026-07-23T09:00:00.000Z"
}
],
"nextCursor": "eyJvZmZzZXQiOjIwfQ=="
}ErrorEnvelope
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"details": {}
}
}/v1/templatesCreate a template (optionally generate immediately — feature 1b)
When the request body includes an optional `generate` field, a document is generated from the newly-created template in the same call (see `CreateTemplateResponseDto` for the resulting 3-way response shape). Without `generate`, behavior is unchanged from create-only.
Request body — CreateTemplateRequestDto
Create only (unchanged, backward compatible)
{
"name": "Order Confirmation",
"description": "Sends a friendly order confirmation with a generated order number",
"body": "Hello <getParam('name')>, order #<createSeq('orderNo')>",
"tags": [
"orders",
"email"
]
}One-shot create + generate (feature 1b)
{
"name": "Invoice",
"body": "{ \"id\": \"<getRandomNumber(1,1000)>\" }",
"generate": {
"seed": 42
}
}Responses
CreateTemplateResponseDto
generate omitted
{
"templateId": "tpl_9f3a1c2e",
"tenantId": "tenant-acme-01",
"name": "Order Confirmation",
"description": "Sends a friendly order confirmation with a generated order number",
"body": "Hello <getParam('name')>, order #<createSeq('orderNo')>",
"tags": [
"orders",
"email"
],
"validated": true,
"status": "active",
"createdAt": "2026-07-23T09:00:00.000Z",
"updatedAt": "2026-07-23T09:00:00.000Z"
}generate present, succeeded
{
"template": {
"templateId": "tpl_1a2b3c4d",
"tenantId": "tenant-acme-01",
"name": "Invoice",
"body": "{ \"id\": \"<getRandomNumber(1,1000)>\" }",
"tags": [],
"validated": true,
"status": "active",
"createdAt": "2026-07-25T10:00:00.000Z",
"updatedAt": "2026-07-25T10:00:00.000Z"
},
"generation": {
"data": {
"id": "731"
},
"meta": {
"seed": 42,
"templateId": "tpl_1a2b3c4d",
"generatedAt": "2026-07-25T10:00:00.000Z"
}
}
}generate present, template persisted but generation failed at runtime
{
"template": {
"templateId": "tpl_5e6f7a8b",
"tenantId": "tenant-acme-01",
"name": "NeedsParam",
"body": "{ \"v\": \"<getParam('sku')>\" }",
"tags": [],
"validated": true,
"status": "active",
"createdAt": "2026-07-25T10:00:00.000Z",
"updatedAt": "2026-07-25T10:00:00.000Z"
},
"generationError": {
"code": "GENERATION_FAILED",
"message": "getParam: parameter 'sku' was not provided and has no default"
}
}ErrorEnvelope
Missing required field
{
"error": {
"code": "VALIDATION_ERROR",
"message": "\"name\" is required",
"details": {
"field": "name"
}
}
}Template has no placeholders
{
"error": {
"code": "INVALID_TEMPLATE",
"message": "Template body must contain at least one placeholder (e.g. <getName()>) — a template with only literal text cannot generate varying data",
"details": {
"reason": "Template body must contain at least one placeholder (e.g. <getName()>) — a template with only literal text cannot generate varying data",
"code": "NO_PLACEHOLDERS"
}
}
}Body references an unknown function
{
"error": {
"code": "UNKNOWN_FUNCTION",
"message": "Unknown function 'notAFunction'",
"details": {
"name": "notAFunction"
}
}
}ErrorEnvelope
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"details": {}
}
}/v1/templates/generateAd-hoc/debug generate — generate directly from a raw body, no persistence (feature 1a)
Generates a document from a raw template body without creating a template record. Useful for quickly iterating on template syntax. Goes through the same billing/usage metering as persisted-template generation (keyed on tenantId, not templateId) — not a free bypass of usage billing. `createSeq`/durable sequence side effects still apply; callers should set `sequenceNamespace`/`variableNamespace` (e.g. `"debug"`) to avoid colliding with real tenant sequences.
Request body — AdhocGenerateRequestDto
{
"body": "{ \"name\": \"<getRandomFullName()>\" }",
"seed": 42,
"sequenceNamespace": "debug",
"variableNamespace": "debug"
}Responses
AdhocGenerateResponseDto
{
"data": {
"name": "Jane Doe"
},
"meta": {
"seed": 42,
"generatedAt": "2026-07-25T10:00:00.000Z"
}
}ErrorEnvelope
Runtime evaluation error
{
"error": {
"code": "GENERATION_FAILED",
"message": "getParam: parameter 'who' was not provided and has no default",
"details": {
"reason": "getParam: parameter 'who' was not provided and has no default"
}
}
}Static validation — template has no placeholders
{
"error": {
"code": "INVALID_TEMPLATE",
"message": "Template body must contain at least one placeholder (e.g. <getName()>) — a template with only literal text cannot generate varying data",
"details": {
"reason": "Template body must contain at least one placeholder (e.g. <getName()>) — a template with only literal text cannot generate varying data",
"code": "NO_PLACEHOLDERS"
}
}
}Static validation — unknown function reference
{
"error": {
"code": "UNKNOWN_FUNCTION",
"message": "Unknown function 'notAFunction'",
"details": {
"name": "notAFunction"
}
}
}ErrorEnvelope
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"details": {}
}
}/v1/templates/{templateId}Get a template by id
| Param | In | Type | Required | Description |
|---|---|---|---|---|
| templateId | path | string | Yes | — |
Responses
TemplateDto
ErrorEnvelope
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"details": {}
}
}ErrorEnvelope
{
"error": {
"code": "NOT_FOUND",
"message": "Template tpl_missing not found",
"details": {}
}
}/v1/templates/{templateId}Update a template
| Param | In | Type | Required | Description |
|---|---|---|---|---|
| templateId | path | string | Yes | — |
Request body — object
{
"body": "Hello <getParam('name')>, your order #<createSeq('orderNo')> is confirmed!"
}Responses
TemplateDto
ErrorEnvelope
{
"error": {
"code": "VALIDATION_ERROR",
"message": "\"name\" is required",
"details": {
"field": "name"
}
}
}ErrorEnvelope
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"details": {}
}
}ErrorEnvelope
{
"error": {
"code": "NOT_FOUND",
"message": "Template tpl_missing not found",
"details": {}
}
}/v1/templates/{templateId}Delete a template
| Param | In | Type | Required | Description |
|---|---|---|---|---|
| templateId | path | string | Yes | — |
Responses
TemplateDto
ErrorEnvelope
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"details": {}
}
}ErrorEnvelope
{
"error": {
"code": "NOT_FOUND",
"message": "Template tpl_missing not found",
"details": {}
}
}/v1/templates/{templateId}/generateGenerate a document from a template
| Param | In | Type | Required | Description |
|---|---|---|---|---|
| templateId | path | string | Yes | — |
Request body — GenerateRequestDto
{
"seed": 123456789,
"params": {
"name": "Jane Doe"
},
"sequenceNamespace": "default",
"variableNamespace": "default"
}Responses
GenerateResponseDto
{
"data": "Hello Jane Doe, order #1001",
"meta": {
"seed": 123456789,
"templateId": "tpl_9f3a1c2e",
"generatedAt": "2026-07-23T09:05:00.000Z"
}
}ErrorEnvelope
{
"error": {
"code": "VALIDATION_ERROR",
"message": "\"name\" is required",
"details": {
"field": "name"
}
}
}ErrorEnvelope
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key",
"details": {}
}
}ErrorEnvelope
{
"error": {
"code": "NOT_FOUND",
"message": "Template tpl_missing not found",
"details": {}
}
}ErrorEnvelope
{
"error": {
"code": "OUTPUT_LIMIT_EXCEEDED",
"message": "Template produced more than the maximum of 8388608 bytes of output",
"details": {
"limit": 8388608,
"kind": "outputBytes",
"producedBytes": 8388621
}
}
}