Skip to content

Digital Marketing

Digital Marketing sends one message to a list of people, and then tells you what happened to it. You upload the list as a segment, point a campaign at it and at a message template held in the Unified Contact Center, and execute it, now or for a scheduled moment. It posts nothing to the books.

This is the reference for whoever runs campaigns and whoever is asked to explain their figures.

Your job, and the screen for it

The jobScreenRead
Load the people you will message, in the column order the importer expectsSegment › Segment Listing › uploadA segment is a file you uploaded
Write the messageTemplate Message (the contact centre’s templates)Two different things are called a message template
Send it now, or queue it for a timeCampaign › edit › Run Campaign › Run now or ScheduleWhat happens when you execute a campaign; Scheduling queues now and sends later
Read how it wentCampaign › edit › Refresh Summary; Report › Campaign ReportThe six counters
Ask whether it sold anythingReport › Sales Tracking ReportThe sales-tracking report

Overview

BigLedger fans a campaign out to one queued message per phone number or e-mail address, hands each to WhatsApp Business or to e-mail broadcast, and records an execution row for every one handed over. It is built to fold WhatsApp’s status callbacks back into a per-campaign tally and an optional sales-tracking report that looks for purchases by the people you messaged; read the callout on the counters before you rely on either.

What it is not is a marketing-automation suite. There is no rule you can write that finds your customers for you, no multi-step journey, no A/B split, no open or bounce tracking on e-mail. The sections below say exactly where each of those stops, because the database carries tables for several of them and the API carries routes for more.

Where it fits

Digital Marketing sits in the CRM & Digital module and depends on the contact centre for everything that actually touches a recipient.

It needsFromWhy
The message template a campaign sendsUnified Contact CenterA campaign sends a contact-centre template, using its lines in the campaign’s language. An inactive template queues nothing; a template with no lines in that language queues messages with no content
The endpoint the message leaves fromUnified Contact CenterThe campaign’s endpoint supplies the sender identity. Which channel each message goes by is decided by the segment line’s CHANNEL_TYPE, not by the endpoint
Delivery, read and reply statusWhatsApp Business callbacks, through the contact centre’s WhatsApp message handlerNothing else in the product writes these. E-mail broadcast never reports back
Customer and entity recordsContact and entity master dataUsed only by the sales-tracking report, to match a recipient back to a customer. Campaigns themselves never look at your customer records
Uploaded images and attachmentsThe platform file serviceHanded over as signed links that expire after 48 hours. See Media links expire after two days

Nothing downstream depends on Digital Marketing. It posts no journal, moves no stock and changes no customer record. A campaign is a read of your data and a send; deleting one changes nothing else in BigLedger, and does not stop messages it already queued (Cautions).

Screens and menus

The applet’s menu has six entries:

  • Project — a folder that campaigns, segments, channels and templates can be grouped under.
  • Campaign — the list of campaigns, and the create/edit form with Run Campaign (execute now, or Schedule) and Refresh Summary.
  • Schedule — dated, timed entries a campaign can be linked to.
  • Segment — Segment Listing and Unsubscribed Listing.
  • Report — Project Report, Campaign Report, Sales Tracking Report.
  • Template Message — the contact centre’s message templates, the ones a campaign sends.

The settings menu lists Field Settings and Default Selection; the applet also serves a webhook page and the five permission listings. Personalization holds a personal default selection.

What the API has and the screen does not

The backend carries 232 REST routes for this applet, and the screens reach a minority of them. Treatments, channels, criteria, dimensions, labels, segment resources, segment conditions and recurring campaigns have full create, read, update and delete routes and no menu entry in the applet. If you have read about any of those in a database dictionary or an API listing and gone looking for them in the applet, that is why you could not find them. Two of them, treatments and segment conditions, have no engine behind them either; What this applet will not do says which.

Configuration

