Loading…

Star4.1kBook a meeting

Forms & Surveys

Capture leads with embeddable forms and run channel-scoped surveys inside Messenger conversations, including one-vote-per-user enforcement and survey-driven ticket automation.

Backend modules: backend/plugins/frontline_api/src/modules/{form,survey}. Frontend: frontend/plugins/frontline_ui/src/modules/{forms,survey} at /frontline/forms and /frontline/surveys.

Forms

Definitions and fields

A Form carries name, title, code, type, description, buttonText, numberOfPages, visibility, leadData, languageCode, departmentIds, tagIds, channelId, and integrationId. Its fields are FrontlineField records managed through the frontlineFields* operations (each with validation, regexValidation, validator, options, order, isRequired, logics, and subFields). Field validation is defined by a FieldValidator (type of PRESET/CUSTOM/NONE, presetKey, customRegex, errorMessage) backed by form/validationRegistry.ts. Preset keys: EMAIL, NUMBER, DATE_TIME, DATE, PHONE, MN_VEHICLE_REGISTRATION, POSTAL_CODE, ALPHANUMERIC. Custom regex support is guarded by length/time limits to avoid pathological patterns.

Submissions

A Submission groups one customer's answers (contentTypeId, formId, conversationId, channelId, customerId, customFieldsData, submissions); each FormSubmission row stores formFieldId, the submitted text/value, formFieldText/formFieldType, and submittedAt. Submissions attach to the customer record and can be logged onto related entities through relation.onRelationAdded. Submission export is registered as content type frontline:formSubmission.formSubmission (permission formSubmissionsExportManage).

Form queries and mutations

Schema: modules/form/graphql/schema/form.ts.

Operation Kind Purpose
forms / formsMain / formsTotalCount / formDetail(_id) query Form list/detail with type, channelId, tagId, status, searchValue filters
formSubmissions(filter…) / formSubmissionDetail(_id) / formsGetContentTypes query Submission reads
cpForms / cpFormDetail query Client portal reads
formsAdd / formsEdit / formsRemove / formsToggleStatus / formsDuplicate mutation Form management
frontlineFields / frontlineFieldByCode / frontlineFieldsGetTypes / frontlineFieldsCombinedByContentType query Field reads
frontlineFieldsAdd / frontlineFieldsEdit / frontlineFieldsRemove / frontlineFieldsBulkAction / frontlineFieldsUpdateOrder / frontlineFieldsUpdateVisible / frontlineFieldsUpdateSystemFields mutation Field management
formSubmissionsSave / formSubmissionsRemove / formSubmissionsEdit mutation Submission writes
widgetsLeadConnect / widgetsSaveLead mutation Widget lead flow: connect a lead integration, then save a submission
cpWidgetsSaveLead mutation Client portal lead submission
mutation {
  formsAdd(
    name: "Contact us"
    title: "Contact us"
    type: "lead"
    channelId: "chan_1"
  ) {
    _id
    code
  }
}

tRPC

form.submissionsByConversation returns a conversation's form submissions.

Surveys

Survey operations are named survey* (older code called them polls: poll*, withPoll, Ticket.sourcePoll).

Definitions

A Survey carries title, question, channelId, brandId, code, options, ordered steps (each with its own question and options), allowMultiselect, durationHours, status (active or archived), sentCount, and createdUserId. Options can arm ticket automation (ticketCreationEnabled, ticketCreationThreshold, ticketPipelineId, ticketStatusId, ticketCreated, ticketId).

Sending into a conversation

surveySendToConversation(_id, conversationId) is a mutation that:

  • Requires an active survey.
  • Only targets Messenger conversations.
  • Checks channel and brand compatibility between the survey and conversation.
  • Writes a survey snapshot onto the message's extraData.survey and marks the conversation hasSurvey: true, which feeds the withSurvey inbox filter.

Warning

Survey conversations are excluded from integrationType-scoped inbox lists unless you pass withSurvey: "true".

Client portal submission

cpSurveySubmit(surveyCode, optionIds) requires an authenticated client portal user. It finds or creates the customer, creates a Messenger conversation with a survey snapshot, validates option ids and allowMultiselect rules, and returns alreadyVoted on repeat votes. A partial unique index on SurveyVotes ({ surveyId: 1, cpUserId: 1 }, filtered to documents where cpUserId exists) enforces one vote per client-portal user per survey (definitions). After a successful vote, runSurveyTicketAutomation evaluates armed options; see Tickets.

Survey queries and mutations

Operation Kind Purpose
surveyList / surveyDetail(_id) / surveyTotalCount query Admin list/detail with searchValue, status, channelId filters
cpSurveys / cpSurveyDetail(channelId, surveyCode) / cpSurveyVotes(conversationId) query Client portal list, detail, and existing vote selections
surveyAdd / surveyEdit / surveyRemove / surveyToggleStatus mutation Survey management
surveySendToConversation(_id, conversationId) mutation Inject the survey into a Messenger conversation
cpSurveySubmit(surveyCode, optionIds) mutation Client portal vote

There is no cpSurveyVote operation; the read is cpSurveyVotes and the write is cpSurveySubmit.

Operate on one system.

Talk to our team about consolidating your stack onto erxes. 30-minute walkthrough, custom to your org.