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