Kiran P K
← All writing
Jan 5, 2024·3 min read

How I Built a WhatsApp Campaign Internal Tool

Node.jsAWSArchitecture

Campaign management is a critical part of any organisation’s outreach strategy, and an effective scheduling and execution system can significantly improve its success. Previously we ran manual scripts against the Freshchat APIs — an API wrapper around the WhatsApp API — which was repetitive work for developers. So I automated the process and built an internal tool around it.

Architecture

When a user uploads a campaign, the scheduling API creates the campaign and schedules an event at the specified time. The event is associated with a campaign ID and time data, and the scheduler returns an event ID which is stored as a property of the campaign. That lets us modify the scheduled event later — but only for campaigns that haven’t happened yet. Scheduling is done using AWS EventBridge rules.

At the scheduled time a Lambda function is triggered to execute the campaign. It calls an internal API to initiate the run, keeping the whole process automated.

The run-campaign process parses a CSV file to generate parameters for a Freshchat API call. Freshchat returns a response object with a response ID; those IDs and their statuses are saved for future reference. When listing campaigns, any message with a status other than Sent, Delivered, or Failed triggers a Freshchat status check to update the value.

Campaigns can be edited with the provided schedule ID. If the schedule time changes during editing, a new event is generated with the corresponding campaign ID.

When the run-campaign API is called it checks the scheduled time. If there’s a difference of five minutes or more between the request and the scheduled time, the campaign is ignored — this prevents unintended or premature executions.

APIs and payloads

  • Create / edit campaignPOST /ss/admin/campaigns and PATCH /ss/admin/campaigns. Create payload: { csv, templateId, numberOfPlaceholders, dateTime, campaignName }; edit adds id.
  • List campaignsGET /ss/admin/campaigns, returning { id, campaignName, templateId, total, success, failed, createdAt, scheduledAt, status, csvUploaded }.
  • Test messagePOST /ss/admin/test-campaign with { phoneNumber, placeholders, templateId }.

Database tables

  • Campaignid, name, templateId, totalMessages, successMessages, failureMessages, inQueue, createdAt, scheduledAt, status, fileUrl.
  • Messageid, messageId, campaignId, status, phoneNumber.