Before you can use it, three things have to exist outside this applet:

  • A contact-centre endpoint for WhatsApp Business or e-mail broadcast, configured and working. See Unified Contact Center.
  • A message template in the contact centre, approved on the channel it will be sent through, with lines in the language the campaign uses.
  • A clock on the campaign-processing job, if you use scheduled sends. Without one, scheduled messages wait in the queue until some other campaign is executed. Clocks are added in the Scheduler applet where its picker offers the code; where it does not, ask BigLedger.

Applet settings

The settings hold Field Settings, Default Selection, a webhook page and the five permission listings, plus a personal default selection under Personalization. No setting was found that changes what a campaign sends or when. The 48-hour media links, the 28-day sales-tracking window, the priority and the retry counts are constants in the backend, and the 1,000-message send limit is the default of a parameter on the execute call, not a setting. The settings mechanism itself is described once at Settings and permissions.

Who can change what

Permissions are declared as 375 codes, all prefixed API_TNT_DM_DGMKT_, following one pattern: seven verbs — owner, admin, member, create, update, delete, read — against each of about 53 objects. Two consequences for whoever assigns them:

  • The granularity is far finer than the screen. Most of the 375 guard routes no menu entry reaches, so granting or withholding them changes nothing a user sees on a screen.
  • Executing a campaign, refreshing its summary and generating its sales-tracking report all need the campaign create permission. There is no separate “may send” right, so anyone who can build a campaign can send one.

Fields

The upload file is the one form whose fields decide what a campaign does. Its four columns, in this order:

ColumnWhat it holdsWhat goes wrong
CHANNEL_TYPEThe channel this line is sent on: WHATSAPP_BUSINESS or EMAIL_BROADCAST. It, not the campaign’s endpoint, decides the channelAn unrecognised value rejects the whole upload; a channel other than the two that have a gateway fails at the send
ENDPOINT_IDThe phone number or e-mail addressEmpty rejects the whole upload; repeated is dropped silently, compared as typed
FIRST_NAMEThe only per-recipient text a template can use, through the customer-name placeholder—
LAST_NAMEPassed to the gateway with a WhatsApp message; never substituted by BigLedger—

The campaign form names the project, the channel, the endpoint, the template message and its language; the schedules are linked on its Schedules tab.

Lifecycle and effects

A campaign posts nothing and has no statuses to move through; its lifecycle is the send. The three paths below are execute now, schedule, and the recurring job.

What happens when you execute a campaign

None of this is visible from the campaign screen, and all of it decides what your recipients get.

Run Campaign › Run now calls the campaign’s execute endpoint, and five things follow in order.

  1. BigLedger expands the campaign into one queued message per endpoint. One database statement walks the campaign’s active segments and their active lines and writes a queued message for each. Two filters apply that the screen does not show: lines are de-duplicated on the endpoint text (the number or address as stored), so a number that appears in three of the linked segments is messaged once, but a person listed under a phone number and an e-mail address gets two messages; and only lines marked valid are taken, which is where most empty sends come from (see below).
  2. The message text is frozen into the queued message, now. The recipient’s name, the template, the language, the resolved placeholder text and the header and body are built into the queued message at this moment. Editing the template afterwards changes nothing about a campaign already executed. Media are stored as file references and signed only when each message is processed.
  3. BigLedger writes the campaign’s unique contact list, one row per recipient, status PENDING, unique on campaign + channel + endpoint. This is the roster the sales-tracking report later works from, and its creation time is where that report’s window starts.
  4. The send job starts, but only if step 3 added at least one new row. It picks the queued messages that are due, not expired and have retries left, by priority then start time, and fires one per-message job for each. It takes up to 1,000 by default, from the whole queue rather than from your campaign.
  5. Each message is signed, built and posted. The per-message job replaces the media reference with a link valid for 48 hours, builds the channel’s request, and posts it to an AWS API Gateway campaign-processing queue. Only then does it write one execution row for the campaign.

