Settings

Create, Preview, Test, Send and Schedule Customer Campaigns

Campaigns send approved SMS or WhatsApp messages to audiences selected from customer and loyalty data. Preview is read-only; sending and scheduling create durable campaign and recipient records.

Menu path
Manage -> Marketing -> Campaigns
Verified from
Posnic demo and current POS source reviewed on 2026-09-13
Technical source evidence

Live Posnic demo reviewed 2026-09-13, frontend/modules/settings_write.html Campaigns tab, frontend/static/script/js/modules/js/campaigns.js, api/src/services/campaign.service.js, api/src/controllers/campaign.controller.js, api/src/constants/campaign.constants.js, api/tests/unit/services/campaign.service.db.test.js, Messaging integration settings

Compose a campaignLive demo rechecked 2026-09-13. Choose the audience and channel, write the message, preview reach, then use the controlled delivery workflow below.
Messaging dependencyA campaign can be composed without a working provider, but real delivery depends on Messaging configuration.

Understand the Campaign Lifecycle

StageWhat Posnic doesOperator evidence
PrepareUses customer phone, channel preferences, category, loyalty and purchase history.Clean customer records and an approved message.
PreviewCounts the selected segment and reachable customers without saving or sending.Reachable of total plus up to five example names.
SaveCreates a draft with its channel, message, segment, branch and operator metadata.Draft row in the campaign list.
SendRebuilds the audience, renders one message per customer and dispatches through the saved branch provider.Sent, failed and skipped totals.
ScheduleStores a date/time and marks the campaign scheduled until the due runner processes it.Scheduled badge and saved time.
AuditKeeps per-recipient outcome records even when the campaign row is later deleted.Campaign status and support-side send history.

Prepare Before Composing

  • Enable Marketing under Manage -> Features. Campaign creation and real delivery require Branch Write unless that permission is not explicitly denied.
  • Configure and test the required provider under Manage -> Messaging. SMS uses the branch SMS provider; WhatsApp uses the shop's linked local WhatsApp device in the current campaign service.
  • Clean customer phone numbers, loyalty tiers and points, category assignments, last-purchase dates and per-channel opt-out records before selecting an audience.
  • Agree the purpose, audience, message, delivery time and approver outside the send screen.
  • Confirm the active branch before creating the campaign because its messaging adapter and audit metadata come from that branch.
  • Use Preview reach first. Do not use Send now as a preview.

Choose the Audience Precisely

AudienceActual filterImportant boundary
All customersEvery customer under the current license enters the segment.Reachable still excludes missing phones and channel opt-outs.
Loyalty tierExact match on the customer's current loyalty tier; options load from Loyalty configuration.A blank tier falls back to all customers in the backend query.
Minimum pointsCurrent loyalty points greater than or equal to the entered value.Negative or invalid input is cleaned to zero.
Lapsed (no purchase)Last purchase is older than the calculated cutoff, missing or null.Zero days includes purchases earlier than the current instant plus customers with no recorded purchase.
Customer categoryExact customer category ID match.The current form asks for the internal category ID, not its display name; a blank value falls back to all customers.

Create the Campaign Step by Step

  1. Open Manage -> Marketing -> Campaigns and click New campaign.
  2. Enter a clear internal Name. Name and Message are mandatory; only WhatsApp or SMS is accepted.
  3. Choose the Channel whose provider and customer consent records have already been checked.
  4. Choose the Audience and complete the tier, minimum-points, lapsed-days or category-ID field that appears.
  5. Write the Message and insert supported merge chips at the cursor where needed.
  6. Select Preview reach and record the reachable count, total segment count and sample names.
  7. Correct source customer data or narrow the audience when the preview is unexpectedly broad, narrow or empty.
  8. Select Save draft when copy, timing or audience still needs approval.

Use Merge Fields Correctly

  • The live form exposes chips for {name}, {points} and {tier}; supported backend fields also include {phone}, {balance} and {lifetime} when typed exactly.
  • Token matching is case-insensitive, but use the displayed lowercase spelling for review consistency.
  • Preview reach does not show the rendered message. Proofread every token and test with representative customer data.
