modules
Messaging — credits, sender ID, top-ups
How SMS and WhatsApp credits work, how to request a sender ID, and how to top up.
Updated 2026-04-19
Channel pricing model
Three channels, three pricing models:
- Email — monthly quota (default 5,000/month per org). Not credit-based. Sent via Emailit.
- SMS — prepaid credits. 1 credit = 1 message segment. Sent via Arkesel. Pricing set per-org by super admin.
- WhatsApp — prepaid credits. 1 credit = 1 message. Sent via Unipile. Same pricing model.
Email sends don't decrement credits. SMS + WhatsApp sends do.
Where to check your balance
Settings → Messaging (/settings/messaging). You'll see:
- Email quota: X / 5,000 used this month
- SMS credits: current balance
- WhatsApp credits: current balance
- Recent transactions: every top-up, usage, refund
- SMS sender ID: current status (none / pending / approved / rejected)
- WhatsApp channels: connection status
Topping up credits
Any admin or manager can request a top-up. Staff can't.
- Click Top Up Credits on the SMS or WhatsApp card
- In the modal:
- Pick a bundle chip (defaults from super admin pricing) or enter a custom amount
- Payment method:
- Pay with Paystack (primary, instant)
- Bank transfer / cash (manual approval by super admin)
- Submit
Paystack flow: redirect to Paystack checkout → pay → webhook fires → credits applied in seconds. Super admin doesn't need to approve.
Manual flow: transaction stays topup_pending until super admin approves. They see pending requests at /admin/messaging-topups.
SMS Sender ID
By default, SMS from Opterflow uses a shared sender ID (usually "Opterflow"). Most tenants want their own brand name to show up as the sender.
How it works
Arkesel (our SMS provider) needs to approve custom sender IDs before they can send. Their approval is off-platform — they review your org and grant the sender ID on their dashboard.
Requesting a sender ID
- Settings → Messaging → SMS Sender ID section
- Enter your desired sender (3–11 chars, alphanumeric only, no reserved words like "Arkesel", "Opterflow", "SMS", "Test")
- Submit → status becomes pending
- Copy on screen: "Awaiting approval from Arkesel (1–2 business days)"
- Once Arkesel approves, super admin flips status to approved in
/admin/messaging-pricing - Your next SMS sends use your custom sender
What if it's rejected?
Super admin enters a reason on rejection. You see it on the Sender ID card. Fix the issue, submit a new one.
Connecting WhatsApp
WhatsApp is powered by Unipile's hosted-auth flow. You connect your WhatsApp Business account (or personal WhatsApp, if you have one dedicated to the business) via QR code.
- Settings → Messaging → Channels → WhatsApp row → Connect
- Opterflow generates a hosted-auth URL and opens it in a new tab
- Scan the QR code with your phone's WhatsApp
- Unipile notifies our webhook → status flips to connected
- Display name + phone number populate on the card
Daily send cap: default 40 sends/day. Adjustable 1–500. This is a soft guardrail — WhatsApp personal accounts get blocked by Meta after sending to too many new contacts without warmup. Keep it low, warm up, increase over time.
When a cron batch hits the cap, the next messages are marked deferred and re-scheduled for tomorrow UTC midnight.
Disconnecting WhatsApp
Same card → Disconnect button. Clears the connection row. Unipile's side gets disconnected too (best-effort).
How the cron sends messages
/api/cron/campaigns runs every 5 minutes. For each active enrollment on a message node:
- Email → Emailit API. No credit deduction.
- SMS → Arkesel API with the per-org sender ID if approved, else env default. Deducts 1 credit per message. If no credits, marks message failed.
- WhatsApp → Unipile API. Uses existing WhatsAppChat if one exists for this contact, else starts a new chat. Deducts 1 credit per message. Daily cap enforced.
Recent transactions log
Every credit event shows up in the transactions list:
| Type | What triggered it |
|---|---|
topup |
Top-up approved (Paystack webhook or manual) |
topup_pending |
Top-up submitted, awaiting approval |
topup_rejected |
Top-up rejected by super admin |
usage |
One message sent (deducts 1 credit) |
refund |
Message failed, credit refunded |
manual_credit / manual_debit |
Super admin adjustment |
Common mistakes
- Expecting 0-approval top-ups on bank transfer. Paystack auto-approves. Bank transfer waits for super admin.
- Custom sender ID with special chars. Arkesel rejects anything with spaces, dashes, or non-alphanumeric. Validator blocks it client-side.
- Sending to unwarmed WhatsApp. New personal WhatsApp accounts get blocked after 2–3 new chats without organic conversation. Keep daily cap at 10–20 for the first week.
- Forgetting email quota. Email doesn't deduct credits but does have a monthly quota. Email campaigns can halt if you exhaust it.