Read step 5 twice if you are trying to reconcile a number. An execution row means BigLedger handed this one over, never that it arrived. A message whose channel is neither WhatsApp Business nor e-mail broadcast fails before the hand-over: the job is retried until its retries run out, an error is logged for the scheduler, and no execution row appears.

Scheduling queues now and sends later

Run Campaign › Schedule does the same expansion, but takes each queued message’s start time from the schedules linked to the campaign, so every message is queued at once with a future start time — once per linked schedule, so two linked schedules send each person two messages. It then runs the send job once, immediately, and that run skips everything not yet due.

Nothing comes back at the scheduled time on its own. The messages go out the next time the send job runs after their start time: on a clock that runs Digital Marketing’s campaign-processing job on the tenant, or when somebody executes another campaign. That clock needs its Json filled in (the request address, the tenant code and a limit above zero), which the job’s own defaults leave empty, so ask BigLedger to set it up rather than adding it from the picker alone. A scheduled campaign on a tenant with no such clock waits in the queue until one of those happens. Linking a schedule to a campaign and waiting, without pressing Schedule, queues nothing at all.

Recurring campaigns

The recurring-campaign job reads the recurring-campaign table and, for every row that has not yet produced one, clones it into a real campaign, stamps the new campaign’s GUID onto the recurring row and marks it POSTED. A row that has generated its campaign is never picked up again, and the job applies no date filter.

The job only creates the campaign; nothing sends it. No execute or schedule call follows, and the schedule is copied onto a header field the scheduled-send path does not read (that path reads campaign-schedule links, and none is created). So a generated campaign sits unsent until someone opens it and runs it. The recurring-campaign queue table beside it is declared and not used; the job’s own comment says the queue will come “later when it grows big”.

Where each number comes from

This is the section to read when a figure looks wrong.

A segment is a file you uploaded, frozen at the moment you uploaded it

There is no query behind a segment. A segment’s lines come from a CSV you upload, and the importer reads exactly four columns, in this order: CHANNEL_TYPE, ENDPOINT_ID, FIRST_NAME, LAST_NAME. Your file’s first row is skipped as a header whatever it says, so a file with its columns in another order imports into the wrong fields. Nothing recomputes the list afterwards. If a customer changes their number, unsubscribes or buys nothing for three years, the segment does not know. Its age is the age of the upload.

What the upload does that no message explains:

At uploadWhat happensWhy it matters
A row with an empty ENDPOINT_ID, an unrecognised CHANNEL_TYPE, or fewer than four columnsThe whole upload is rejected; nothing is importedClean the file first; one bad row costs the whole list
A repeated ENDPOINT_IDDropped, first occurrence kept. The comparison is on the text as typed, so +60 12-345 6789 and +60123456789 both stayThe row counts differ with no explanation, and two spellings of one number reach the send, where they collapse to one message
CHANNEL_TYPE is WHATSAPP_BUSINESSThe number is checked and marked valid or invalid, with the reason stored on the lineThis is the only channel the importer validates, and the only one it ever marks valid
An e-mail segment built by CSV upload sends to nobody. Every line starts life not valid, and the upload marks lines valid only on the WhatsApp branch. The campaign’s expansion takes valid lines only. So every line of an e-mail-broadcast segment is filtered out, execute returns 0, no queue rows are written, no error is raised, and the campaign looks executed. The e-mail path is otherwise built — the per-message job has a complete e-mail-broadcast branch — so this is a gap between two working halves. Until it is fixed, an e-mail campaign needs its segment lines created through the segment-line API with the valid flag set, not through the upload screen.

Personalisation reaches exactly one field

When the expansion fills a template placeholder, the only per-recipient source it recognises is the customer-name placeholder, which it fills from the segment line’s first name. Everything else falls back to the placeholder’s fixed value, then its default, the same for every recipient. BigLedger never substitutes the last name; it passes it to the gateway with a WhatsApp message, and BigLedger’s code does not show what the gateway does with it. An e-mail’s body is the template text with no placeholder filled at all.