TokenRendered valueEmpty-data behavior
{name}Customer name.Blank text.
{phone}Stored customer phone.Blank text.
{points}Current loyalty point balance.0.
{balance}The same current loyalty point balance as {points}.0.
{lifetime}Lifetime points earned.0.
{tier}Current loyalty tier.Blank text.
Unknown tokenAn unsupported token such as {shop} remains literally in the message.Never assume it will be removed or populated.

Preview Reach Before Saving

Preview is the safest read-only audience check. Posnic rebuilds the selected segment, counts every matching customer, then counts a customer as reachable only when a nonblank phone exists and that customer has not opted out of the selected channel.

  • The sample contains at most five reachable customers and displays names, falling back to phone only when the name is blank.
  • Preview checks that a phone string exists; it does not prove the number is valid or that the provider can deliver.
  • Changing Channel can change the reachable count because SMS and WhatsApp opt-outs are independent.
  • The audience query is license-wide in the current service, not restricted to the branch that created the campaign. Treat Preview as the authoritative reach count for multi-outlet shops.

Understand the Current Dry-Run Caveat

Test (dry run) first saves the form, renders a message for each reachable customer and writes recipient logs without calling SMS or WhatsApp. However, the current de-duplication query treats a dry-run record as already processed. A later real send of that same saved campaign skips those dry-run recipients.

  • A dry run dispatches zero messages and leaves the campaign status unchanged, normally Draft.
  • Its result alert reports reachable dry-run rows under skipped rather than sent.
  • Do not dry-run the exact campaign record intended for delivery in the current release.
  • Safe controlled workflow: preview the final audience, create a temporary test campaign for the dry run, inspect the result, delete that temporary row if approved, then create a fresh campaign record for real send or scheduling.
  • Deleting the temporary campaign keeps its recipient send history in the database. Use a clearly labeled test name for auditability.

Save, Edit and Delete Drafts

  • Save draft creates a durable campaign row with Draft status and zeroed result totals.
  • Open uses the pencil action and loads the row into the same form. Saving updates name, channel, message, segment and branch metadata but preserves the existing status and counters.
  • A sent campaign may be opened and edited, but editing does not make it sendable again; Send now still rejects a Sent record.
  • Delete asks for confirmation and removes the campaign row. Its per-recipient send history is deliberately retained.
  • Do not edit a sent record to represent a new promotion. Create a new campaign so content and delivery evidence stay aligned.

Send Now Safely

  1. Create or open the approved delivery campaign and run Preview reach again.
  2. Confirm the campaign is a fresh record with no dry-run logs attached.
  3. Select Send now. Posnic saves current form values before it asks for confirmation.
  4. Read the confirmation: reachable customers will be messaged. Cancel if any audience, copy or provider check is incomplete.
  5. Confirm only once. The campaign moves through Sending and finishes as Sent or Partial.
  6. Record the displayed sent, failed and skipped totals and compare them with Preview reach.
  7. Open the list again and verify channel, audience label, status and accumulated result totals.

Schedule a Future Send

A future campaign is sent only when the server's due-campaign runner executes. Invalid dates are rejected, but the service does not itself reject a past time. Confirm server and branch time-zone policy before scheduling.

  1. Enter a date and time in the field beside Schedule. The browser submits the datetime-local value and the server stores the parsed instant.
  2. Select Schedule. Posnic saves the campaign first, then marks it Scheduled and stores schedule_at.
  3. Verify the Scheduled badge in the list; the current list does not display the scheduled timestamp, so retain the approved time separately.
  4. Keep the branch provider connected until the scheduled runner processes the due campaign.
  5. After the due time, verify the status and result totals rather than assuming the runner dispatched it.

Read Status and Result Totals

ValueMeaningOperator response
DraftSaved but not scheduled or successfully sent.Review and preview; do not assume delivery.
ScheduledWaiting for the due runner at schedule_at.Verify timing and provider readiness.
SendingA real send has started.Do not click Send again.
SentThe run finished with no provider failures, including a run where everyone was skipped.Read totals; Sent does not guarantee at least one recipient received a message.
PartialOne or more provider sends failed, whether or not another send succeeded.Fix the provider issue, then use the controlled retry behavior.
ResultCumulative sent and failed counters; skipped appears in backend result but the list summarizes sent and failed only.Keep the immediate result alert when skipped totals matter.

