Skip to content

Forms & Profiles

The forms system drives any structured input the platform needs to render: account creation, account settings, profiles, surveys, and product reviews. A form is a server-defined list of fields with validators and render conditions, which the client renders and then posts back as a FormSubmissionInput.

Because forms are configured server-side, the same client code can render any form by identifier — you don’t need bespoke UI per form. Availability is gated by FORM_INFO; additional form types are unlocked by PROFILES, SURVEYS and REVIEWS.

Fetching a form

Every form has an identifier and a list of fields. Each field carries its type, the validators that apply, a required flag, and any renderConditions that hide it until other answers are given.

query GetForm {
form(input: { identifier: "subscription-cancellation" }) {
identifier
type
displayStyle
isMultiSubmission
canSubmit
fields {
name
label
translation {
value
}
type
required
confirmable
disabled
defaultValue
validators {
name
argument
}
answerOptions {
optionKey
translation {
value
}
}
renderConditions {
referenceFieldName
relationship
value
}
}
}
}

displayStyle tells the client whether to render every field on a single page or step through one question at a time.

Listing forms

forms returns multiple forms by type. profiles is the same query restricted to profile forms; this is what powers the profile-list view in the account section.

query AccountProfiles {
profiles(input: { type: MAIN_PROFILE }) {
identifier
type
canSubmit
isMultiSubmission
submissions {
id
complete
submissionTitle
submissionSubtitle
}
}
}

Convenience queries for common fields

For sign-up and login flows the API exposes the canonical email, password and referral-code field definitions directly, so the client doesn’t have to fetch a whole form to render them. The matching accountCreationForm and accountSettingsForm queries return the full forms.

query SignupFields {
accountCreationForm {
identifier
fields {
name
type
required
validators {
name
argument
}
}
}
emailField {
name
validators {
name
argument
}
}
passwordField {
name
validators {
name
argument
}
}
referralCodeField {
name
validators {
name
argument
}
}
}

Reading a submission

Existing submissions are reachable from the form. Use this to pre-populate a form for editing.

query FormSubmission {
form(input: { identifier: "main-profile" }) {
submission(submissionId: "sub_123") {
id
complete
editable
answers {
question {
name
type
}
values
locked
}
}
}
}

Submitting a profile

Pass the form identifier and an answers array keyed by questionName. For new submissions leave submissionId null; for edits pass the existing id.

mutation SubmitProfile {
submitProfile(
input: {
identifier: "main-profile"
answers: [
{ questionName: "fitness_goal", values: ["LOSE_WEIGHT"] }
{ questionName: "preferred_flavours", values: ["CHOCOLATE", "VANILLA"] }
]
}
) {
status
fieldErrors {
fieldName
validators
requiredButNotProvided
invalidOption
}
outcome {
outcomeType
outcomeValue
}
}
}

The status enum covers the full set of outcomes (SUBMITTED, SUBMITTED_INCOMPLETE, EDITED_NOW_COMPLETE, LOGIN_REQUIRED, ALREADY_SUBMITTED, ERROR_SUBMITTING, etc.). fieldErrors is populated when validation fails so the client can highlight the offending fields. For profiles that produce a recommendation, outcome carries the URL to send the customer to next.

Deleting a profile submission

mutation DeleteProfileSubmission {
deleteProfileSubmission(submissionId: "sub_123")
}

Submitting a survey

Surveys use the same input shape but go through their own mutation and require authentication. The otherText field on FormAnswerInput is for answers where the survey offers an “other” free-text option alongside the predefined choices.

mutation SubmitSurvey {
submitSurvey(
input: {
identifier: "post-purchase-survey-42"
answers: [
{ questionName: "satisfaction", values: ["4"] }
{
questionName: "improvement_area"
values: ["OTHER"]
otherText: "Faster delivery"
}
]
}
) {
status
fieldErrors {
fieldName
validators
}
}
}