Skip to main content

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, questions JSONB, theme JSONB
  • thank_you JSONB (nullable) — a NULL value is equivalent to {"mode":"default"}, meaning the standard Thai-language thank-you page is used
  • form_hash VARCHAR(50) unique — the public key used in the form link, and the target of the foreign key from submissions
  • status (enum FormBuilderStatus) — active / inactive / draft / delete
  • the collection window start_date / end_date, plus the responses_count counter
  • behaviour flags require_line_login (default true), one_time_submission, and convert_to_member
  • field_attribute_mappings JSONB — maps questions onto line_user attributes
  • profile_mapping JSONB (added via manual SQL) — prefills values from the customer database and holds an otp sub-key marking which fields require verification
  • line_oa_id FK (nullable)

form_submission (model FormSubmission)

  • form_hash FK → form_builder.form_hash (ON DELETE CASCADE) — note that it references the hash, not the id
  • answer JSONB and metadata JSONB
  • line_user_id, ip_address, user_agent, submitted_date
  • is_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.

  • typeshort_text, paragraph, email, phone, number, file_upload, date, date_of_birth, and so on
  • name and properties JSONB, 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_key and thaibulksms_secret_enc, the latter AES-encrypted with APP_ENCRYPT_SECRET — the plaintext secret is never stored
  • sms_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.

  • prisma/schema.prisma:916 — model FormBuilder
  • prisma/schema.prisma:948 — model FormSubmission
  • prisma/schema.prisma:904 — model FormBuilderRule
  • prisma/schema.prisma:802 — model OtpConfig
  • prisma/migrations/20260725090000_add_form_builder_thank_you/migration.sql
  • prisma/migrations/20260726100000_add_otp_config/migration.sql
  • manual-sql/6.customer_database.sql — adds the form_builder.profile_mapping column
  • manual-sql/2.notify_attribute_change.sql and manual-sql/5.sql — the form_submitted trigger
  • seed-data/16.form_builder_rule.sql — seeds validation rules for every field type
  • schema-dumps/2026-07-24/schema.sql:1749, :1832, :1797

Connections to Other Services

  • client-api-go — loads the form by form_hash, validates against form_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_submitted channel to write attributes, add users to audiences, and fire workflows

Related domains: LINE Friends, Attribute Master, Customer Database, Rich Menu