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

GET /v1/forms

Lists the key owner's forms. Scope forms.read.

QueryDescription
searchFilter by title.
limit / offsetPagination (default 25).
curl https://dev.bodek.us/v1/forms \
  -H "Authorization: Bearer bf_live_XXXX"

Create a form

POST /v1/forms

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

GET /v1/forms/{id}

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>"
}
FieldNotes
presetUI convenience that seeds the other style fields. title, subtitle, paragraph, or custom when manually tweaked. Optional; defaults to paragraph.
sizeOne of sm, md, lg, xl. Defaults to md.
alignleft, center, or right.
boldBoolean; bolds the whole block.
color#RRGGBB or empty.
htmlRich 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.

FieldNotes
enabledMaster switch. If false and no custom values are set, the theme is omitted entirely.
page_bg / card_bgPage and question-card background, #RRGGBB or blank.
text_color / accentBody text and accent (buttons, highlights). The selected rating and submit button automatically pick a readable text color from the accent luminance.
field_bg / field_textBackground and text color of question input fields (text boxes, dropdowns). Blank keeps the default.
logo_align / logo_heightLogo placement and pixel height (24–200).

Embed config

GET /v1/forms/{id}/embed

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

POST /v1/forms/{id}/responses

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

GET /v1/forms/{id}/responses

Scope forms.read. Returns submitted responses (owner key) with their decoded answer data. Supports search, limit, offset.

Field types

typeValue you send
text / longtextstring
emailvalid email string
numbernumber
dateYYYY-MM-DD
dropdownone of options
checkbox (single)one of options
checkbox (multi)array of options
ratinginteger between from and to
filenot 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>".