Retry a Partial Campaign

A Partial campaign can be sent again. Posnic suppresses every customer with an earlier non-failed recipient record and retries only customers whose prior record was Failed. This makes successful deliveries idempotent per campaign and customer.

  • Fix the SMS or WhatsApp provider before retrying.
  • Do not change the audience or message before a retry; doing so makes the original and retry evidence describe different content.
  • Customers skipped for no phone, opt-out or dry run are treated as processed and are not reconsidered on that same campaign.
  • Successful retry increments the cumulative Sent count, while the earlier Failed count remains in the campaign totals.
  • A fully Sent or currently Sending campaign is rejected by Send now.

Permissions, Scope and Privacy

AreaCurrent behaviorOperating rule
Read and previewAuthenticated campaign routes list records and preview audiences without an explicit Branch Write check.Limit Marketing access to trusted staff.
Create, update, delete, schedule, real sendRejected only when Branch Write is explicitly false.Use a manager-controlled role and approval process.
Dry runDoes not require Branch Write in the campaign controller, but it writes recipient logs.Treat it as an auditable data action, not a harmless screen preview.
Campaign recordsList, get and audience selection are license-wide; branch_id is metadata and chooses the messaging provider.Review cross-outlet reach and customer consent before every send.
Recipient dataLogs contain customer ID, name, phone, rendered message, channel, outcome and error.Handle campaign history as customer personal data.
Opt-outOnly an explicit false blocks the selected channel; SMS and WhatsApp preferences are separate.Maintain consent on the customer record and follow applicable messaging law.

Controlled Campaign Test Matrix

TestExpected resultEvidence
Preview onlyNo campaign saved and no message dispatched.Reach total and unchanged campaign list.
Missing phoneCustomer counts in total but not reachable and is skipped in a run.Customer record and skipped result.
Channel opt-outExcluded for that channel but may remain reachable on the other channel.Both channel previews.
Tier boundaryOnly exact current tier matches.Loyalty profile and preview sample.
Points boundaryA customer exactly at the threshold is included.Points balance and reach count.
LapsedOld, null and absent purchase dates are included.Customer histories and reach count.
Merge renderingKnown fields render current values; unknown token stays literal.Temporary dry-run recipient log.
Dry-run isolationZero adapter calls and campaign status unchanged.Dry-run result and no received message.
Real sendReachable opted-in phone receives once.Provider ID/log, phone receipt and campaign totals.
Provider failureCampaign becomes Partial and the recipient is Failed.Error and failed count.
Partial retryOnly previously failed recipients are attempted.Provider logs before and after retry.
Scheduled futureNo send before due time; runner sends after due.Scheduled badge and later provider log.

Troubleshoot Campaigns

ProblemLikely causeAction
Campaigns tab is missingMarketing is disabled or navigation access is restricted.Enable Marketing with an authorized role and reopen Manage.
Reach is zeroNo matching customers, no phones or all matching customers opted out.Check total versus reachable, then repair source customer data.
Tier list is emptyLoyalty tiers did not load or are not configured.Configure Loyalty and reopen Campaigns.
Category reaches everyoneBlank or incorrect internal category ID fell back to the broad query.Do not send; obtain and verify the exact category ID, then preview again.
Dry run shows 0 sentDry-run rows are counted as skipped in the returned summary.Confirm no messages arrived and inspect the dry-run audit result.
Real send after dry run reaches nobodyThe same campaign's dry-run records triggered de-duplication.Create a fresh approved delivery campaign; never delete customer consent or recipient history to bypass it.
Status is Sent but sent count is zeroThe audience was empty or all recipients were skipped without provider failures.Read the immediate totals and investigate reach before claiming delivery.
Status is PartialAt least one provider dispatch failed.Fix provider/configuration, preserve evidence and retry only the unchanged Partial campaign.
Scheduled campaign did not sendDue runner has not executed, provider is unavailable or time interpretation differed.Check stored time, server scheduler and provider logs.
Send says already sentCampaign is Sent or still Sending.Do not bypass the guard; create a new campaign for new content or audience.
Wrong names or loyalty valuesSource customer or loyalty data was blank/stale at render time.Correct source records and create a new campaign when content must change.