Custom Templates API
Custom templates let you save your own Mustache HTML templates for reuse across multiple PDF generations. Once saved, reference them by ID in the /v1/create endpoint or use them with the batch generation API.
Endpoints Overview
Section titled “Endpoints Overview”| Method | Endpoint | Description |
|---|---|---|
POST | /v1/templates/saved | Save a new template |
GET | /v1/templates/saved | List your saved templates |
GET | /v1/templates/saved/:id | Get a specific template |
PUT | /v1/templates/saved/:id | Update a template |
DELETE | /v1/templates/saved/:id | Delete a template |
POST /v1/templates/saved
Section titled “POST /v1/templates/saved”POST /v1/templates/saved
Save a new custom template. Templates use Mustache syntax for data binding.
Request
Section titled “Request”Headers
Section titled “Headers”| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer gk_your_api_key |
Content-Type | Yes | application/json |
{ "name": "my-invoice", "type": "invoice", "description": "Custom invoice template with company branding", "html": "<html><head><style>body { font-family: 'Inter', sans-serif; }</style></head><body><h1>Invoice #{{invoice_number}}</h1><p>Client: {{client.name}}</p></body></html>", "schema": { "fields": [ { "name": "invoice_number", "type": "string", "required": true }, { "name": "client.name", "type": "string", "required": true } ] }, "style": "stripe-clean", "isDefault": false}Parameters
Section titled “Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Template name (1-255 characters) |
html | string | Yes | Mustache HTML template |
type | string | No | Document type: invoice, quote, report, certificate, letter, receipt, contract, custom |
description | string | No | Human-readable description (max 1000 characters) |
schema | object | No | Field schema for validation and documentation |
schema.fields | array | No | Array of field definitions |
schema.fields[].name | string | Yes | Field name (supports dot notation for nested fields) |
schema.fields[].type | string | Yes | Field type: string, number, boolean, array, object |
schema.fields[].required | boolean | No | Whether the field is required |
style | string | No | Style preset: stripe-clean, professional, minimal, bold, classic, corporate, modern, vibrant |
isDefault | boolean | No | Set as default template for this type (default: false) |
Response
Section titled “Response”Success (201 Created)
Section titled “Success (201 Created)”{ "success": true, "template": { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "my-invoice", "type": "invoice", "description": "Custom invoice template with company branding", "html": "<html>...</html>", "schema": { "fields": [ { "name": "invoice_number", "type": "string", "required": true }, { "name": "client.name", "type": "string", "required": true } ] }, "style": "stripe-clean", "isDefault": false, "version": 1, "createdAt": "2026-01-28T12:00:00.000Z", "updatedAt": "2026-01-28T12:00:00.000Z" }}Code Examples
Section titled “Code Examples”curl -X POST https://api.glyph.you/v1/templates/saved \ -H "Authorization: Bearer gk_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "name": "my-invoice", "type": "invoice", "description": "Custom invoice with company branding", "html": "<html><head><style>body { font-family: Inter, sans-serif; padding: 40px; } h1 { color: #1a1a1a; } .amount { font-size: 24px; font-weight: bold; }</style></head><body><h1>Invoice #{{invoice_number}}</h1><p>Client: {{client.name}}</p><p>Email: {{client.email}}</p><div class=\"amount\">Total: ${{total}}</div></body></html>", "schema": { "fields": [ { "name": "invoice_number", "type": "string", "required": true }, { "name": "client.name", "type": "string", "required": true }, { "name": "client.email", "type": "string" }, { "name": "total", "type": "number", "required": true } ] }, "style": "stripe-clean" }'JavaScript
Section titled “JavaScript”const response = await fetch('https://api.glyph.you/v1/templates/saved', { method: 'POST', headers: { 'Authorization': 'Bearer gk_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'my-invoice', type: 'invoice', description: 'Custom invoice with company branding', html: ` <html> <head> <style> body { font-family: 'Inter', sans-serif; padding: 40px; } h1 { color: #1a1a1a; } .amount { font-size: 24px; font-weight: bold; } </style> </head> <body> <h1>Invoice #{{invoice_number}}</h1> <p>Client: {{client.name}}</p> <p>Email: {{client.email}}</p> <div class="amount">Total: \${{total}}</div> </body> </html> `, schema: { fields: [ { name: 'invoice_number', type: 'string', required: true }, { name: 'client.name', type: 'string', required: true }, { name: 'client.email', type: 'string' }, { name: 'total', type: 'number', required: true }, ], }, style: 'stripe-clean', }),});
const { template } = await response.json();console.log(`Template saved with ID: ${template.id}`);Python
Section titled “Python”import requests
response = requests.post( 'https://api.glyph.you/v1/templates/saved', headers={ 'Authorization': 'Bearer gk_your_api_key', 'Content-Type': 'application/json', }, json={ 'name': 'my-invoice', 'type': 'invoice', 'description': 'Custom invoice with company branding', 'html': ''' <html> <head> <style> body { font-family: 'Inter', sans-serif; padding: 40px; } h1 { color: #1a1a1a; } .amount { font-size: 24px; font-weight: bold; } </style> </head> <body> <h1>Invoice #{{invoice_number}}</h1> <p>Client: {{client.name}}</p> <p>Email: {{client.email}}</p> <div class="amount">Total: ${{total}}</div> </body> </html> ''', 'schema': { 'fields': [ {'name': 'invoice_number', 'type': 'string', 'required': True}, {'name': 'client.name', 'type': 'string', 'required': True}, {'name': 'client.email', 'type': 'string'}, {'name': 'total', 'type': 'number', 'required': True}, ] }, 'style': 'stripe-clean', })
template = response.json()['template']print(f"Template saved with ID: {template['id']}")GET /v1/templates/saved
Section titled “GET /v1/templates/saved”GET /v1/templates/saved
List all templates saved under your API key.
Request
Section titled “Request”Headers
Section titled “Headers”| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer gk_your_api_key |
Query Parameters
Section titled “Query Parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
type | string | — | Filter by document type |
limit | number | 50 | Results per page (1-100) |
offset | number | 0 | Pagination offset |
Response
Section titled “Response”{ "success": true, "templates": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "my-invoice", "type": "invoice", "description": "Custom invoice with company branding", "style": "stripe-clean", "isDefault": false, "version": 1, "createdAt": "2026-01-28T12:00:00.000Z", "updatedAt": "2026-01-28T12:00:00.000Z" } ], "total": 1, "limit": 50, "offset": 0}Code Examples
Section titled “Code Examples”# List all templatescurl -X GET https://api.glyph.you/v1/templates/saved \ -H "Authorization: Bearer gk_your_api_key"
# Filter by typecurl -X GET "https://api.glyph.you/v1/templates/saved?type=invoice" \ -H "Authorization: Bearer gk_your_api_key"
# Paginate resultscurl -X GET "https://api.glyph.you/v1/templates/saved?limit=10&offset=20" \ -H "Authorization: Bearer gk_your_api_key"JavaScript
Section titled “JavaScript”const response = await fetch('https://api.glyph.you/v1/templates/saved?type=invoice', { headers: { 'Authorization': 'Bearer gk_your_api_key', },});
const { templates, total } = await response.json();console.log(`Found ${total} invoice templates`);
for (const template of templates) { console.log(`- ${template.name} (v${template.version})`);}GET /v1/templates/saved/:id
Section titled “GET /v1/templates/saved/:id”GET /v1/templates/saved/:id
Get a specific template by ID, including full HTML and schema.
Request
Section titled “Request”Headers
Section titled “Headers”| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer gk_your_api_key |
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Template ID |
Response
Section titled “Response”{ "success": true, "template": { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "my-invoice", "type": "invoice", "description": "Custom invoice with company branding", "html": "<html>...</html>", "schema": { "fields": [ { "name": "invoice_number", "type": "string", "required": true } ] }, "style": "stripe-clean", "isDefault": false, "version": 1, "createdAt": "2026-01-28T12:00:00.000Z", "updatedAt": "2026-01-28T12:00:00.000Z" }}Code Examples
Section titled “Code Examples”curl -X GET https://api.glyph.you/v1/templates/saved/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer gk_your_api_key"JavaScript
Section titled “JavaScript”const templateId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(`https://api.glyph.you/v1/templates/saved/${templateId}`, { headers: { 'Authorization': 'Bearer gk_your_api_key', },});
const { template } = await response.json();console.log(`Template: ${template.name}`);console.log(`HTML length: ${template.html.length} characters`);PUT /v1/templates/saved/:id
Section titled “PUT /v1/templates/saved/:id”PUT /v1/templates/saved/:id
Update an existing template. Only include the fields you want to change.
Request
Section titled “Request”Headers
Section titled “Headers”| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer gk_your_api_key |
Content-Type | Yes | application/json |
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Template ID |
All fields are optional. Include only what you want to update.
{ "name": "my-invoice-v2", "description": "Updated invoice template", "html": "<html>...</html>", "style": "professional"}Response
Section titled “Response”{ "success": true, "template": { "id": "550e8400-e29b-41d4-a716-446655440000", "name": "my-invoice-v2", "type": "invoice", "description": "Updated invoice template", "html": "<html>...</html>", "schema": { ... }, "style": "professional", "isDefault": false, "version": 2, "createdAt": "2026-01-28T12:00:00.000Z", "updatedAt": "2026-01-28T14:30:00.000Z" }}Code Examples
Section titled “Code Examples”curl -X PUT https://api.glyph.you/v1/templates/saved/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer gk_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "description": "Updated invoice template with new footer", "style": "professional" }'JavaScript
Section titled “JavaScript”const templateId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(`https://api.glyph.you/v1/templates/saved/${templateId}`, { method: 'PUT', headers: { 'Authorization': 'Bearer gk_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ description: 'Updated invoice template with new footer', style: 'professional', }),});
const { template } = await response.json();console.log(`Updated to version ${template.version}`);DELETE /v1/templates/saved/:id
Section titled “DELETE /v1/templates/saved/:id”DELETE /v1/templates/saved/:id
Delete a saved template. This action is permanent.
Request
Section titled “Request”Headers
Section titled “Headers”| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer gk_your_api_key |
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Template ID |
Response
Section titled “Response”{ "success": true, "deleted": "550e8400-e29b-41d4-a716-446655440000"}Code Examples
Section titled “Code Examples”curl -X DELETE https://api.glyph.you/v1/templates/saved/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer gk_your_api_key"JavaScript
Section titled “JavaScript”const templateId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(`https://api.glyph.you/v1/templates/saved/${templateId}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer gk_your_api_key', },});
const { deleted } = await response.json();console.log(`Deleted template: ${deleted}`);Using Custom Templates
Section titled “Using Custom Templates”Once saved, use your custom template with the /v1/create endpoint by passing the template ID:
curl -X POST https://api.glyph.you/v1/create \ -H "Authorization: Bearer gk_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "templateId": "550e8400-e29b-41d4-a716-446655440000", "data": { "invoice_number": "INV-2026-001", "client": { "name": "Acme Corp", "email": "billing@acme.com" }, "total": 4500 } }'Default Templates
Section titled “Default Templates”Setting isDefault: true marks a template as the default for its document type. When you have a default template:
- Only one template per type can be default
- Setting a new default automatically unsets the previous one
- Useful for automated workflows that don’t specify a template ID
// Set as default invoice templateawait fetch('https://api.glyph.you/v1/templates/saved', { method: 'POST', headers: { 'Authorization': 'Bearer gk_your_api_key', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'company-invoice', type: 'invoice', html: '...', isDefault: true, // This becomes the default invoice template }),});Mustache Template Syntax
Section titled “Mustache Template Syntax”Custom templates use Mustache for data binding. Here are the most common patterns:
Variable Interpolation
Section titled “Variable Interpolation”<h1>Invoice #{{invoice_number}}</h1><p>Client: {{client.name}}</p>Sections (Conditionals)
Section titled “Sections (Conditionals)”{{#paid}}<span class="badge">PAID</span>{{/paid}}
{{^paid}}<span class="badge warning">UNPAID</span>{{/paid}}<table> {{#items}} <tr> <td>{{description}}</td> <td>{{quantity}}</td> <td>${{total}}</td> </tr> {{/items}}</table>Nested Objects
Section titled “Nested Objects”<address> {{client.address.street}}<br> {{client.address.city}}, {{client.address.state}} {{client.address.zip}}</address>Error Responses
Section titled “Error Responses”400 - Validation Error
Section titled “400 - Validation Error”{ "error": "Validation failed", "code": "VALIDATION_ERROR", "details": [ { "path": ["name"], "message": "Template name is required" } ]}403 - Demo Tier Limitation
Section titled “403 - Demo Tier Limitation”{ "error": "Template persistence requires a registered API key. Please sign up to save templates.", "code": "DEMO_TIER_LIMITATION"}404 - Template Not Found
Section titled “404 - Template Not Found”{ "error": "Template not found", "code": "TEMPLATE_NOT_FOUND"}503 - Database Not Configured
Section titled “503 - Database Not Configured”{ "error": "Database is not configured", "code": "DATABASE_NOT_CONFIGURED"}See Also
Section titled “See Also”- POST /v1/create - Generate PDFs using saved templates
- Templates Overview - Browse built-in templates
- POST /v1/batch/generate - Batch PDF generation
- Mustache Documentation - Template syntax reference