So a WhatsApp template can say “Hi {{name}}”. It cannot say “your last order was …”, because nothing in the send path reads an order.

The six counters on a campaign are recomputed when someone asks, and then written down

processed, not sent, sent, delivered, read and replied are stored on the campaign header, and they are not a running tally. Pressing Refresh Summary counts that campaign’s execution rows six ways, writes the six answers onto the header, and shows them. The button needs the campaign-create permission.

So the numbers on a campaign header are as old as the last time somebody pressed Refresh Summary. If a campaign was executed on Monday and nobody refreshed it until Friday, Monday’s numbers sat on the record all week. The numbers are never wrong; they are answers to a question asked at a moment the screen does not show.

Delivered, read and replied are WhatsApp-only, and may stay at zero on WhatsApp too. The only thing that sets them is the contact centre’s WhatsApp message handler, reacting to WhatsApp’s status callbacks, and a sent callback is meant to mark a message delivered.

But the handler looks the execution row up by the campaign’s own id rather than by the row’s id, so, unless the gateway sends the two the other way round, a callback updates no row. We have found no execution row on any tenant marked delivered or failed. Until that is settled, treat delivered, read and replied as unavailable on both channels, and the sales-tracking report (which starts from delivered contacts) as empty. An e-mail-broadcast campaign leaves all three at zero by design.

The sales-tracking report is a stored snapshot over a 28-day window from the run

The sales-tracking report asks: did the people we messaged buy anything afterwards? It is generated on demand, runs through its own queue and job, and inserts rows into a report table. It is a snapshot, not a view.

How it arrives at its answer, in order:

  1. It starts from the campaign’s unique contact list, restricted to contacts whose status is DELIVERED. BigLedger sets that on any WhatsApp status callback, a failure included, and never for e-mail. So an e-mail campaign always produces an empty report, and a WhatsApp one can include people the message never reached.
  2. For each contact it looks for a customer, and it only finds one through an active CRM contact whose ID number equals the recipient’s endpoint, in one of three forms: as given, without the +, and with the first two characters dropped (for +60…, the local 0… form). From that contact it reaches the customer by phone, e-mail, contact key or entity link.
  3. It pulls that customer’s sales lines — cash bills, sales invoices and sales orders only — with a non-zero amount and quantity, dated from when the campaign was run (the contact-list row’s creation time: Run now, or the moment you pressed Schedule, not the later send) to the end of the window.
  4. The window ends 28 days later unless you pass an end date.

Three limits follow. A walk-in customer who pays cash and is not matched to a contact record cannot appear, because step 2 cannot find them. A purchase on any other document type is not counted. And because both sales orders and the invoices raised from them count, one purchase can be counted twice — an inference from the filter, not something we have tested.

Re-running the generation appends a fresh set of rows; nothing removes the previous run. Compare generations by their creation date.

What this applet will not do

Stated plainly, because each of these has a table, a route family or a menu entry that suggests otherwise.

It will not find your customers for you. There is no rule-based segment. Segment conditions, criteria, and operator and value types all exist in the database and the API, and the one piece of code that reads them, the segment report, builds its SQL in a stream that is never run, so the conditions are never evaluated and the statement it produces is not valid SQL. It has not changed since 2022. A segment is a file you upload.

It will not run a treatment. A treatment — “send this template to this segment on this schedule through this endpoint” — has a header table, an execution table, four controllers, a full CRUD route family and 42 permission codes. Nothing executes one: no job, no scheduler, and no service writes a treatment execution row except the plain REST endpoint you would call yourself. The table and its API date from late 2021 and the execution endpoint from early 2023; the engine never followed.

It will not run a journey, a drip or an A/B test. One campaign is one message to one list at one moment. Sequencing, branching on behaviour, hold-out groups and variant splits have no representation anywhere in the applet.

