Forms (Form Builder)
Overview
The form builder lets administrators create data-collection forms — lead generation, member registration, and similar — and expose them through LIFF or a public link. Answers can be written back as attributes on the LINE friend and can promote a user from guest to member.
One design point worth knowing: the entire question structure is stored as a single JSONB
blob, with no separate tables for questions or choices. Validation rules live in
form_builder_rule, which acts as the master table of rules per field type.
Core Data Structure
form_builder (model FormBuilder)
title,description,questionsJSONB,themeJSONBthank_youJSONB (nullable) — aNULLvalue is equivalent to{"mode":"default"}, meaning the standard Thai-language thank-you page is usedform_hashVARCHAR(50) unique — the public key used in the form link, and the target of the foreign key from submissionsstatus(enumFormBuilderStatus) —active/inactive/draft/delete- the collection window
start_date/end_date, plus theresponses_countcounter - behaviour flags
require_line_login(default true),one_time_submission, andconvert_to_member field_attribute_mappingsJSONB — maps questions ontoline_userattributesprofile_mappingJSONB (added via manual SQL) — prefills values from the customer database and holds anotpsub-key marking which fields require verificationline_oa_idFK (nullable)
form_submission (model FormSubmission)
form_hashFK →form_builder.form_hash(ON DELETE CASCADE) — note that it references the hash, not the idanswerJSONB andmetadataJSONBline_user_id,ip_address,user_agent,submitted_dateis_submitted— the row is created as soon as the user starts filling in the form, then flipped to true when they actually submit
form_builder_rule (model FormBuilderRule)
The master table of validation rules, one per field type.
type—short_text,paragraph,email,phone,number,file_upload,date,date_of_birth, and so onnameandpropertiesJSONB, holding the regex, min/max values, and error messages in both Thai and English
otp_config (model OtpConfig, UUID primary key)
One row per OA (line_oa_id is unique).
thaibulksms_keyandthaibulksms_secret_enc, the latter AES-encrypted withAPP_ENCRYPT_SECRET— the plaintext secret is never storedsms_enabled,email_enabled,email_subject,email_body
Trigger
form_submission_notify (AFTER INSERT OR UPDATE) calls notify_form_submitted(), emitting
pg_notify('form_submitted', ...) when is_submitted flips to true. The function joins
form_builder to line_oa to include lineOaId and organizationId in the payload.
Related Files
prisma/schema.prisma:916— modelFormBuilderprisma/schema.prisma:948— modelFormSubmissionprisma/schema.prisma:904— modelFormBuilderRuleprisma/schema.prisma:802— modelOtpConfigprisma/migrations/20260725090000_add_form_builder_thank_you/migration.sqlprisma/migrations/20260726100000_add_otp_config/migration.sqlmanual-sql/6.customer_database.sql— adds theform_builder.profile_mappingcolumnmanual-sql/2.notify_attribute_change.sqlandmanual-sql/5.sql— theform_submittedtriggerseed-data/16.form_builder_rule.sql— seeds validation rules for every field typeschema-dumps/2026-07-24/schema.sql:1749,:1832,:1797
Connections to Other Services
- client-api-go — loads the form by
form_hash, validates againstform_builder_rule, sends OTPs, stores submissions, and switches the rich menu after a successful send - cms-api-go — form creation and editing, response browsing, OTP and profile-mapping setup
- worker-go — listens on the
form_submittedchannel to write attributes, add users to audiences, and fire workflows
Related domains: LINE Friends, Attribute Master, Customer Database, Rich Menu