Transactional
Your application’s e-mails, finally kept
A receipt that does not arrive is a customer who calls. Lekalao sends your applications’ e-mails through your provider, and keeps every one of them in a log.
14-day trial. Nothing to pay to start.
Atelier Mboa
Your receipt no. 20418
Thank you, Mireille.
- Glazed bowl ×2
- 18,000
- Delivery
- 2,500
Total20,500 FCFA
Log
Resend- Accepted by Postmark09:41:07
- Delivered09:41:09
- Opened, twice09:52:30
order-receipt · fr
Three ways to plug in
- The API
- One POST: variables, attachments, the language, the provider.
- The Laravel transport
- MAIL_MAILER=lekalao, and your code does not change.
- The SMTP relay
- For what only speaks SMTP: an API token is the password. No TLS today, so self-hosted or behind your own terminator.
curl -X POST "$LEKALAO_URL/api/v1/transactional-mails/send" \
-H "Authorization: Bearer $LEKALAO_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-2026-00481-confirmation" \
-d '{
"template": "order-confirmation",
"to": ["ada@example.com"],
"locale": "fr",
"variables": {
"customer": { "name": "Ada" },
"order": { "number": "2026-00481", "total": "12 500 FCFA" }
}
}'// config/mail.php
'mailers' => [
'lekalao' => [
'transport' => 'lekalao-api',
'endpoint' => env('LEKALAO_URL'),
'token' => env('LEKALAO_TOKEN'),
],
],
// .env: MAIL_MAILER=lekalao
Mail::to($order->customer)->send(new OrderConfirmed($order));MAIL_MAILER=smtp
MAIL_HOST=$LEKALAO_SMTP_HOST
MAIL_PORT=2525
MAIL_USERNAME=lekalao
# An API token with the "write" ability
MAIL_PASSWORD=$LEKALAO_TOKENThe log answers “did they get it?”
Recipients, content, delivery, opens, clicks, and a button to send it again. Statistics per template.
Two streams, two providers
Campaigns through one, invoices through the other. A campaign that draws complaints never spoils the stream that sends your receipts.
Templates marketing can proofread
HTML, Markdown or MJML, one version per language, default recipients. The application sends the variables; the wording is corrected without a deployment.
Webhooks that prove themselves
Subscriptions, departures, tags, bounces, complaints: each call is signed with HMAC-SHA256. The log keeps the answer it received, and an address that fails ten times in a row is switched off.
Verify a webhook
Route::post('/webhooks/lekalao', function (Request $request) {
$expected = hash_hmac(
'sha256',
$request->getContent(),
config('services.lekalao.webhook_secret'),
);
$received = (string) $request->header('X-Lekalao-Signature');
abort_unless(hash_equals($expected, $received), 401);
// subscriber.unsubscribed, mail.bounced, campaign.sent…
return response()->noContent();
});Everything the interface does, your code does too
The same key never writes twice: an Idempotency-Key returns the first answer for 24 hours. Limits are counted per token, not per address. A thousand subscribers go in one call, with a report per row.
A test refuses any route without a description, and any description without a route.
No SDK, on purpose
Five packages to maintain while the API still moves would cost more than they bring. The OpenAPI specification is right; your generator does the rest. For Laravel, the transport is ready.
How it is built
Laravel, PostgreSQL, Redis. More than 500 automated tests, one of which opens every page with data in it. This site subscribes to its own list through the product’s form.
Fourteen days to see for yourself
Create the account, connect your provider, send your first campaign. Nothing to pay to start.