It will not send on any channel but two. WhatsApp Business and e-mail broadcast are the only channels with a gateway. A segment line on any other channel fails at the send and writes no execution row.

It will not tell you an e-mail bounced, opened or was clicked. Nothing in the product takes in e-mail delivery events.

It will not check your unsubscribed list at send time, as far as we can establish. The unsubscribed-numbers table and its screen exist, and the campaign expansion does not join to them, nor to the segment line’s own unsubscribed flag: a number on the list is queued like any other. The message then leaves BigLedger through an AWS gateway whose behaviour we cannot read, so we cannot say whether suppression happens there. This is unresolved and we flag it rather than guess. Until it is settled, keep unsubscribed numbers out of the CSV you upload, and do not rely on the list as your only consent control.

Cautions

Media links expire after two days

The media a campaign message carries — the header image, video or document on WhatsApp, the attachments on e-mail — is handed to the gateway as a signed link that expires 48 hours after the message was processed, not 48 hours after the recipient opened it. An e-mail sends its body text and its attachments only; image, video or document components of the template are not included. The code carries a note saying a public endpoint should be built instead.

Whether a WhatsApp recipient still sees the media after two days depends on whether the gateway hands the file over at the send. For e-mail, whether a recipient who opens the message on day three still sees the file depends on whether the gateway attaches the file or sends the link, and BigLedger’s code does not show which. Send yourself the test e-mail described below and open it again after two days before you rely on images in an e-mail campaign.

Executing the same campaign twice queues it twice and may send nothing

The queue rows are written every time, but the send job is started only when the unique contact list gains a new row, and on a second execute of the same campaign it gains none. The result is duplicate messages sitting in the queue, unstarted, that go out the next time the send job runs: another campaign’s execute, a clock, or a manual run.

Do not press execute twice to “make sure it sent”. Check the execution rows instead.

Which duplicate wins is not decided by anything you can set

The de-duplication on the endpoint has no tie-break. If the same number sits in two linked segments under different first names, or against different channel types, the row that survives — and so the name your recipient is greeted by and the channel they are reached on — is arbitrary and may differ between runs. Keep a person in one segment per campaign.

Two different things are called a message template

The Template Message menu edits the contact centre’s templates, which are the ones a campaign sends. The applet’s own message-template routes, with a table of their own, are not read by campaigns. If you build templates through the API, build them in the Unified Contact Center’s template family.

Deleting a campaign does not stop messages already queued

Each queued message carries its own frozen copy of the send, and the send job does not check that the campaign still exists. Deleting a scheduled campaign therefore does not stop the messages it queued; they go out the next time the send job runs after their start time.

Anyone who can create a campaign can send any campaign

Execute checks the campaign-create permission (admin and owner also accepted), and the check is not tied to the campaign: whoever holds it can execute any campaign in the tenant, to everybody on its segments.

The part that is still your job

A campaign is a business decision with a send attached, and the software does the send. Everything below is a person’s work, before or after the button.

You decide who is on the list, and nothing checks you

Because a segment is an uploaded file, the list is exactly as good as whoever built the file, and the applet offers no opinion on it. Before you upload:

  • Get the list from a query someone can show you again, an export with its filter recorded, rather than a spreadsheet forwarded twice. When the results are questioned in a month, the question will be who was on it, and the segment will not tell you why.
  • Take the unsubscribed numbers out yourself. The applet may not do it, and the consequence of being wrong is a complaint, not an error message.
  • Put the four columns in order, write numbers one way, and compare the segment’s line count to your file’s row count after the upload. It takes ten seconds and it is the only check there is.

You approve the send, because the product has no approval step

There is no draft state, no reviewer and no second pair of eyes. Permission decides who may execute, not whether anyone looked. Whatever approval your business wants happens before someone with that permission presses the button, in a meeting or a message, not in BigLedger.

