Elizon Docs
Workflows

Form trigger

Let a person start a run by filling in a hosted form.

Lets a person start a run by filling in a hosted form — a request form, an intake questionnaire, an internal ticket — with nothing for you to build or host. Add a Form Trigger node (trigger_form), declare its fields, and publish.

https://<the origin you open Studio on>/forms/{formToken}

The form page is served by the app, not by the API, so this is not an api. host — the panel builds the link from the origin you are already on.

The formToken is opaque, is never the workflow's own id, and is minted by publishing — Studio's Form Trigger panel shows the real link once a deployment exists, and says the workflow is not published yet until then. It is carried forward unchanged every time you republish, so a URL you have already handed out keeps working across deploys. A workflow whose published graph has no Form Trigger node has no token at all.

Each field carries a name and a human-readable label — deliberately two things, since ticketId is the right data key and the wrong question to put in front of a person. Eleven field types are available (text, textarea, number, email, password, date, dropdown, radio, checkboxes, file, hidden); the three choice types carry their own options, and a file field carries its own Accepted file types and Allow multiple files options — see File uploads below.

The three auth modes

Set on the node, applied by the published deployment:

ModeWho gets in
NoneAnyone with the link.
BasicA username and password you set on the node. The password is bcrypt-hashed at publish time and never stored in the graph.
Elizon loginA signed-in, active member of the workflow's own organization — not any Elizon user.

Under Basic and Elizon login the field list is itself protected: an unauthenticated visitor cannot see what the form even asks, let alone submit to it. A visitor who is signed in but belongs to another organization gets a specific "not a member of this organization" state rather than a silent empty form or a second sign-in prompt.

Changing the mode or the username takes effect on the next publish — and a username change has to carry a new password with it, because a stored hash is only kept for the username it was set for; publishing a renamed Basic form without one is refused rather than left accepting the old pair. Changing nothing keeps the existing password working — an ordinary republish does not invalidate credentials you have already handed out.

File uploads

A file field does not carry bytes in the submission itself — it carries a reference, minted by its own upload endpoint:

POST /api/v1/forms/{formToken}/upload?field={fieldName}

One call per file, multipart/form-data with a single file part — mirroring how the field behaves on the hosted page: select a file and it uploads immediately, before the rest of the form is necessarily filled in. A successful upload returns:

{ "fileId": "…", "filename": "resume.pdf", "sizeBytes": 48213 }

Submit the returned fileId as the field's value — a plain string, or a string[] of them when the field's Allow multiple files option is on — never the file itself. The submission ingress resolves each id before the run starts, so a workflow reads the field's real value as:

{
  "filename": "resume.pdf",
  "contentType": "application/pdf",
  "sizeBytes": 48213,
  "text": "…extracted text…"
}

text is extracted once, at upload time: .txt/.csv/.md/.html/.xml are read directly, .pdf/.docx/.doc/.xlsx/.xls are parsed, and anything else (images included) falls back to a short description rather than failing the upload.

Two settings live on the field itself:

SettingEffect
Accepted file typesA comma-separated extension list (.pdf,.docx,.csv). Empty accepts any extension.
Allow multiple filesOff by default. On, the field submits an array and the upload endpoint is called once per file.
StatusMeaning
201Stored — body is the fileId/filename/sizeBytes shown above.
400field names no file field on this form.
422The file was rejected — wrong extension, over the size limit, or storage failed.
429Rate limit — the same one submissions share.

Upload size is a platform setting, not a per-form one

Every upload is capped at the same size regardless of the field's own configuration — 16 MB by default. A rejected file's 422 response names the real, current limit, so a form need not duplicate the number anywhere.

An upload nobody ever submits is deleted after 2 hours. Once a submission actually uses it, it is kept for 30 days from that submission — long enough to outlive a run parked on a Human Review node — after which it is deleted along with its stored bytes. Both windows, like the size limit, are platform settings rather than something a form author configures.

Submission responses

A submission is validated against the declared fields in the request, so an incomplete form reports its errors on the page rather than starting a run that fails where nobody is looking.

StatusMeaning
202Accepted — body is { "status": "received" }. The run proceeds in the background.
400A field failed validation — issues names each one — or the graph behind the form is empty, or the body holds more than 20,000 values (array elements plus object keys across the whole body, each array or object counting 5 more) or nests deeper than 256 levels.
401Credentials required, or wrong.
403Signed in, but not a member of the workflow's organization.
404No published form matches that token — including an unpublished or deleted workflow.
413Body exceeded the 1 MB limit.
429Rate limit, or the concurrency ceiling.

A submitter never sees the run's result

The acknowledgment is immediate and static: no execution id, no polling handle, nothing that waits for the run. A workflow can park 30 days on a Human Review node, which makes "wait for the result" the wrong contract for a browser tab. If the submitter needs an answer, have the graph send them one.

Submitted values arrive under body, keyed by each field's name, so a form-driven graph reads {{ trigger.body.email }} — the same envelope the webhook path uses.

Configuration

FieldTypeDefaultDescription
fieldslist of form fields (below)[]The form's questions, in order.
authModenone | basic | elizon_loginnoneWho can open and submit the form — see The three auth modes.
basicAuthUsernamestring—The username for basic. The password is set in the panel, hashed at publish, and never stored in the graph.

Each entry of fields:

KeyTypeDescription
namestringThe data key the value arrives under ({{ trigger.body.<name> }}).
labelstringThe question shown to the person.
typestringtext, textarea, number, email, password, date, dropdown, radio, checkboxes, file or hidden.
requiredbooleanWhether the form can be submitted without it.
optionsstring[]The choices. Required on dropdown, radio and checkboxes; refused on every other type.
acceptedExtensionsstring[]file only: lower-case extensions without a dot (pdf, docx). Empty accepts any extension.
allowMultiplebooleanfile only: the field submits a list of files.

Output

ReferenceValue
{{ trigger.body.<name> }}The submitted value. A checkboxes field is a list; a file field is { filename, contentType, sizeBytes, text }, or a list of them.
{{ run.trigger.type }}form.
{{ run.trigger.id }}This node's id.

Limits and errors

  • One per workflow. The form URL is issued once per workflow, so a second Form trigger is refused at save.
  • Bodies over 1 MB are refused, and so are bodies over 20,000 values (array elements plus object keys across the whole body, each array or object counting 5 more) or nested deeper than 256 levels, with 400, even under 1 MB; uploads are capped by a platform setting (16 MB by default).
  • See Submission responses for every status code.

Example

workflows/service-request.yaml
name: Service request
description: Takes a service request from the customer form.
graph:
  nodes:
    - id: request-form
      type: trigger_form
      position: { x: 0, y: 0 }
      data:
        authMode: none
        fields:
          - { name: email, label: Your email, type: email, required: true }
          - { name: machine, label: Machine, type: dropdown, required: true, options: [X200, X300] }
          - {
              name: photo,
              label: Photo of the fault,
              type: file,
              required: false,
              acceptedExtensions: [jpg, png],
            }
    - id: triage
      type: ai_agent
      position: { x: 280, y: 0 }
      data:
        mode: existing
        agent: agents.triage
        agentName: Triage
        inputMappings: []
  edges:
    - { id: form-to-triage, source: request-form, target: triage }

In a Package

A Package declares no manifest trigger for a form: the Form trigger node in the packaged workflow is the entry point, and the form's link is minted when the installed workflow is first published. On the node:

  • Travels: fields, authMode
  • Converted: none
  • Never carried: basicAuthUsername

A form that uses Basic auth asks the installer for its username (and a password) at install; neither is ever carried. See Values the installer supplies.