Documentation
Everything needed to wire up contacts, send SMS under your own sender ID with automatic provider failover, connect your Hubtel Merchant Account for USSD payments, trigger SMS sends from your own backend, and auto-add contacts from another system.
00 Our services
Tunnelo SU ships four services on the same USSD and SMS infrastructure.
| Service | Purpose |
|---|---|
| SMS Gateway | Bulk SMS sending under your own sender ID, with delivery tracking and automatic failover across up to 3 providers |
| USSD Menu Builder | Build interactive USSD menus and deploy them to your shortcode |
| USSD Payment Collection | Collect money on a USSD menu via Hubtel's native AddToCart checkout |
| Contact Database Sync | Live read-only sync of contacts from your own Postgres, MySQL, MongoDB, or Supabase database |
01 Contacts & database connections
Add recipients manually, import a CSV, or keep a live sync from your own database.
phone, mobile, number, or msisdn — and is auto-detected. A name column (name, contact, person, first name, full name) is optional. No other custom fields are stored, and there's no merge-tag support in messages today.phone/mobile column (auto-detected by name, no manual field mapping), and is capped at 10,000 rows — export a smaller or filtered subset first if yours is bigger.// PostgreSQL connection postgres://readonly_user:password@db.yourhost.com:5432/yourdb?sslmode=require // MongoDB connection URI mongodb+srv://readonly_user:password@cluster0.mongodb.net/yourdb // Supabase (project API instead of direct Postgres) project_url: "https://xyzcompany.supabase.co" api_key: "YOUR_SUPABASE_KEY"
192.168.x.x) is unreachable from Tunnelo SU's servers — there's no network path in. Port-forward your DB to a public address instead, and restrict that forwarding rule to accept connections only from Tunnelo SU's IP: 68.183.116.88. This keeps your database access-controlled to one source rather than open to the whole internet.02 Sender ID & sending SMS
Register the name recipients see instead of a phone number, then send.
YOURBRAND) along with its credentials — Hubtel needs a Client ID and Client Secret; Arkesel and Nalo need only an API Key. There's no separate approval step on Tunnelo SU's side — credit and sender ID stay tied to your own provider account, never shared with other customers.↑ ↓ controls next to each — the top one is tried first on every send. If that attempt fails (provider outage, rejected message, bad credentials), Tunnelo SU automatically retries the same message through the next provider in your list, live, within the same send — no manual switching, no delay waiting on a health check. This applies to every send path: immediate sends, scheduled broadcasts, webhook-triggered sends, and USSD payment-confirmation SMS. The delivery log records which provider actually delivered each message.03 Groups & table selection
Organize contacts into reusable lists for targeted sends.
phone/mobile column is pulled into the group. There's no column-mapping step or filter; if you only want a subset, prepare that subset as its own table or view first.04 Connect your payment provider (USSD)
USSD payment nodes collect money through Hubtel's own native checkout — there's no provider to pick, and no separate charge API call.
payment, vote, or ticket item with a charge triggers Hubtel's own AddToCart prompt directly on the caller's phone as part of the USSD session — Tunnelo SU never makes a separate outbound call to start the charge. Money settles straight to your own Hubtel account; Tunnelo SU never holds customer payment funds.06 Trigger an SMS from your own system
Webhooks on Tunnelo SU work in reverse from most platforms: your own backend calls us, and we send the SMS — there's no outbound event stream to subscribe to.
{key} placeholders for values you'll send at call time, e.g. {name} or {order_id}.// Your webhook URL POST /api/inbound/<token> Content-Type: application/json { "name": "Efua", "order_id": "8f2a1c" } // Template: "Hi {name}, your order {order_id} has shipped." // Sends: "Hi Efua, your order 8f2a1c has shipped."
07 API keys — auto-add contacts
The opposite direction from §06: instead of Tunnelo SU sending an SMS, an external system creates a contact in Tunnelo SU — e.g. adding every new Shopify customer automatically, with no manual CSV export.
// Single contact POST /api/customer/contacts Authorization: Bearer <your key> { "name": "Jane Doe", "phone": "+233241234567", "group_name": "shopify" } // Bulk — up to 10,000 per call POST /api/customer/contacts/bulk Authorization: Bearer <your key> { "group_name": "shopify", "contacts": [{ "name": "Jane", "phone": "+233241234567" }, ...] }
phone is required on both. name is required on the single endpoint, optional on bulk. group_name is optional on both, and applies to the whole batch in bulk mode.// Keep the key server-side only — an env var, never in browser/client JS, // since anyone who can read it could add contacts to your account. const TUNNELO_API_KEY = process.env.TUNNELO_API_KEY; const TUNNELO_URL = 'https://your-domain.com/api/customer/contacts'; async function addContactToTunnelo({ name, phone, groupName }) { const res = await fetch(TUNNELO_URL, { method: 'POST', headers: { 'Authorization': `Bearer ${TUNNELO_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name, phone, group_name: groupName }), }); if (!res.ok) { const err = await res.json(); throw new Error(`Tunnelo contact sync failed: ${err.error ?? res.status}`); } return res.json(); // { contact: { id, name, phone, network, group_name, created_at } } } // e.g. inside your own "customer created" event await addContactToTunnelo({ name: newCustomer.fullName, phone: newCustomer.phone, groupName: 'shopify', });
Authorization header, so it only works in tools that let you set headers — a native "Webhook URL" field that just takes a bare URL (like most e-commerce/form apps' own settings) can't hold this key.| Platform | Where |
|---|---|
| Zapier | Add step "Webhooks by Zapier" → POST → URL + Headers (Authorization) + Data (body) all live in that one step |
| Make | Add an "HTTP" module → "Make a request" → URL, Method POST, Headers, Body |
| n8n | "HTTP Request" node → URL, Method POST, Header Auth, Body |
| HubSpot | Automation → Workflows → pick a trigger → action "Send a webhook" (Operations Hub Starter+; on lower tiers use Zapier's HubSpot trigger instead) |
| Your own backend | Straight into the POST call wherever a customer record is created |