Skip to content

Build a form, step by step

The create, edit, save and publish workflow an assistant follows in trulyform, including revisions, retries, conflicts and form design advice.

View as Markdown

All docs pages
On this page

What you need to know

  1. Find out what the form is for, who will fill it in and what happens with the answers.
  2. create_form (or get_form for an existing form) returns the whole definition and its draft_revision.
  3. Edit the complete definition. Save it with save_form_draft, passing the current revision as base_revision and a new UUIDv7 as mutation_id.
  4. Read the issues in the reply. Fix them and save again until the list is empty.
  5. Show the person what you built. Call publish_form only when they ask, with the revision they reviewed.

Before you build

Ask, or work out from the conversation:

  • The goal. What decision or task do the answers feed? Leave out every question that does not serve it.
  • The respondent. A customer on a phone, a colleague, a guest? It sets the tone and the length.
  • What is required. Require only the answers the person cannot do without.
  • What happens next. The ending screen should tell respondents what to expect.

For a long or sensitive form, list the questions in the chat and let the person confirm them before you save.

Create the form

Call create_form with a name. The reply's form holds the id, draft_revision (1 for a new form), the definition and its current issues.

A new form has a welcome screen, one untitled short text question, a thank-you ending and a white theme. Its issues ask for the missing question title. Replace that starter question with your own, or reuse its ID for your first question; nothing depends on it yet.

create_form is not idempotent. If the reply is lost, call list_forms and look for the form before you create another one.

To change an existing form instead, find it with list_forms and read it with get_form.

Edit the definition

  • Start from the definition you just read. Keep every top-level key, including schemaVersion, mediaDefaults, theme and settings, and keep every existing ID. See The form definition.
  • Write the questions in fields, in the order respondents will see them. Start each one from its template in Question types or from list_field_types, then give it a title.
  • Give every new question, choice, row, rule and calculation a fresh ID: a lowercase UUIDv7, unique within the form. If you cannot generate one, call list_field_types with id_count and use the IDs it returns. Details are in Identifiers.
  • Titles and descriptions are rich text, not plain strings. See Rich text.
  • To change a question's type, replace the question with a new one that has a new ID, then update any rules, calculations or piped answers that pointed at the old ID.
  • Label the last question's button "Submit" or similar with its nextButton setting. There is no separate submit button.

A complete, publishable definition:

Contact request. The smallest useful form: three required questions, and an ending that greets the person by the name they typed.

It shows:

  • required questions
  • placeholder and maxLength settings
  • an answer piped into the ending

Contact request definition

{
    "schemaVersion": 2,
    "mediaDefaults": {
        "assetId": null,
        "layout": "above",
        "altText": ""
    },
    "welcome": {
        "enabled": true,
        "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Get in touch"}]}]},
        "description": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Three quick questions. We reply within one working day."}]}]},
        "button": "Start"
    },
    "ending": {
        "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Thanks, "},{"type":"answerToken","attrs":{"source":"answer","ref":"01990000-0000-7000-8000-000000000001","fallback":"there"}},{"type":"text","text":"!"}]}]},
        "description": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"We will reply to your email within one working day."}]}]}
    },
    "fields": [
        {
            "id": "01990000-0000-7000-8000-000000000001",
            "kind": "short_text",
            "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"What is your name?"}]}]},
            "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
            "required": true,
            "options": [],
            "rows": [],
            "settings": {"nextButton":"Next","placeholder":"Jane Smith"},
            "media": {"inherit":true,"assetId":null,"layout":"above"}
        },
        {
            "id": "01990000-0000-7000-8000-000000000002",
            "kind": "email",
            "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"What is your email address?"}]}]},
            "description": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"We only use it to reply to this message."}]}]},
            "required": true,
            "options": [],
            "rows": [],
            "settings": {"nextButton":"Next"},
            "media": {"inherit":true,"assetId":null,"layout":"above"}
        },
        {
            "id": "01990000-0000-7000-8000-000000000003",
            "kind": "long_text",
            "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"How can we help?"}]}]},
            "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
            "required": true,
            "options": [],
            "rows": [],
            "settings": {"nextButton":"Send message","maxLength":2000},
            "media": {"inherit":true,"assetId":null,"layout":"above"}
        }
    ],
    "theme": {
        "preset": "paper",
        "background": "#ffffff",
        "foreground": "#111827",
        "primary": "#2563eb",
        "buttonText": "#ffffff",
        "font": "Inter",
        "radius": 12,
        "density": "comfortable",
        "logoAssetId": null,
        "backgroundAssetId": null
    },
    "settings": {
        "allowReanswer": true,
        "hiddenFields": [],
        "fillLimit": null
    },
    "rules": [],
    "calculations": []
}

