Forms

Build public forms with the GraphQL API — create a form, configure its fields, and collect submissions that become records in a workspace.


A form is a public page that collects responses and turns each submission into a record in a workspace. Forms are Form objects scoped to a workspace (Project in the API); each visible input is a FormField, and a submission creates a Todo (record) in the form’s target list.

There are two sides to the API:

  • Building & managing forms (authenticated) — create a form, add and order its fields, theme it, and activate it. Requires project-level OWNER, ADMIN, or MEMBER.
  • Submitting a form (public) — submitForm is unauthenticated and rate-limited, so a respondent never needs an account or token header. This is what Blue’s hosted form page calls.

All authenticated examples use personal access token headers and target https://api.blue.app. See Authentication for how to generate a token.

List forms

forms returns a paginated list of the forms in one workspace.

query ListForms {
  forms(filter: { projectId: "project_123" }, sort: updatedAt_DESC, take: 20) {
    items {
      id
      title
      isActive
    }
  }
}
ArgumentTypeRequiredDescription
filterFormFilterInputNo{ projectId } — scope to one workspace (its id, a CUID).
sortFormSortNoupdatedAt_DESC (default) or title_ASC.
skipIntNoOffset for pagination. Default 0.
takeIntNoPage size. Default 20.

The result is a FormPagination with items (an array of Form) and pageInfo.

Get one form

form fetches a single form — including its ordered formFields — by id.

query GetForm {
  form(id: "form_123") {
    id
    title
    description
    isActive
    primaryColor
    formFields {
      id
      field
      name
      required
      position
    }
  }
}

Create a form

createForm creates an empty form in a workspace. Only projectId is needed to start; everything else has sensible defaults you can fill in later with updateForm.

mutation CreateForm {
  createForm(
    input: {
      projectId: "project_123"
      title: "Contact us"
      description: "Tell us how we can help."
      primaryColor: "#2563eb"
      hideBranding: false
    }
  ) {
    id
    title
    isActive
  }
}

CreateFormInput

FieldTypeRequiredDescription
projectIdStringYesWorkspace id (CUID) the form belongs to.
titleStringNoForm title shown to respondents.
descriptionStringNoIntro text under the title.
primaryColorStringNoAccent color, hex (e.g. #2563eb).
hideBrandingBooleanNoHide the “Powered by Blue” footer.
assigneeIds[String!]NoUsers auto-assigned to records this form creates.

A new form is not active until you set isActive: true with updateForm.

Add and update fields

A form’s inputs are FormField rows. upsertFormField both creates and edits a field: pass an existing formFieldId to update it, or a new id to create one. field selects what the input maps to — a built-in record property or a custom field.

mutation AddNameField {
  upsertFormField(
    input: {
      formId: "form_123"
      formFieldId: "field_new_1"
      field: title
      name: "Your name"
      placeholder: "Ada Lovelace"
      required: true
      position: 1
    }
  ) {
    id
    field
    name
    required
    position
  }
}

UpsertFormFieldInput

FieldTypeRequiredDescription
formIdString!YesThe form to add the field to.
formFieldIdString!YesExisting field id to update, or a new id to create.
fieldFormFieldsField!YesWhat the input maps to (see below).
customFieldIdStringWhen field: customThe workspace custom field this input writes to.
nameStringNoLabel shown above the input.
placeholderStringNoPlaceholder text inside the input.
positionFloatNoOrder within the form (ascending).
requiredBooleanNoWhether a response is mandatory.
hiddenBooleanNoHide the field from the public form (e.g. a pre-filled value).
addToDescriptionBooleanNoAppend the field’s value to the created record’s description.
extraInfoStringNoHelper text shown under the input.

field accepts: title, description, tags, assignees, startedAt, duedAt, or custom. Use custom together with customFieldId to collect a value for one of the workspace’s custom fields.

List a form’s fields with formFields:

query FormFields {
  formFields(filter: { formId: "form_123" }) {
    id
    field
    name
    required
    position
  }
}

Remove a field with deleteFormField(id: "field_123"), which returns Boolean.

Update a form

updateForm edits any form property and is how you activate a form (isActive: true) once its fields are in place.

mutation ActivateForm {
  updateForm(
    input: {
      id: "form_123"
      isActive: true
      submitText: "Send"
      responseText: "Thanks — we'll be in touch."
      redirectURL: "https://example.com/thank-you"
    }
  ) {
    id
    isActive
  }
}

UpdateFormInput covers the visible form: title, description, footerText, showFooter, submitText, responseText, theme, primaryColor, hideBranding, imageURL, redirectURL, isActive, plus assigneeIds, tagIds, and todoListId (the list new records land in).

Manage fields with upsertFormField

UpdateFormInput also has a formFields array, but the supported way to add, edit, and order fields is one upsertFormField call per field — it’s explicit about create-vs-update and field mapping.

Copy a form

copyForm duplicates a form — and its fields — into a workspace.

mutation CopyForm {
  copyForm(input: { formId: "form_123", projectId: "project_456", title: "Contact us (copy)" }) {
    id
    title
  }
}

projectId is required and may be the same workspace or a different one. title is optional; omit it to keep the source title.

Delete a form

deleteForm(id: "form_123") deletes a form and returns Boolean.

mutation DeleteForm {
  deleteForm(id: "form_123")
}

Submit a form

submitForm is public — no authentication header, rate-limited. Each submission creates a record in the form’s target list. This is the call Blue’s hosted form page makes; use it directly only when you render your own form UI.

mutation SubmitForm {
  submitForm(
    input: {
      formId: "form_123"
      formToken: "form_123_token"
      formFieldInput: [
        { formFieldId: "field_1", value: "Ada Lovelace" }
        { formFieldId: "field_2", value: "ada@example.com" }
      ]
    }
  )
}

SubmitFormInput

FieldTypeRequiredDescription
formIdString!YesThe form being submitted.
formTokenString!YesThe form’s submission token.
formFieldInput[SubmitFormFieldInput!]!YesOne entry per answered field.

Each SubmitFormFieldInput is { formFieldId, customFieldId, value }, where value is JSON (a string, number, or structured value matching the field). The mutation returns Booleantrue once the record is created.

File uploads on public forms

To accept files on a public form, upload to the same presigned-PUT endpoint without a token by passing ?formId=<form-id> — the upload is gated by the form being active. See Anonymous form uploads.

Watch for changes

subscribeToForm streams create/update/delete events for the forms in a workspace.

subscription WatchForms {
  subscribeToForm(filter: { projectId: "project_123" }) {
    mutation
    node {
      id
      title
      isActive
    }
    updatedFields
  }
}

mutation is CREATED, UPDATED, or DELETED; node is the affected Form.

Permissions

OperationRequirement
form, forms, createForm, updateForm, copyForm, deleteForm, upsertFormFieldProject-level OWNER, ADMIN, or MEMBER on the workspace.
deleteFormFieldAny authenticated user.
submitFormPublic — no authentication, rate-limited.