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.

  1. Click Top Up Credits on the SMS or WhatsApp card
  2. 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)
  3. 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

  1. Settings → Messaging → SMS Sender ID section
  2. Enter your desired sender (3–11 chars, alphanumeric only, no reserved words like "Arkesel", "Opterflow", "SMS", "Test")
  3. Submit → status becomes pending
  4. Copy on screen: "Awaiting approval from Arkesel (1–2 business days)"
  5. Once Arkesel approves, super admin flips status to approved in /admin/messaging-pricing
  6. 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.

  1. Settings → Messaging → Channels → WhatsApp row → Connect
  2. Opterflow generates a hosted-auth URL and opens it in a new tab
  3. Scan the QR code with your phone's WhatsApp
  4. Unipile notifies our webhook → status flips to connected
  5. 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.

Was this article helpful?