Select the text to copy it.

Save the draft

Call save_form_draft with:

Parameter Value
form_id The form's ID.
base_revision The draft_revision you last read, or the revision your last save returned.
mutation_id A new UUIDv7 for every save.
name The full form name. An empty string saves it as "Untitled form".
definition The complete definition, including the parts you did not change. A partial object is rejected.

The reply holds the new revision, issues, saved_at and the published_version_id. Use revision as the base_revision of your next save.

If the network fails and you do not know whether a save went through, send the identical request again with the same mutation_id. trulyform returns the original result instead of saving twice. Reusing a mutation_id with different content is rejected.

When a save is rejected

  • validation_failed with errors: the definition has the wrong shape, such as an unknown key, an invalid ID or a plain-string title. Nothing was saved. Fix the paths named in errors and save again with the same base_revision and a new mutation_id. Limits and errors explains the messages.
  • draft_conflict: a newer draft exists, perhaps because the person is editing the form in trulyform. Call get_form, apply your change to the definition it returns, and save with its draft_revision. Never overwrite the newer draft with an old copy.

Fix the issues

A save succeeds even when the form is not ready to publish. The reply's issues list what still blocks publication:

One issue

{
    "nodeId": "01990000-0000-7000-8000-000000000001",
    "path": "title",
    "message": "Write your question.",
    "severity": "error"
}

Select the text to copy it.

nodeId is the ID of the question, rule or calculation with the problem, or form, theme, welcome or ending. Each issue also has a docs field naming the section that explains it; pass it to read_docs. Fix every issue and save again. Publish issues lists the common ones.

Review with the person

Before publishing, summarize the form in plain words: the questions in order, which ones are required, and what the logic does ("If they choose In person, we ask about dietary needs"). Give the person the form's editor_url from create_form, get_form or list_forms: it opens the form in the trulyform editor, where they can preview it exactly as respondents will see it and make changes themselves.

Publish

Call publish_form with the form_id and the revision the person reviewed, and only after they ask you to publish. The reply contains the public_url to share.

  • If the draft changed since that revision, publishing fails with draft_conflict. Show the person the newer draft first.
  • If the draft still has issues, publishing fails with validation_failed and the list of issues.
  • Publishing identical content again does not create a new version.
  • Responses already collected keep the version they were answered on.
  • A closed form stays closed. The person reopens it in trulyform.

Design a good form

  • Keep it short. Most forms need fewer than ten questions. Each extra question costs answers.
  • One idea per question, in plain words. Put help text in the description, not the title.
  • Match the type to the answer. Use email for email addresses, dropdown for long lists such as countries, single_choice for a handful of options, opinion_scale or nps for ratings, and consent for agreements. A number answer may have decimals, so ask for whole counts, such as guests, with a choice question.
  • Use logic instead of "If yes…" questions, so people only see what applies to them.
  • Use the welcome screen to say what the form is for and how long it takes. Turn it off for very short forms.
  • Use the ending to thank people and say what happens next. Piping in their name makes it personal.

Ready to build?
Connect your assistant.

Pick your assistant, sign in to trulyform, and ask for the form you need.

Choose your assistant