The Sailup team · · 12 min read
How to Send Bulk SMS in Ghana: A Step-by-Step, NCA-Compliant Workflow
Bulk SMS in Ghana takes a registered sender ID, a list of people who opted in, a one-segment message with a free opt-out line, and a send between 8am and 7pm, Monday to Saturday. Here is the workflow on Sailup.
Sending bulk SMS in Ghana comes down to five steps: register a sender ID, build a list of people who agreed to hear from you, write a message that fits one 160-character segment, create a campaign, and read the delivery report. Around those steps sit three rules from the NCA and the Data Protection Act that decide whether the campaign is legal, and they are worth reading before you touch a spreadsheet. This post walks through the whole workflow on Sailup and says plainly where you will need a workaround.
What bulk SMS means here
Bulk SMS is one message sent to many phone numbers at once, from a business name rather than a phone number. The name is your sender ID, and it is what the recipient sees at the top of the message: Kofi Stores, not 024 XXX XXXX.
On Sailup the pieces are a sender ID (registered once and approved by the carriers), contacts grouped into lists, and a campaign that joins a list to a message and a sender. One campaign reaches MTN, Telecel and AirtelTigo numbers from one balance at one rate. All of it works from the dashboard with no code; the API is there for the day your own software needs to send.
The three rules before you send anything
Two documents govern bulk SMS in Ghana: the NCA's Unsolicited Electronic Communications Code of Conduct and the Data Protection Act, 2012 (Act 843). Between them they reduce to three rules.
Only text people who opted in. The UEC Code says marketing messages "shall be permission/subscription/consent based" and that consent "will always follow an Opt-in Procedure". It is explicit about the shortcut every business is tempted by: "Service Providers shall not use an existing list of phone numbers, unless they have explicit consent of the subscribers to join the messaging service." Act 843 adds that a phone number is personal data, so it has to be processed with consent and for the purpose it was collected. A number a customer gave you for deliveries was not given for promotions. Organisations that process personal data are also expected to register with the Data Protection Commission.
Send promotions between 8am and 7pm, Monday to Saturday. The NCA's consumer guidance says "You should receive promotional messages on your phone from 8.00 a.m. to 7.00 p.m. only" and that "promotional messages should not be sent to you on Sundays". The Code itself is addressed to licensed operators and content providers, and its timing clause is written about network commercial communications, but every bulk SMS provider in Ghana applies the window to promotional traffic and so do we. Transactional messages (an OTP, a receipt, a delivery update the customer is waiting for) are not promotions and go out whenever the event happens. The promotional versus transactional post covers the grey areas.
Put a free opt-out in every promotional message. The Code requires that "a means by which a Recipient may unsubscribe shall be provided for in all Commercial Electronic Communications", that it keeps working for at least 30 days after the message, and that "Unsubscriptions from UECs shall be without charge." This is the rule that needs a workaround on Sailup, covered in Step 3.
Penalties sit under the Electronic Transactions Act, 2008 and the Electronic Communications Regulations, 2011, and the NCA can serve enforcement notices. The consequence you will meet sooner is closer to home: a recipient complaint triggers a review of your traffic, and Sailup can withdraw a sender ID whose messages do not match what was registered.
Step 1: Register a sender ID
Do this first, because carrier approval takes 1 to 3 business days and nothing goes out without it.
In your project dashboard, open Sender IDs and submit a name, a use case and a sample message of up to 160 characters. The format is 3 to 11 characters, letters, numbers and single spaces only, with at least one letter. The name has to be your brand or business name, and it has to say who you are rather than what the message is about. Results and Invoice are refused on that basis, as are generic words like ALERT, INFO, VERIFY and UPDATE, and lookalikes of other people's brands. If the name does not match the details on your account, attach a business registration certificate or a letter on company letterhead. Names in banking, mobile money, government, health, examinations, utilities and telecoms need an official email address from that organisation on the Sailup account.
The request sits at Pending until the carriers approve it, then turns Active. An approved name works on MTN, Telecel and AirtelTigo. Each account gets two sender IDs, one transactional and one promotional; if you only run campaigns, one is enough. The full rules, with the names that get rejected and why, are in the sender ID guide.
Step 2: Build a list of people who agreed
The list is the part of the workflow the regulator cares about most, so build it from people who ticked a box, filled in a form or messaged you to ask.
Import the file you already have. The contacts import accepts CSV, XLSX and XLS files, as long as the first row is a header. Sailup reads the headers and suggests a match for Phone, First name, Last name or Full name from the usual names (phone, mobile, msisdn, tel, firstname, surname, name). Any column it does not recognise becomes a custom attribute on the contact, so a "branch" or "class" column comes through as attributes.branch or attributes.class and can be merged into a message later. Set any column you do not want to "Don't import".
Fix the bad rows before they land. The Review step flags invalid rows, and nothing is saved until you fix or remove them. Rows go up in batches of 500, and the result tells you how many contacts were created and how many were updated, because existing contacts are matched by phone number. Re-importing last month's spreadsheet updates people rather than duplicating them.
Keep consent on the contact. Every contact carries an opted_in flag, an opt_in_source (API, CSV or MANUAL) and an opted_out_at timestamp. When someone asks to stop, switch opted_in off from the dashboard or through the API, and every later campaign leaves them out.
Group into lists. A contact can belong to many lists, so "Osu customers" and "Bought in August" can overlap without duplicating anyone. Lists are the audience you pick at campaign time, and they are the only audience that supports merge fields.
Step 3: Write the message
Write for one segment. A segment is 160 characters in the GSM-7 alphabet, and one segment is one credit per recipient. Two things push a message past that: length, and characters outside GSM-7. A single curly quote, an emoji, the cedi sign or a Twi letter like ɛ flips the whole message to Unicode, where a segment holds 70 characters instead of 160. Write "GHS 50" instead of "GH₵50", use straight quotes, and use a plain hyphen. The character limit post lists every trigger. The campaign editor only counts characters against its 4,096 limit, so check the segment count before you paste the body in: the one-off send form under Messages counts segments as you type and warns you the moment a character needs Unicode.
Personalise with merge fields: {{first_name}}, {{last_name}}, {{phone}}, and {{attributes.branch}} for any custom attribute you imported. The body can be up to 4,096 characters, but a promotion should never need more than one segment.
Here is a promotional message that fits:
Hi {{first_name}}, Kofi Stores Kumasi has rice, oil and sugar at last month's prices till Sat 13 Sept. To opt out, WhatsApp STOP to 024 XXX XXXX.
With a name like Ama merged in, that is 134 characters, all GSM-7, one segment. Leave room for long names; Nana Akosua takes eight more characters than Ama.
The opt-out line, and the STOP problem. Sailup sends SMS; it does not receive it. A reply to your campaign goes nowhere, so "Reply STOP" on its own does not satisfy the Code. Point the instruction at a channel you actually monitor: a WhatsApp line, a phone number to call, or a keyword on a platform that can receive replies. When a request comes in, switch the contact's opted_in flag off, and the next campaign skips them. Unsubscribing has to be free for the recipient, and a WhatsApp message or a call to your ordinary line qualifies. The line costs 42 characters in the example above, so budget for it.
Step 4: Create the campaign
- Open Campaigns and give it a name of up to 100 characters. Name it for the report you will read later: "Kumasi restock 13 Sept", not "Campaign 4".
- Choose the audience: a saved list, all contacts, or numbers typed in. Typed numbers are capped at 10,000 and have no contact record behind them, so merge fields are blocked; for anything larger or anything personalised, use a list. Typed numbers also bypass opt-out: a contact who asked to stop is left out of a list send, but the same number typed in will still receive the message, so only type numbers you have checked yourself.
- Pick the sender ID. One sender can be set as the project default, which saves a click every time.
- Paste the message you already checked in the send form.
- Choose Send immediately or Save as draft.
There is no scheduler. If the campaign has to go out at 9am on Saturday, save it as a draft on Friday and press send on Saturday morning; the draft keeps the audience, sender and body. If you need the same send every week, drive the API from a cron job on your own server (see below). Any campaign can be duplicated into a new draft, which is the quickest way to repeat last month's send with a new body.
Before you press send, check the balance against recipients times segments. A campaign to 5,000 contacts with a one-segment message needs 5,000 credits. With auto top-up on, a send that outruns your balance sends what it can and holds the rest in pending balance until the card is charged. With it off, a send your balance cannot cover is rejected with insufficient_balance and nothing goes out.
Step 5: Read the report
A campaign moves from Sending to Completed, and the report then shows total recipients, sent, delivered, failed, opted out, delivery rate, and when it was triggered and finished. Read the numbers in this order.
- Opted out is the number of contacts the campaign skipped because their
opted_inflag was off. It is not a failure; it is the consent rule working. Typed numbers are never counted here, because they are not checked. - Sent means handed to the carrier. It is not the same as delivered. A message stays sent (the stats call it unconfirmed) until the carrier reports back, and that can take a while for a phone that is switched off.
- Delivered is carrier-confirmed. Sailup's delivery rate counts only these, so it will read lower than a provider that counts sent as success. Delivered plus failed plus unconfirmed equals sent.
- Failed covers two carrier outcomes. Expired means the carrier gave up because the handset was unreachable for too long. Rejected means the carrier refused the number.
A number that rejects is usually wrong or out of service, so remove it. A number that expires on two campaigns in a row is probably dead, so remove that too. If every message fails, look at the sender ID (is it still Active?) and the balance before you look at the list. The delivery reports post walks through every status and what to do about it.
When to move to the API
The dashboard is the right tool for a campaign a person decides to send. The API is the right tool when the trigger is an event in your software: a payment lands, an order ships, a login needs a code, or a cron job fires at 8am on Saturday. Those messages go to one recipient at a time, at the moment the event happens, and nobody should have to press a button.
The endpoint is the same for one recipient or a hundred, which is the cap per request; anything larger belongs in a campaign. It is POST https://api.sailup.io/v1/sms/, with a project API key from Settings.
curl -X POST https://api.sailup.io/v1/sms/ \
-H "Authorization: Bearer sailup_a1b2c3..." \
-H "Content-Type: application/json" \
-d '{
"from": "KofiStores",
"to": ["0201234567"],
"body": "Hi Ama, your order #1042 is ready for pickup at Kofi Stores, Adum. Bring this message. Open 8am-6pm Mon-Sat."
}'That body is 108 characters, one segment. Sailup answers 202 Accepted with, for each recipient, a message id and a quantity (segments, which is what you are charged); delivery_status reads pending until the carrier reports back. Register a webhook in the dashboard and Sailup calls your server on message.delivered, message.failed, message.expired and message.rejected, so there is nothing to poll. Contacts and lists have endpoints too, including a bulk upsert of up to 500 contacts per request. The SMS API reference has the full request and response shapes.
What it costs
Sailup charges ₵0.02 per segment to every network, with no monthly fee, no setup cost and no minimum spend. Credits are prepaid, start at 100 credits for ₵2, and never expire. They are bought through Paystack, by card or MoMo, and auto top-up needs a saved card. The only variable is segments: a one-segment message costs one credit per recipient, and a message that spills into two segments costs double.
| Recipients | One segment | Two segments |
|---|---|---|
| 1,000 | ₵20 | ₵40 |
| 5,000 | ₵100 | ₵200 |
| 20,000 | ₵400 | ₵800 |
If you send at high volume every month, contact the team with your monthly volume and we will talk about a rate.
Start with the sender ID today, because the approval clock is the only part of this you cannot speed up. While it is pending, clean the spreadsheet and write the message. Create an account, register the name, and your first campaign can go out this week.
Frequently asked questions
Can I send bulk SMS in Ghana without registering a sender ID?
Not on Sailup. Every campaign and every API call goes out from a sender ID that the carriers have approved, and the name has to be your brand or business name. Approval takes 1 to 3 business days, so register the name before you build the list. An approved name works on MTN, Telecel and AirtelTigo.
What hours am I allowed to send promotional SMS in Ghana?
The NCA's consumer guidance says promotional messages should arrive between 8am and 7pm only, and not on Sundays, so the safe window is 8am to 7pm, Monday to Saturday. Transactional messages such as OTPs, receipts and delivery updates are not promotions and can be sent at any hour, because the customer's own action triggered them.
How do recipients opt out if Sailup cannot receive replies?
Point the opt-out line at a channel you monitor, such as a WhatsApp number or a phone line, instead of asking people to reply to the SMS. When a request arrives, switch the contact's opted_in flag off in the dashboard or through the API. Every later campaign skips that contact and counts them under Opted out in the report. The opt-out must be free for the recipient.
How much does it cost to send 10,000 bulk SMS in Ghana?
On Sailup, 10,000 recipients cost ₵200 if the message fits in one 160-character GSM-7 segment, because the rate is ₵0.02 per segment on every network. A two-segment message costs ₵400. There is no monthly fee or setup cost, the smallest purchase is 100 credits for ₵2, and credits never expire.
Can I schedule a bulk SMS campaign to go out later?
Sailup has no scheduler. The two ways to send at a chosen time are to save the campaign as a draft and press send when the time comes, or to call the SMS API from a cron job on your own server. Drafts keep the audience, sender ID and message, and any campaign can be duplicated into a new draft.
Can I upload an Excel file of customer numbers to Sailup?
Yes. The contacts import accepts CSV, XLSX and XLS files with a header row. Sailup suggests which columns are the phone number and names, turns any other column into a custom attribute, and flags invalid rows for you to fix before anything is saved. Existing contacts are matched by phone number, so re-importing a file updates people instead of duplicating them.
Send your first SMS in five minutes.
No setup fees, no contracts — pay only for what you send, and volume discounts when you scale.