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
    • Runtype MCP server
Dashboard
LogoLogo
On this page
  • 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

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 ("1.0", "1.1", or "2.0"; "2.0" is the current default). At "2.0" (the default), each inline agent’s runtime fields (model, systemPrompt, tools, …) live under agent.config; at "1.0"/"1.1" they sit directly on the agent object.

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

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.