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.

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.

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
}
}
}

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
}
}
}

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
}
}
}
}

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.

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

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
}
}
}