Schema Items
How to read and handle intake bot schema items — questions and content blocks — from the API.
The GET /intake-bots/:id endpoint returns a schema object that describes all items in an intake bot. Items fall into two categories: fields (question items that collect user input) and content blocks (informational items that display content between questions).
Schema Response Shape
{
"success": true,
"data": {
"id": "clxyz...",
"name": "Onboarding Bot",
"schema": {
"fields": [
{
"id": "field_1",
"name": "full_name",
"label": "What's your full name?",
"type": "short_text",
"required": true
},
{
"id": "cb_1",
"kind": "content",
"name": "intro_block",
"label": "Welcome Message",
"content_type": "static",
"body": "Thanks **{{full_name}}**! Here's what to expect next..."
},
{
"id": "field_2",
"name": "company",
"label": "What company are you with?",
"type": "short_text",
"required": false
}
]
}
}
}Distinguishing Item Types
Use the kind field to determine how to handle each item:
kind value | Meaning | Has type? | Accepts input? |
|---|---|---|---|
"field" or absent | Question / input field | Yes | Yes |
"content" | Content block (display only) | No | No |
Backward compatibility: Fields created before content blocks were introduced do not have a
kindproperty. Treat items withoutkind, or withkind: "field", as question fields.
Recommended Handling
for (const item of schema.fields) {
if (item.kind === 'content') {
// Display-only item — skip when building submission data
continue;
}
// Regular question field
console.log(item.name, item.type, item.required);
}Content Block Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique item ID |
kind | "content" | Identifies this as a content block |
name | string | Internal slug name |
label | string | null | Admin-facing display name |
content_type | "plain" | "rich" | "editor" | "ai_generated" | Authoring mode — plain (text), rich/editor (Tiptap), ai_generated (runtime AI) |
body | string | null | Content for static types; AI prompt for ai_generated |
template_variables | boolean | null | Whether body contains {{field_name}} references |
continue_button_text | string | null | Label for the "Continue" button (defaults to "Continue") |
ctas | array | null | Call-to-action buttons (Pro plan only) |
ai_generated content blocks
When content_type is "ai_generated", body contains the author's prompt, not the generated output. Generated content is produced at runtime per respondent and stored in the conversation history.
CTAs
Each CTA in the ctas array:
{
"id": "cta_1",
"label": "View Pricing",
"style": "primary",
"action": "modal",
"url": "https://example.com/pricing"
}| Field | Values | Description |
|---|---|---|
id | string | Unique CTA identifier |
label | string | Button text |
style | "primary" | "secondary" | "link" | Button style |
action | "modal" | "link" | Opens in overlay or new tab |
url | string | null | Target URL (may contain {{field_name}} references) |
Submitting Data
Content blocks are never included in submission data. When creating a submission via the API (POST /intake-bots/:id/submissions), only provide values for field items (those without kind: "content"):
{
"data": {
"full_name": "Jane Doe",
"company": "Acme Corp"
}
}Passing a content block name as a key in data has no effect and is silently ignored.
Conditional Logic
Content blocks support the same conditional_logic structure as fields. A content block with conditional logic may or may not appear in a given session depending on the respondent's answers. The schema always returns all items; evaluate conditional_logic client-side if you need to predict which items will be shown.