Message Library (Rich Message / Template / Quick Reply)
Overview
This domain stores the message content that goes out over LINE, kept separate from the act of sending, which belongs to the campaign side. That separation is what lets a single piece of content be reused by both campaigns and auto-responses.
Three tables make up the library:
rich_message— the primary message, covering text, images, tappable images and flex messagestemplate_message— pre-built flex templates that users fill content intoquick_reply— sets of quick-reply buttons that can be attached to a message
Core Data Structure
rich_message (model RichMessage)
Holds title and content JSONB, the latter being the payload sent straight to the LINE Messaging API. The message kind is given by type (enum RichMessageType: text, image, tappable_image, flex_message), and the row is bound to a channel through the line_oa_id FK.
The quick_reply_id column is a UUID referencing quick_reply.id, but deliberately has no real foreign key. The application, not a database constraint, is responsible for preventing the deletion of a quick reply that is still in use.
It has 1 : N relationships with campaign and auto_response.
template_message (model TemplateMessage)
Keyed by UUID, holding title and template_message_showcase_id (the template's number in the showcase).
Content is stored twice over: line_template_message JSONB is the payload destined for LINE, while form_template_message JSONB keeps the values the user entered in the CMS form so the editor can be re-rendered later. An index covers (line_oa_id, template_message_showcase_id, status).
An important observation: a template message has no direct send path to LINE. Its flex payload only goes out embedded inside a rich message — which is precisely why quick replies attach at the rich_message level.
quick_reply (model QuickReply)
Keyed by UUID, holding name, items JSONB (the button list, defaulting to []) and status.
One difference to watch for: this table's line_oa_id and organization_id columns are BIGINT, unlike the older tables which use INT. Indexes cover (line_oa_id, status) and (created_date).
Related Files
prisma/schema.prisma:356— modelRichMessageprisma/schema.prisma:761— modelTemplateMessageprisma/schema.prisma:784— modelQuickReplyprisma/migrations/20260725100000_add_quick_reply/migration.sql— createsquick_replyand addsrich_message.quick_reply_id, with a comment explaining the missing FKseed-data/11.rich_message.sqlschema-dumps/2026-07-24/schema.sql:2667and:2942
Connections to Other Services
- cms-api-go — CRUD across all three tables and flex validation against the LINE API; the number of templates is capped by
plan_limits.maxTemplates. - worker-go — reads
rich_message.contentwhen sending a campaign, though the campaign itself also snapshots the content intocampaign.rich_message_contentat send time. - webhook-go — replies with the
rich_messagebound to an auto-response rule. - Connects to Campaign, Auto Response and Tracking, where buttons inside flex payloads are rewritten as tracking links.