Email and SMS
Solvo sends every email and SMS through one pipeline: pick a template → fill it with the platform's or the school's details → save it to the outbox → hand it to the engine you connected. Every message is logged, failures retry on their own, and you can see exactly what was sent.
1. Connect an email engine
Super Admin → Email & SMS engines. Choose an engine, paste the details it asks for, save, then use Send test email. Keys are encrypted before they are stored.
| Engine | What you need | Notes |
|---|---|---|
| Resend | API key | Verify your sending domain at resend.com/domains first. |
| Mailchimp Transactional (Mandrill) | Transactional API key | Mailchimp's transactional add-on, not the newsletter API key. |
| Mailgun | Private API key, sending domain, region (US/EU) | Use the region your domain was created in. |
| SendGrid | API key with "Mail Send" permission | Verify a single sender or domain. |
| Brevo (Sendinblue) | API key (v3) | |
| Postmark | Server API token, message stream | Stream defaults to outbound. |
| Amazon SES | Access key ID, secret access key, region | Uses the SES v2 API with Signature V4. Request production access to leave the sandbox. |
| ZeptoMail (Zoho) | Send-mail token, API host | Host is api.zeptomail.com (or .eu, .in …). |
| Elastic Email | API key | |
| MailerSend | API token | |
| SMTP | Host, port, encryption, username, password | Gmail/Workspace needs an app password. cPanel mailboxes work too (port 465 SSL or 587 STARTTLS). |
| PHP mail() | Nothing | Uses the server's mail system. Fine for testing, weakest deliverability. |
| Log only | Nothing | Renders and logs messages without sending — useful on staging. |
Set the From name, From email and Reply-to on the same page. The From address must be on a domain you have verified with the engine.
Deliverability checklist: add the SPF and DKIM records your engine gives you to your domain's DNS,
add a DMARC record (v=DMARC1; p=none; rua=mailto:you@yourdomain), and send from an address on that
domain. Most "emails go to spam" problems are a missing DKIM record.
2. Connect an SMS gateway
Same page, lower card. Every gateway asks for a default country code (Kenya: 254), used to turn
local numbers like 0712 345 678 into +254712345678.
| Gateway | What you need |
|---|---|
| Africa's Talking | Environment (live/sandbox), username, API key, sender ID or short code |
| Twilio | Account SID, auth token, From number or Messaging Service SID |
| Advanta SMS (Kenya) | API key, partner ID, sender ID |
| Celcom Africa (Kenya) | API key, partner ID, sender ID |
| Infobip | Your base URL (from the Infobip portal), API key, sender |
| Vonage (Nexmo) | API key, API secret, From |
| Termii | API key, sender ID, channel, base URL |
| Custom HTTP gateway | URL, GET or POST, body format (form or JSON), body template with {{to}}, {{message}} and {{sender}} placeholders, extra headers, and the text a successful response contains |
| Log only | Nothing |
Then use Send test SMS. SMS templates avoid characters that force Unicode encoding, so a 160-character message costs one credit; the editor shows the character and segment count as you type.
3. Templates
Email & SMS templates lists every template by category, with its audience, channels and whether it has been customised. There are 44 email and 37 SMS templates:
- Account: welcome, password reset, password changed, one-time code
- Billing (platform → schools): signup complete, school welcome, trial ending, subscription invoice, payment received, overdue, suspended
- Fees: invoice, reminder, payment receipt, instalment plan created, instalment due, instalment paid
- Academics: absence alert, report card published, exam results, assignment posted, live class
- Admissions: application received, status update, offer, new application (to the school)
- Communication: announcement, new message, event invitation
- School life: library overdue, payslip ready, transport assignment, election open, alumni donation receipt
- Enquiries: contact form received (to the team), auto-reply (to the sender)
- Live chat: new conversation, reply while away, transcript
- Affiliates: application received, approved, commission paid
- System: general notice, test message
Editing
Open a template to edit the subject, preheader (the grey preview line in the inbox) and body, with a live preview on the right. Click a variable in the list to insert it. Send test sends the current draft to you with sample data. Reset to default removes your changes.
- Super Admin edits the platform-wide versions. Changes apply to every school that hasn't customised that template.
- School Admin sees the school templates (fees, attendance, admissions …) and can customise any of them for their own school only. Turning a template off stops that message for the school.
Template language
| Syntax | Meaning |
|---|---|
{{student_name}} |
Insert a value (HTML-escaped) |
{{{message_html}}} |
Insert a value that is already HTML (used for announcement bodies) |
{{#if balance}} … {{/if}} |
Show the block only when the value is not empty |
<a class="btn" href="{{pay_url}}">Pay now</a> |
A full-width, email-client-safe button in the brand colour |
<table class="facts">…</table> |
A two-column details table (labels left, values right) |
<div class="panel">…</div> |
A tinted call-out box (panel warn amber, panel success green, panel danger red) |
Always available: platform_name, brand_name (the school's name on school emails, otherwise the
platform's), brand_email, brand_phone, brand_address, brand_website, brand_whatsapp,
login_url, year and recipient_name. Each template lists its own extra variables in the editor.
How branding is chosen
- Platform emails (billing, affiliates, enquiries to the platform) use the platform's name, logo, colours and contacts from Branding & payments.
- School emails use the school's name, logo, brand colours, address, phone, email, website and social links — falling back to the platform's for anything the school hasn't filled in.
- The header uses the dark logo on a dark header and the light logo on a light header, so the logo is always readable. Upload both variants for best results.
- The footer carries the address, contacts, social links and, for schools, an optional footer note.
4. Per-school sender settings
School Admin → Sender settings: sender name, reply-to address and an email footer note. A school can also connect its own email engine and SMS gateway (handy if it already pays for a bulk SMS sender ID); otherwise it uses the platform's — recommended, nothing to set up.
5. The outbox and message log
Every message lands in the outbox first (comm_outbox). Interactive messages (password resets,
receipts) are sent straight away; bulk sends (reminders, invoices) are queued.
scripts/process_outbox.php — run it every 5 minutes from cron — sends queued messages and retries
failures after 5 minutes, 30 minutes and 2 hours. Message log shows recipient, template, engine,
status and the exact email or SMS as received, with a Retry button for failures.
Statuses: queued, sent, failed (after the last retry), suppressed (the demo edition, which never
sends real messages).
6. For developers
// Send a template (school branding when school_id is set, platform branding otherwise)
Comms::email('fee_payment_receipt', $guardian['email'], [
'recipient_name' => $guardian['full_name'],
'student_name' => 'Brian Muthoni',
'amount' => 'KES 4,800.00',
], ['school_id' => $schoolId, 'related_type' => 'payment', 'related_id' => $paymentId]);
Comms::sms('fee_payment_receipt', $guardian['phone'], $vars, ['school_id' => $schoolId]);
// Queue instead of sending now (picked up by process_outbox.php)
Comms::email('fee_reminder', $to, $vars, ['school_id' => $schoolId, 'queue' => true]);
New templates go in core/Comms/TemplateLibrary.php (key, name, category, audience, sample variables,
email subject/preheader/body and SMS text). New engines implement one method in
core/Comms/MailDrivers.php or core/Comms/SmsDrivers.php plus an entry in that file's catalog().