For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Dashboard
User GuideDeveloper GuidesAPI Reference
User GuideDeveloper GuidesAPI Reference
  • Getting Started
    • Introduction
    • Authentication
    • Quickstart
  • Guides
    • Working with Tools
    • Runtime Tools
    • FPO Templates
    • Importing Products
  • Integrations
    • MCP Servers
Dashboard
LogoLogo
On this page
  • FPO Templates
  • Template Shape
  • Variable Manifest
  • Resolution Rules
  • Example Template
  • Validate a Template Before Publishing
  • Creation Workflow
Guides

FPO Templates

Was this page helpful?
Previous

Importing Products

Next
Built with

FPO Templates

An FPO template is a wrapper around the canonical Full Product Object (FPO) format. It lets you publish a reusable product definition while leaving specific values for the importer to fill in later.

Use an FPO template when:

  • the product structure is stable
  • the deployer needs to supply names, URLs, API keys, or environment-specific values
  • you want a /now preview before the product is created

Template Shape

An FPO template has three top-level keys:

  • version: the template document format version. Current value: 1.0
  • productObject: a normal Full Product Object
  • template.variables: metadata for every variable referenced inside the FPO

The nested productObject must also declare its own version. Current value: 1.0.

Variable references are allowed in string leaves inside the FPO:

1"name": "{{productName}}"

The full example used in this guide is available at docs/templates/quick-start/customer-support-fpo-template.json.

Variable Manifest

Each variable must be declared in template.variables with:

FieldRequiredNotes
keyYesReferenced by {{key}} inside productObject
labelYesUser-facing form label
descriptionNoShown in preview UIs
inputTypeYesOne of text, textarea, url, secret, select, number, boolean
requiredYesWhether the importer must supply a value
defaultValueNoAllowed for non-secret variables only
placeholderNoHelpful UI hint
optionsSelect onlyRequired when inputType is select

Resolution Rules

  • Every {{variableName}} reference must be declared in the manifest
  • Inline defaults such as {{apiKey|default}} are not supported
  • Defaults must be defined with template.variables[].defaultValue
  • Secret variables cannot define defaults
  • If a field value is exactly {{variableName}} and the variable type is number or boolean, the resolved value keeps that scalar type
  • Otherwise, substitutions resolve to strings

Example Template

1{
2 "version": "1.0",
3 "productObject": {
4 "version": "1.0",
5 "product": {
6 "name": "{{productName}}",
7 "description": "Support automation for {{companyName}}",
8 "metadata": {
9 "requireEscalationApproval": "{{requireEscalationApproval}}",
10 "responseTimeoutMinutes": "{{responseTimeoutMinutes}}"
11 }
12 },
13 "capabilities": [
14 {
15 "id": "cap_support",
16 "name": "Support Agent",
17 "description": "Handle incoming support requests.",
18 "agent": {
19 "name": "{{productName}} Agent",
20 "description": "Handle support requests for {{companyName}}.",
21 "model": "claude-sonnet-4-5",
22 "systemPrompt": "Use a {{brandVoice}} tone when responding."
23 }
24 }
25 ],
26 "tools": [
27 {
28 "id": "tool_search",
29 "type": "integration",
30 "provider": "{{searchProvider}}",
31 "name": "Knowledge Search",
32 "config": {
33 "apiKey": "{{searchApiKey}}",
34 "baseUrl": "{{knowledgeBaseUrl}}"
35 },
36 "auth": {
37 "type": "user_provided",
38 "setupRequired": true,
39 "secrets": [
40 {
41 "key": "searchApiKey",
42 "required": true
43 }
44 ],
45 "setupInstructions": {
46 "summary": "Add your search provider key",
47 "steps": ["Create an API key", "Paste it into the deployment form"]
48 }
49 }
50 }
51 ],
52 "surfaces": [
53 {
54 "id": "surface_chat",
55 "name": "{{productName}} Chat",
56 "type": "chat",
57 "config": {},
58 "routes": [
59 {
60 "capabilityId": "cap_support"
61 }
62 ]
63 }
64 ],
65 "_meta": {
66 "schemaVersion": "1.0",
67 "catalogVersion": "1.0",
68 "generatedAt": "2026-03-07T00:00:00.000Z",
69 "generatorVersion": "1.0.0",
70 "planHash": "template-example"
71 }
72 },
73 "template": {
74 "variables": [
75 {
76 "key": "productName",
77 "label": "Product Name",
78 "inputType": "text",
79 "required": true,
80 "defaultValue": "Acme Support Copilot"
81 },
82 {
83 "key": "companyName",
84 "label": "Company Name",
85 "inputType": "text",
86 "required": true
87 },
88 {
89 "key": "knowledgeBaseUrl",
90 "label": "Knowledge Base URL",
91 "inputType": "url",
92 "required": true
93 },
94 {
95 "key": "searchProvider",
96 "label": "Search Provider",
97 "inputType": "select",
98 "required": true,
99 "defaultValue": "firecrawl",
100 "options": [
101 { "label": "Firecrawl", "value": "firecrawl" },
102 { "label": "Exa", "value": "exa" }
103 ]
104 },
105 {
106 "key": "requireEscalationApproval",
107 "label": "Require Escalation Approval",
108 "inputType": "boolean",
109 "required": true,
110 "defaultValue": true
111 },
112 {
113 "key": "responseTimeoutMinutes",
114 "label": "Response Timeout Minutes",
115 "inputType": "number",
116 "required": true,
117 "defaultValue": 15
118 },
119 {
120 "key": "brandVoice",
121 "label": "Brand Voice",
122 "inputType": "textarea",
123 "required": false,
124 "defaultValue": "Calm and direct."
125 },
126 {
127 "key": "searchApiKey",
128 "label": "Search API Key",
129 "inputType": "secret",
130 "required": true
131 }
132 ]
133 }
134}

Validate a Template Before Publishing

Use POST /v1/public/products/validate-template before shipping a template to users.

cURL
$curl https://api.runtype.com/v1/public/products/validate-template \
> -H "Content-Type: application/json" \
> -d @docs/templates/quick-start/customer-support-fpo-template.json
TypeScript
1const template = await fetch('/customer-support-fpo-template.json').then((response) => response.json())
2
3const validation = await fetch('https://api.runtype.com/v1/public/products/validate-template', {
4 method: 'POST',
5 headers: {
6 'Content-Type': 'application/json',
7 },
8 body: JSON.stringify(template),
9}).then((response) => response.json())
10
11console.log(validation.valid)
12console.log(validation.errors)
13console.log(validation.referencedVariableKeys)
14console.log(validation.defaultsSufficient)

The validation response reports:

  • structural template errors
  • undeclared variable references
  • unused manifest variables
  • normalized variable metadata
  • whether defaults are enough to produce a valid resolved FPO without extra input

Creation Workflow

Once a template validates:

  1. Preview it with POST /v1/quick-start/imports/preview
  2. Collect missing variable values from the preview response
  3. Create the product with POST /v1/quick-start/create

See Importing Products for the full import-session flow.

Secret variables are part of the manifest, but their values should only be provided at create time. Do not put live secrets into template files or default values.