Forms API
Read forms, create them, embed them on any site, and collect responses. All endpoints
use a forms key and respect every form feature: invite-only gating,
open/close schedules, response limits, conditional visibility and required-field
validation.
Base https://dev.bodek.us/v1/forms
Auth Authorization: Bearer bf_live_…
List forms
Lists the key owner's forms. Scope forms.read.
| Query | Description |
|---|---|
search | Filter by title. |
limit / offset | Pagination (default 25). |
curl https://dev.bodek.us/v1/forms \
-H "Authorization: Bearer bf_live_XXXX"
Create a form
Scope forms.write. Send a title, a list of questions, and optional settings.
curl -X POST https://dev.bodek.us/v1/forms \
-H "Authorization: Bearer bf_live_XXXX" -H "Content-Type: application/json" \
-d '{
"title": "Event signup",
"questions": [
{ "kind": "text", "preset": "title", "size": "xl", "bold": true,
"html": "Join us for the Spring Mixer" },
{ "question_text": "About you",
"answers": [
{ "type": "text", "label": "Full name", "required": true },
{ "type": "email", "label": "Email", "required": true }
] },
{ "question_text": "Will you attend?",
"answers": [
{ "type": "checkbox", "single": true, "options": ["Yes","No"], "required": true } ] }
],
"settings": { "thank_you_message": "See you there!", "max_responses": 100 }
}'
Returns the new form's id and its normalized schema. Mix in
kind: "text" items anywhere to add headings, instructions, or lists.
Get a form
Scope forms.read. Returns the full schema — items, answers (with each
answer's field name), and settings. Every item in questions
carries a kind: either "question" (the default — has
question_text and answers) or "text" (a
display-only content block — see below). Older forms created before content blocks
existed simply omit kind on their questions; treat a missing
kind as "question".
Content blocks
A content block is a styled, display-only item — a heading, a paragraph, or a bullet/numbered list — that renders inline among your questions but collects no answer. Use it for section titles, instructions, legal text, and the like. Content blocks never appear in responses, CSV exports, or response analytics, and they are skipped by required-field and conditional-logic evaluation on submit.
A content block is any item in questions with "kind": "text":
{
"kind": "text",
"preset": "title", // "title" | "subtitle" | "paragraph" | "custom"
"size": "xl", // "sm" | "md" | "lg" | "xl"
"align": "left", // "left" | "center" | "right"
"bold": true,
"color": "#1a2230", // "" or a #RRGGBB hex; blank inherits the form color
"html": "<b>Welcome</b> — please read <a href=\"https://x.tld\">the terms</a>.<ul><li>One</li><li>Two</li></ul>"
}
| Field | Notes |
|---|---|
preset | UI convenience that seeds the other style fields. title, subtitle, paragraph, or custom when manually tweaked. Optional; defaults to paragraph. |
size | One of sm, md, lg, xl. Defaults to md. |
align | left, center, or right. |
bold | Boolean; bolds the whole block. |
color | #RRGGBB or empty. |
html | Rich content. Sanitized server-side against a strict allowlist — only p, br, b, strong, i, em, u, s, ul, ol, li, a, span survive, all attributes are stripped except href on links (http/https/mailto only, forced to rel="noopener noreferrer nofollow"). Anything else is removed. Max 20 000 characters. |
Create or update forms with content blocks exactly as you would questions — just put
kind: "text" items in the questions array, in the order you
want them rendered. They may be freely interleaved with questions.
Branding & theme
Forms carry an optional settings.theme object for custom branding: a logo
at the top of the form plus page, card, text and accent colors. When
enabled is false (or the object is absent) the form uses the
clean standard theme — this is the default, and existing forms are unaffected.
On read (GET /v1/forms/{id} and the embed config) the theme
is returned with a resolved absolute logo_url:
"theme": {
"enabled": true,
"page_bg": "#f4f6f9",
"card_bg": "#ffffff",
"text_color": "#1a2230",
"accent": "#0d6efd",
"field_bg": "#ffffff",
"field_text": "#1a2230",
"logo_url": "https://forms.bodeksolutions.com/questionnaires/ABC1234567/creator/logo_….png",
"logo_align": "left",
"logo_height": 64
}
On write (create/update) send the same fields, but the logo is set by
uploading through the form builder UI rather than the JSON API — the API accepts and
preserves an already-uploaded logo but does not accept binary uploads. Colors must be
#RRGGBB (3-digit hex is expanded); invalid colors are dropped. Heights are
clamped to 24–200 px.
| Field | Notes |
|---|---|
enabled | Master switch. If false and no custom values are set, the theme is omitted entirely. |
page_bg / card_bg | Page and question-card background, #RRGGBB or blank. |
text_color / accent | Body text and accent (buttons, highlights). The selected rating and submit button automatically pick a readable text color from the accent luminance. |
field_bg / field_text | Background and text color of question input fields (text boxes, dropdowns). Blank keeps the default. |
logo_align / logo_height | Logo placement and pixel height (24–200). |
Embed config
A public-safe render config used by the SDK: items (questions and content
blocks, each tagged with kind), the accepting state and
reason, invite requirements, the submit URL, and the theme object
(with a resolved logo_url) so embedded forms can match your branding.
Submit a response
Scope forms.submit. Field names are q{question}_a{answer}
(see each answer's field in the schema). Gating, response limits,
conditional visibility and required-field checks all run server-side; validation
failures return 422 with the offending fields.
curl -X POST https://dev.bodek.us/v1/forms/ABC1234567/responses \
-H "Authorization: Bearer bf_live_XXXX" -H "Content-Type: application/json" \
-d '{
"fields": { "q0_a0": "Jane Doe", "q0_a1": "jane@example.com", "q1_a0": "Yes" },
"invite_token": "OPTIONAL_IF_INVITE_ONLY"
}'
List responses
Scope forms.read. Returns submitted responses (owner key) with their
decoded answer data. Supports search, limit, offset.
Field types
| type | Value you send |
|---|---|
text / longtext | string |
email | valid email string |
number | number |
date | YYYY-MM-DD |
dropdown | one of options |
checkbox (single) | one of options |
checkbox (multi) | array of options |
rating | integer between from and to |
file | not supported over the JSON API |
For multi-select with "other" enabled, also send q{q}_a{a}_other_text.
Conditional questions/answers carry a show_if rule; hidden fields are
skipped and never required. Items with kind: "text" are content blocks,
not fields — they take no value and are skipped on submit.
Embeddable SDK
Drop a form onto any site with one div and one script. Use a publishable
forms key restricted to your domains (scopes forms.read +
forms.submit).
<div data-bodek-form="ABC1234567" data-bodek-key="bf_live_XXXX"></div>
<script src="https://forms.bodeksolutions.com/sdk/bodek-forms.js"></script>
The widget renders inputs by type, draws content blocks and applies your branding
(logo and colors) from the embed config, applies conditional logic client-side
(re-validated server-side), and submits. For invite-only forms add
data-bodek-invite="<token>".