- Broadcast and Campaign Scheduling: Utilize
makeandcampaignedge functions to schedule and initiate broadcasts and campaigns based on predefined times. - Audience Segmentation: Leverage the
makeedge function to retrieve and process audience segments fromaudience_segmentandbroadcast_segmenttables, ensuring targeted message delivery. - Two-phase Message Sending: Implement the
send-messagesedge function to send initial and follow-up messages via the Missive API, managing both broadcast and campaign queues. - Twilio Integration: Use the
reconcile-twilio-statusedge function to update message statuses from Twilio, ensuring accurate tracking of message delivery. - Message Status Tracking: Monitor message statuses through the
send-messagesandreconcile-twilio-statusedge functions, handling success and failure scenarios. - Unsubscribe Handling: Manage unsubscription requests with the
admin-actionedge function, triggered by Missive rules, and update theunsubscribefield accordingly. - Failed Message Handling: Use
handle-failed-deliveriesto process conversations with multiple failed messages, applying theundeliverablelabel and updating theunsubscribefield. - Message Archival: Implement
archive-double-failuresto archive conversations with consecutive failed messages, moving them out of the team inbox. - User Interaction Management: The
user-actionedge function listens to Missive webhooks to manage user replies, removing follow-up messages when users respond. - Broadcast Configuration: Utilize
broadcast-settingandbroadcast-sidebaredge functions to set up and manage broadcast schedules and content via the Missive broadcast sidebar. - Personalized Campaign Messages: Send individualized messages to recipients where each person receives a unique custom message, using database triggers to automatically process and queue messages.
- Language: Deno
- Hosting: Supabase Edge Functions
- Database: PostgreSQL (Supabase)
- Job Scheduling: pg_cron (Supabase)
Follow these steps to run the project locally:
-
Set up environment files - see Environment Files Documentation
-
Run:
deno task dev- Run
deno task test:setupto prepare the testing environment. - Run
deno task testto execute all tests. - Run
deno task test:dbto run database-specific tests.
For detailed information about the testing architecture and best practices, see the Testing Documentation.
- Run the application:
deno task dev - Run tests:
deno task test - Setup test environment:
deno task test:setup - Run database tests:
deno task test:db - Run linter:
deno task lint - Format code:
deno task fmt - Stop the local Supabase environment:
supabase stop --no-backup- Use this to remove local Supabase containers and ensure a fresh start, which is required for new migrations to
be applied correctly by
deno task dev.
- Use this to remove local Supabase containers and ensure a fresh start, which is required for new migrations to
be applied correctly by
- Migration files reside in
supabase/migrations. - Development: Upon running
deno task devin a fresh environment (aftersupabase stop --no-backup), all migrations are automatically applied for local development. - Production: New migrations must be applied manually by executing the SQL from the relevant file(s) directly against the production database (e.g., via the Supabase SQL Editor). Note: Ensure you apply them in the correct order.
- Refer to the Supabase documentation for concepts: Supabase Database Migrations Guide
<!-- TODO: Integrate automated database migration deployment to production when a CI/CD pipeline is implemented. -->
Currently, there is no Continuous Deployment pipeline configured for this project. Therefore, developers must manually deploy any changes to the Supabase Edge Functions.
Please follow the official Supabase documentation for detailed deployment instructions: Supabase Functions Deployment Guide
The following edge functions are utilized within the broadcast and campaign systems:
- make: Runs a broadcast that is expected to run at the calling time. It retrieves segments linked to a broadcast
and enqueues phone numbers to
broadcast_first_messages, called by thecheck-and-trigger-broadcast-every-minutecron job. - send-messages: Pulls messages from queues and sends SMS via the Missive API, handling success and failure
scenarios, called by the
send-messagescron job. - reconcile-twilio-status: Updates the status of broadcast and campaign messages using the Twilio API, called by
daily-reconciliation. - handle-failed-deliveries: Processes conversations with multiple failed messages, applies the
undeliverablelabel, and updates theunsubscribefield, called bydaily-failed-deliveries-setup. - archive-double-failures: Applies the
archivelabel to conversations with two consecutive failed messages, called bydaily-archive-double-failures-setup. - user-action: Listens to the Missive webhook and helps the broadcast and campaign processes when there's a reply.
It removes follow-up messages from
broadcast_second_messageswhen users reply to the first message. - admin-action: Called by Missive rule when an admin comments "unsubscribe" or "stop". It will mark
unsubscribetoTruefor that phone number and send a post to Missive to notify the team. - broadcast-setting: Called by the broadcast sidebar on Missive, letting the admin team set up the broadcast schedule. Note: time is in Detroit.
- broadcast-sidebar: Used by the broadcast sidebar on Missive to get a list of broadcasts and let admins update upcoming broadcast message content.
- campaign: API to get/create/update/delete campaign.
- broadcast_first_messages: Queue for initial messages of both broadcast and campaign campaigns.
- broadcast_second_messages: Queue for follow-up messages of both broadcast and campaign campaigns.
- outlier_on_broadcast_first_messages_insert: Trigger that activates when a new message is enqueued in
broadcast_first_messages. It sets up a cron job to run every 2 seconds, which calls thesend-messagesedge function. - outlier_on_broadcast_first_messages_delete: Trigger that runs when a message is removed from
broadcast_first_messages. - outlier_on_broadcast_second_messages_insert: Trigger that activates when a new message is enqueued in
broadcast_second_messages. It sets up a cron job to run every 2 seconds, which calls thesend-messagesedge function. - outlier_on_broadcast_second_messages_delete: Trigger that runs when a message is removed from
broadcast_second_messages. - trigger_process_campaign_personalized_recipient_batch: Statement-level trigger that activates when new records
are inserted into the
campaign_personalized_recipientstable, automatically creating a single campaign for all records and queuing personalized messages in a batch.
- check_and_trigger_broadcast: Runs every minute to initiate broadcasts based on schedule.
- check_and_run_campaigns: Runs every minute to initiate campaigns based on schedule.
- daily-reconciliation: Runs daily to update message statuses from Twilio.
- daily-failed-deliveries-setup: Runs daily to handle failed message deliveries.
- daily-archive-double-failures-setup: Runs daily to archive conversations with multiple failed messages.
- queue_broadcast_messages: Function to queue messages for broadcasting.
- check_and_run_campaigns: PostgreSQL function that checks the
run_atfield of thecampaigntable and enqueues messages tobroadcast_first_messages. - process_campaign_personalized_recipient_batch: Function that processes batches of personalized campaign recipients and queues messages for sending.
- check_and_trigger_broadcast checks the nearest upcoming broadcast record where
editable = False.
- If
run_atis set, it uses this field to determine the run time. - Otherwise, it calculates the run time from
broadcast_settings(in Detroit time zone).
- If the current time matches the calculated run time (at the minute level),
check_and_trigger_broadcastcalls themakeedge function.
For detailed information about campaigns, including label features and API usage, see Campaigns Documentation.
For details on how personalized campaigns work, see Personalized Campaigns Documentation.
- make edge function (for broadcasts) retrieves segments linked to the broadcast from
audience_segmentandbroadcast_segmenttables.
- Segments are processed to obtain phone numbers, excluding those where
excluded = True,unsubscribe = True, oradded_via_file_upload = True. - The retrieved phone numbers are enqueued to
broadcast_first_messages.
-
outlier_on_broadcast_first_messages_insert triggers immediately upon enqueueing a new message, setting up a cron job that runs every 2 seconds to call
send-messages. -
send-messages edge function:
- Pulls one message from either
broadcast_first_messagesorbroadcast_second_messages. - Calls the Missive API to send the SMS.
- If successful and from
broadcast_first_messages, enqueues a follow-up message tobroadcast_second_messages. - If successful, removes the message from the queue.
- If failed, leaves the message in the queue with a 3-minute sleep.
- If failed 3 times (excluding 429 errors), deletes the message from the queue.
- If successful and from
- outlier_on_broadcast_second_messages_delete runs once the
broadcast_second_messagesqueue is depleted, cleaning up the cron job.
-
daily-reconciliation runs to call the
reconcile-twilio-statusedge function, which updates the Twilio status for every broadcast and campaign sent message. This job is crucial as some Twilio SMS status updates may be delayed by 1-2 days. -
daily-failed-deliveries-setup calls
handle-failed-deliveries:
- Identifies conversations with 3 latest messages that are all failed and have never received a reply.
- Applies the
undeliverablelabel to these conversations, moving them out of the team inbox. - Updates the
unsubscribefield toTruefor the corresponding phone number in theauthortable.
- daily-archive-double-failures-setup calls
archive-double-failures:
- Applies the
archivelabel to conversations with 2 latest messages that are failed. - This label moves the conversation out of the team inbox.
- User Replies: If users reply after receiving the first message, the
user-actionedge function removes the follow-up message frombroadcast_second_messagesfor that user. - Admin Actions: The
admin-actionedge function is triggered by Missive rules to handle unsubscription requests from admins, updating theunsubscribefield and notifying the team. - Personalized Campaigns: The personalized campaign feature allows for one-time campaigns where each recipient
receives a custom message. This is implemented through database triggers that automatically process and queue messages
when records are inserted into the
campaign_personalized_recipientstable. See Personalized Campaigns Documentation for usage instructions.
- Ensure that the system time is synchronized with the server hosting the application to prevent timing discrepancies.
- Monitor the queues and scheduled jobs to ensure smooth operation and timely processing of messages.
- Regularly review and update the segment queries to ensure they exclude the appropriate phone numbers (excluded, unsubscribe, added_via_file_upload). Note: The added_via_file_upload field should always be excluded in broadcast segment queries because these authors were recipients added through campaign file uploads and were not intended to be part of broadcasts. Including them in broadcasts could lead to unintended message delivery and potential compliance issues.
- Missive sidebars
- Lookup backend