The practical minimum: send the campaign to a segment containing only yourself first, on the real channel, and read the message on a real handset. It is the only way to see the rendered result, including whether the media loads.

You choose the window the results are judged over

The sales-tracking report defaults to 28 days from the run, and 28 days is a number in the code, not a finding about your customers. For a phone, 28 days is generous; for an appliance, it may be too short. Decide the window that matches how long your customers take to buy what you advertised, pass that end date, and use the same length every time you compare two campaigns — otherwise you are comparing windows, not campaigns.

You reconcile attribution, because the report cannot

The report credits a campaign with any qualifying sale by a matched customer inside the window. It does not know whether the message caused it, and it cannot see a purchase by someone it could not match to a contact record — which, in a retail business, is most walk-in cash customers. Before anyone acts on the figure, someone has to say what share of sales is matchable at all. A useful habit is to keep a small part of the list unsent, look up what those people bought over the same window by hand, and compare: that is the baseline the report cannot give you.

You notice that nothing arrived

Nothing raises an alarm when a campaign sends to nobody. Execute returning 0 is a successful response. Messages waiting in the queue are not shown. An e-mail campaign’s delivered count of zero looks the same as the feature not working.

The person who ran the campaign has to look, within the hour:

  • Did the campaign produce execution rows at all? Zero means the expansion matched nobody — almost always the valid-line filter or an inactive template.
  • Does the execution count match the segment’s valid line count, allowing for duplicates removed?
  • Ask a few people on the list whether the message arrived. The delivered count may never move (see the counters callout), so it cannot tell you.
  • For a scheduled campaign, check again after the start time: if nothing moved, the send job has not run since.

Related applets

  • Unified Contact Center — owns the endpoints and the message templates every campaign uses, and is the only source of delivery, read and reply status. A campaign is unusable without it. For one message to one list, sent now, its Broadcast screen is the simpler tool.
  • Engagement Applet — a client register for an audit or tax practice, not a marketing tool, despite the name.
  • Bot Applet — conversational flows on the same contact-centre channels. Campaigns push; bots respond.

Troubleshooting

SymptomCauseWhat to do
The upload fails and nothing is importedOne row has an empty ENDPOINT_ID, an unrecognised CHANNEL_TYPE, or fewer than four columnsFix the row and upload again; the importer takes the whole file or none of it
Execute returns 0 and nothing is queuedNo matched segment line is marked valid — on a CSV-uploaded e-mail segment, all of them; on WhatsApp, the numbers that failed validation — or the template, a segment or the campaign is not activeRead the reason stored beside each invalid line. For e-mail, create the lines through the segment-line API instead of the upload screen
A scheduled campaign never sendsThe messages are queued for later, and the send job has not run since their start timeAdd a clock on the campaign-processing job in the Scheduler, or wait for the next campaign execute
A recurring campaign was created but never sentThe recurring job only creates the campaignOpen the generated campaign and run it
Delivered / read / replied stay at zeroOn e-mail, nothing reports back. On WhatsApp, the callback looks the execution row up by the wrong idUse the execution row count, and ask recipients, until the WhatsApp callback is settled
The sales-tracking report is emptyIts list is limited to DELIVERED contacts, which only WhatsApp sets; or no active CRM contact carries the recipient’s number as its ID number; or the purchases fall outside cash bills, sales invoices and sales ordersCheck the delivered count first. If it is zero, the report will be empty
Campaign counters look staleThey are recomputed only on Refresh SummaryPress Refresh Summary
Duplicate messages arrive later, apparently at randomA second execute queued them without starting the send job; a later run sent themDo not re-execute. Check execution rows to confirm a send
A recipient is greeted by the wrong nameThey appear in two linked segments with different first names, and the de-duplication has no tie-breakKeep each person in one segment per campaign
A message on a channel other than WhatsApp Business or e-mail never goes and leaves no execution rowOnly those two channels have a gatewayUse one of the two channels on the segment line

Related documentation

Last updated on