WooCommerce & WordPress Integration
smslenz.lk offers a dedicated WooCommerce add-on plugin, so you can add SMS notifications to your WordPress store. Send automated SMS updates to customers for purchase events and keep them informed at every step.
Download SMSlenz for WooCommerce and start sending SMS notifications from your WordPress-powered store today.
REST API Endpoint Guide
Need to integrate SMSlenz.lk with your custom development? We provide a complete guide to our direct API endpoints: call them from your application and start using our SMS services instantly.
Our API supports all major programming languages, including PHP, JavaScript, Python, Java, C#, Ruby, Go, and Node.js. It works with any type of application, whether it’s a web platform, desktop software, or mobile app (Android or iOS).
Direct Endpoints for smslenz.lk SMS Actions
Send SMS
POSTGETUse this endpoint directly to send SMS to a contact number.
Request
URL: https://mail.smslenz.lk/api/send-sms
Parameters
| Key | Required | Value |
|---|---|---|
user_id |
Required | User ID from your settings page. |
api_key |
Required | API key from your settings page. |
sender_id |
Required | Your approved Sender ID. Use SMSlenzDEMO for testing. This one is case sensitive. |
contact |
Required | The recipient, Should be in the format of +9476XXXXXXX. |
message |
Required | The message. Max: 1500 chars. |
contact_name |
Optional | Name to save with the contact (max 255 chars). Only applied when the contact is new or has no name yet, so names you edited in your dashboard are never overwritten. |
Sample Request Body (POST · JSON)
{
"user_id": "12",
"api_key": "YOUR_API_KEY",
"sender_id": "SMSlenzDEMO",
"contact": "+9476XXXXXXX",
"message": "Welcome to SMSlenz!",
"contact_name": "Kamal"
}
Sample GET Request URL
https://mail.smslenz.lk/api/send-sms?user_id=12&api_key=YOUR_API_KEY&sender_id=SMSlenzDEMO&contact=%2B9476XXXXXXX&message=Welcome%20to%20SMSlenz%21
Query values must be URL-encoded — note the + in the contact number is sent as %2B.
Code Examples
curl -X POST "https://mail.smslenz.lk/api/send-sms" \
-d "user_id=12" \
-d "api_key=YOUR_API_KEY" \
-d "sender_id=SMSlenzDEMO" \
-d "contact=+9476XXXXXXX" \
-d "message=Welcome to SMSlenz!"
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://mail.smslenz.lk/api/send-sms");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'user_id' => '12',
'api_key' => 'YOUR_API_KEY',
'sender_id' => 'SMSlenzDEMO',
'contact' => '+9476XXXXXXX',
'message' => 'Welcome to SMSlenz!',
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
fetch("https://mail.smslenz.lk/api/send-sms", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
user_id: "12",
api_key: "YOUR_API_KEY",
sender_id: "SMSlenzDEMO",
contact: "+9476XXXXXXX",
message: "Welcome to SMSlenz!"
})
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
import requests
url = "https://mail.smslenz.lk/api/send-sms"
payload = {
"user_id": "12",
"api_key": "YOUR_API_KEY",
"sender_id": "SMSlenzDEMO",
"contact": "+9476XXXXXXX",
"message": "Welcome to SMSlenz!"
}
response = requests.post(url, data=payload)
print(response.json())
Response
You will get a JSON response in a success attempt.
Content-Type: application/json
Body:
{
"success": true,
"message": "SMS sent successfully",
"data": {
"status": "success",
"campaign_id": 42,
"message": "Welcome to SMSlenz!",
"sender_id": "SMSlenzDEMO",
"pages": 1,
"recipient_number": "9475XXXXXXX",
"sms_credit_balance": "2,513.95",
"charged_from": "main"
}
}
sms_credit_balance is the remaining balance of the pool the sender ID draws from: its allocated credit when the sender has an active credit allocation, otherwise your main balance. charged_from tells you which (allocation or main).
Send Bulk SMS
POSTUse this endpoint directly to send SMS to multiple contact numbers.
Request
URL: https://mail.smslenz.lk/api/send-bulk-sms
Parameters
| Key | Required | Value |
|---|---|---|
user_id |
Required | User ID from your settings page. |
api_key |
Required | API key from your settings page. |
sender_id |
Required | Your approved Sender ID. Use SMSlenzDEMO for testing. This one is case sensitive. |
contacts |
Required | The number of the recipients, Should be in the format of array["+9476XXXXXXX","+9475XXXXXXX"]. |
message |
Required | The message. Max: 1500 chars. |
Sample Request Body (POST · JSON)
{
"user_id": "12",
"api_key": "YOUR_API_KEY",
"sender_id": "SMSlenzDEMO",
"contacts": ["+9476XXXXXXX", "+9475XXXXXXX"],
"message": "Welcome to bulk sms API!"
}
This endpoint accepts POST only — send the body as JSON (Content-Type: application/json) or as regular form fields.
Code Examples
curl -X POST "https://mail.smslenz.lk/api/send-bulk-sms" \
-H "Content-Type: application/json" \
-d '{
"user_id": "12",
"api_key": "YOUR_API_KEY",
"sender_id": "SMSlenzDEMO",
"contacts": ["+9476XXXXXXX", "+9475XXXXXXX"],
"message": "Welcome to bulk sms API!"
}'
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://mail.smslenz.lk/api/send-bulk-sms");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'user_id' => '12',
'api_key' => 'YOUR_API_KEY',
'sender_id' => 'SMSlenzDEMO',
'contacts' => ['+9476XXXXXXX', '+9475XXXXXXX'],
'message' => 'Welcome to bulk sms API!',
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
fetch("https://mail.smslenz.lk/api/send-bulk-sms", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
user_id: "12",
api_key: "YOUR_API_KEY",
sender_id: "SMSlenzDEMO",
contacts: ["+9476XXXXXXX", "+9475XXXXXXX"],
message: "Welcome to bulk sms API!"
})
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
import requests
url = "https://mail.smslenz.lk/api/send-bulk-sms"
payload = {
"user_id": "12",
"api_key": "YOUR_API_KEY",
"sender_id": "SMSlenzDEMO",
"contacts": ["+9476XXXXXXX", "+9475XXXXXXX"],
"message": "Welcome to bulk sms API!"
}
response = requests.post(url, json=payload)
print(response.json())
Response
You will get a JSON response in a success attempt.
Content-Type: application/json
Body:
{
"success": true,
"message": "SMS sent successfully",
"data": {
"status": "success",
"campaign_id": 43,
"message": "Welcome to bulk sms API!",
"sender_id": "SMSlenzDEMO",
"pages": 1,
"no_of_recipients": 2,
"sms_credit_balance": "2,512.77",
"charged_from": "main"
}
}
sms_credit_balance is the remaining balance of the pool the sender ID draws from: its allocated credit when the sender has an active credit allocation, otherwise your main balance. charged_from tells you which (allocation or main).
Account Status
POSTGETUse this endpoint to check the status of your account.
Request
URL: https://mail.smslenz.lk/api/account-status
Parameters
| Key | Required | Value |
|---|---|---|
user_id |
Required | User ID from your settings page. |
api_key |
Required | API key from your settings page. |
Sample Request Body (POST · JSON)
{
"user_id": "12",
"api_key": "YOUR_API_KEY"
}
Sample GET Request URL
https://mail.smslenz.lk/api/account-status?user_id=12&api_key=YOUR_API_KEY
Code Examples
curl -X POST "https://mail.smslenz.lk/api/account-status" \
-d "user_id=12" \
-d "api_key=YOUR_API_KEY"
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://mail.smslenz.lk/api/account-status");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'user_id' => '12',
'api_key' => 'YOUR_API_KEY',
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
fetch("https://mail.smslenz.lk/api/account-status", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
user_id: "12",
api_key: "YOUR_API_KEY"
})
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
import requests
url = "https://mail.smslenz.lk/api/account-status"
payload = {
"user_id": "12",
"api_key": "YOUR_API_KEY"
}
response = requests.post(url, data=payload)
print(response.json())
Response
You will get a JSON response in a success attempt.
Content-Type: application/json
Body:
{
"success": true,
"message": "Account status retrieved successfully",
"data": {
"status": "active",
"sms_credit_balance": "2,515.72",
"allocated_credit_total": "1,500.00",
"sender_id_balances": [
{
"sender_id": "YOURBRAND",
"allocated_balance": "1,500.00",
"active": true
}
],
"active_plan": "Pay As You Go",
"total_contacts": 3,115,
"total_sender_ids": 6
}
}
sms_credit_balance is your main balance. sender_id_balances lists sender IDs holding a credit allocation; a sender with an active allocation is charged only from its allocated balance.
Save Contact
POSTUse this endpoint to add a contact to your account without sending an SMS. Saving a number that already exists is safe: you get the existing contact back.
Request
URL: https://mail.smslenz.lk/api/save-contact
Parameters
| Key | Required | Value |
|---|---|---|
user_id |
Required | User ID from your settings page. |
api_key |
Required | API key from your settings page. |
contact |
Required | A Sri Lankan mobile number, e.g. +9476XXXXXXX or 076XXXXXXX. |
name |
Optional | Contact name (max 255 chars). Only applied when the contact is new or has no name yet. |
group |
Optional | A contact group name (max 255 chars). The contact is added to it, and the group is created if you don't have it yet. Every contact is also added to "All Contacts". |
Sample Request Body (POST · JSON)
{
"user_id": "12",
"api_key": "YOUR_API_KEY",
"contact": "+9476XXXXXXX",
"name": "Kamal Perera",
"group": "VIP Customers"
}
Code Examples
curl -X POST "https://mail.smslenz.lk/api/save-contact" \
-d "user_id=12" \
-d "api_key=YOUR_API_KEY" \
--data-urlencode "contact=+9476XXXXXXX" \
--data-urlencode "name=Kamal Perera" \
--data-urlencode "group=VIP Customers"
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://mail.smslenz.lk/api/save-contact");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'user_id' => '12',
'api_key' => 'YOUR_API_KEY',
'contact' => '+9476XXXXXXX',
'name' => 'Kamal Perera',
'group' => 'VIP Customers',
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
fetch("https://mail.smslenz.lk/api/save-contact", {
method: "POST",
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
user_id: "12",
api_key: "YOUR_API_KEY",
contact: "+9476XXXXXXX",
name: "Kamal Perera",
group: "VIP Customers"
})
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
import requests
url = "https://mail.smslenz.lk/api/save-contact"
payload = {
"user_id": "12",
"api_key": "YOUR_API_KEY",
"contact": "+9476XXXXXXX",
"name": "Kamal Perera",
"group": "VIP Customers"
}
response = requests.post(url, data=payload)
print(response.json())
Response
HTTP 201 when a new contact was created, 200 when the number was already in your contacts.
Content-Type: application/json
Body:
{
"success": true,
"message": "Contact saved successfully",
"data": {
"contact_id": 1024,
"name": "Kamal Perera",
"phone": "+9476XXXXXXX",
"created": true,
"groups": ["All Contacts", "VIP Customers"]
}
}
groups lists every group the contact belongs to.
Errors
| HTTP status | Meaning |
|---|---|
400 |
Invalid contact number format. |
401 |
Invalid API key or user ID. |
403 |
Your account is suspended. |
422 |
A parameter is missing or invalid. The errors field says which. |
429 |
Too many requests. Please slow down. |
Reload Credit
POSTUse this endpoint to top up your SMS credit from your own system. It creates a payment link; whoever pays (you, your staff or your customer) opens it and pays by card on PayHere's secure checkout. Card details never pass through your system or ours.
How it works
- Call this endpoint. You get an
order_idand apayment_url. - Open the
payment_urlin a browser, or redirect your user to it. It takes them to card checkout. No SMSlenz login is needed. - Get the result: pass a
callback_urland we'll POST it to your server (see Payment Callbacks), or check Payment Status. The credit is added to your account as soon as the payment completes.
Request
URL: https://mail.smslenz.lk/api/reload-credit
Parameters
| Key | Required | Value |
|---|---|---|
user_id |
Required | User ID from your settings page. |
api_key |
Required | API key from your settings page. |
amount |
Required | Amount to reload in rupees, as a whole number between 1000 and 100000. |
sender_id |
Optional | One of your approved Sender IDs. Once paid, the whole amount is allocated to this sender instead of your main balance. Case sensitive. |
client_reference |
Optional | Your own reference, such as an invoice number (max 64 chars). Sending the same value again returns the existing payment link instead of creating a new one, so retrying a request is safe. |
return_url |
Optional | An https:// page to send the payer back to after paying or cancelling. Without it, they see an SMSlenz confirmation page. |
callback_url |
Optional | A public https:// URL on your server. We POST the result to it when this payment is paid or its link expires. See Payment Callbacks. |
Sample Request Body (POST · JSON)
{
"user_id": "12",
"api_key": "YOUR_API_KEY",
"amount": 5000,
"sender_id": "YOURBRAND",
"client_reference": "INV-1001",
"return_url": "https://example.com/payment-complete",
"callback_url": "https://example.com/smslenz-callback"
}
This endpoint accepts POST only — send the body as JSON (Content-Type: application/json) or as regular form fields.
Code Examples
curl -X POST "https://mail.smslenz.lk/api/reload-credit" \
-H "Content-Type: application/json" \
-d '{
"user_id": "12",
"api_key": "YOUR_API_KEY",
"amount": 5000,
"client_reference": "INV-1001",
"callback_url": "https://example.com/smslenz-callback"
}'
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://mail.smslenz.lk/api/reload-credit");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'user_id' => '12',
'api_key' => 'YOUR_API_KEY',
'amount' => 5000,
'client_reference' => 'INV-1001',
'callback_url' => 'https://example.com/smslenz-callback',
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
// Save $response['data']['order_id'], then send the payer to checkout:
header('Location: ' . $response['data']['payment_url']);
fetch("https://mail.smslenz.lk/api/reload-credit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
user_id: "12",
api_key: "YOUR_API_KEY",
amount: 5000,
client_reference: "INV-1001",
callback_url: "https://example.com/smslenz-callback"
})
})
.then(res => res.json())
.then(data => {
// Save data.data.order_id, then open checkout
window.location.href = data.data.payment_url;
})
.catch(err => console.error(err));
import requests
url = "https://mail.smslenz.lk/api/reload-credit"
payload = {
"user_id": "12",
"api_key": "YOUR_API_KEY",
"amount": 5000,
"client_reference": "INV-1001",
"callback_url": "https://example.com/smslenz-callback"
}
data = requests.post(url, json=payload).json()
print(data["data"]["order_id"], data["data"]["payment_url"])
Response
You will get a JSON response with status 201 when a new link is created, or 200 when client_reference matches an existing one.
Content-Type: application/json
Body:
{
"success": true,
"message": "Payment link created",
"data": {
"order_id": "PRU3FTX7HUJP8RAHY4",
"amount": "5,000.00",
"status": "pending",
"last_attempt": null,
"client_reference": "INV-1001",
"sender_id": "YOURBRAND",
"allocation_status": null,
"callback_url": "https://example.com/smslenz-callback",
"expires_at": "2026-10-08T15:30:00+05:30",
"paid_at": null,
"payment_url": "https://mail.smslenz.lk/pay/PRU3FTX7HUJP8RAHY4?signature=3f9c…"
}
}
- The
payment_urlworks for 60 minutes (untilexpires_at). If a payment is cancelled or declined, the same link can be opened again to retry. - With a
return_url, the payer is sent back to it with?order_id=…&status=…added. Treat that as a hint only: rely on your callback or Payment Status before acting on it.
Errors
| HTTP status | Meaning |
|---|---|
401 |
Invalid API key or user ID. |
403 |
Your account is suspended. |
409 |
The client_reference was already used for a different amount or sender ID. |
422 |
A parameter is invalid, your account has no active package, or the sender ID can't receive credit. The message field says which. |
429 |
Too many requests. You can create up to 10 payment links per minute. |
Payment Status
POSTUse this endpoint to check whether a Reload Credit payment has completed. If you don't use a callback_url, check after the payer returns from checkout, and keep checking every few seconds until the status is paid or expired.
Request
URL: https://mail.smslenz.lk/api/payment-status
Parameters
| Key | Required | Value |
|---|---|---|
user_id |
Required | User ID from your settings page. |
api_key |
Required | API key from your settings page. |
order_id |
Required | The order_id returned by Reload Credit. |
Sample Request Body (POST · JSON)
{
"user_id": "12",
"api_key": "YOUR_API_KEY",
"order_id": "PRU3FTX7HUJP8RAHY4"
}
Code Examples
curl -X POST "https://mail.smslenz.lk/api/payment-status" \
-d "user_id=12" \
-d "api_key=YOUR_API_KEY" \
-d "order_id=PRU3FTX7HUJP8RAHY4"
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://mail.smslenz.lk/api/payment-status");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'user_id' => '12',
'api_key' => 'YOUR_API_KEY',
'order_id' => 'PRU3FTX7HUJP8RAHY4',
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($response['data']['status'] === 'paid') {
// Credit has been added to your account
}
fetch("https://mail.smslenz.lk/api/payment-status", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
user_id: "12",
api_key: "YOUR_API_KEY",
order_id: "PRU3FTX7HUJP8RAHY4"
})
})
.then(res => res.json())
.then(data => console.log(data.data.status))
.catch(err => console.error(err));
import requests
url = "https://mail.smslenz.lk/api/payment-status"
payload = {
"user_id": "12",
"api_key": "YOUR_API_KEY",
"order_id": "PRU3FTX7HUJP8RAHY4"
}
response = requests.post(url, data=payload)
print(response.json()["data"]["status"])
Response
You will get a JSON response in a success attempt.
Content-Type: application/json
Body:
{
"success": true,
"message": "Payment status retrieved successfully",
"data": {
"order_id": "PRU3FTX7HUJP8RAHY4",
"amount": "5,000.00",
"status": "paid",
"last_attempt": null,
"client_reference": "INV-1001",
"sender_id": "YOURBRAND",
"allocation_status": "allocated",
"callback_url": "https://example.com/smslenz-callback",
"expires_at": "2026-10-08T15:30:00+05:30",
"paid_at": "2026-10-08T14:42:10+05:30",
"callback_status": "delivered",
"sms_credit_balance": "2,515.72",
"sender_id_balance": "6,500.00"
}
}
| Field | Meaning |
|---|---|
status |
pending (not paid yet), paid (credit added), or expired (the link ran out unpaid — create a new one). |
last_attempt |
While pending: cancelled or failed if the payer's last try didn't go through, otherwise null. |
allocation_status |
When a sender_id was given: allocated, or skipped if the sender couldn't receive credit by the time the payment completed (the credit went to your main balance instead). |
callback_status |
When a callback_url was given: pending (being sent or retried), delivered, or failed (gave up after the last retry). null until there is something to send. |
sms_credit_balance |
Your main balance now. |
sender_id_balance |
Only when a sender_id was given: that sender's allocated balance now. |
Unknown order IDs, and orders belonging to another account, return 404.
Payment Callbacks
POSTPass a callback_url to Reload Credit and we'll POST the result to your server when that payment is paid or its link expires, so you don't need to keep checking Payment Status. Each payment can use a different callback_url.
Events
| Event | Sent when |
|---|---|
payment.paid |
The payment completed and the credit was added (or allocated). |
payment.expired |
The payment link ran out without being paid. |
What we send
A POST with a JSON body (Content-Type: application/json) and these headers:
| Header | Value |
|---|---|
X-SMSlenz-Event |
The event, e.g. payment.paid. |
X-SMSlenz-Event-Id |
Unique per event. Retries of the same event reuse it. |
X-SMSlenz-Signature |
t=<unix timestamp>,v1=<signature> — see Verifying a callback below. |
{
"event": "payment.paid",
"event_id": "evt_01k6ztg3m8q2v7r4c1x9n5b0wd",
"created_at": "2026-10-08T14:42:11+05:30",
"data": {
"order_id": "PRU3FTX7HUJP8RAHY4",
"amount": "5,000.00",
"status": "paid",
"last_attempt": null,
"client_reference": "INV-1001",
"sender_id": "YOURBRAND",
"allocation_status": "allocated",
"callback_url": "https://example.com/smslenz-callback",
"expires_at": "2026-10-08T15:30:00+05:30",
"paid_at": "2026-10-08T14:42:10+05:30",
"sms_credit_balance": "2,515.72",
"sender_id_balance": "6,500.00"
}
}
data has the same fields as the Payment Status response.
Responding
- Reply with any
2xxstatus within 10 seconds. Do slow work after replying. - Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then we stop. Redirects are not followed.
- The same event can arrive more than once. Process each
event_idonly once. - Payment Status is always the source of truth, so you can still check it if a callback doesn't arrive.
Verifying a callback
The v1 signature is an HMAC-SHA256 of {t}.{raw request body}, keyed with your API key. Compute it yourself and compare; also reject callbacks whose t is more than 5 minutes old. If you regenerate your API key, callbacks sent after that are signed with the new key.
<?php
$body = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_SMSLENZ_SIGNATURE'] ?? ''), $sig);
$expected = hash_hmac('sha256', ($sig['t'] ?? '') . '.' . $body, 'YOUR_API_KEY');
if (! hash_equals($expected, $sig['v1'] ?? '') || abs(time() - (int) ($sig['t'] ?? 0)) > 300) {
http_response_code(400);
exit;
}
$event = json_decode($body, true);
// Skip $event['event_id'] if you've already processed it, then:
if ($event['event'] === 'payment.paid') {
// $event['data']['order_id'] is paid
}
http_response_code(200);
const crypto = require("crypto");
const express = require("express");
const app = express();
// Use the raw body: the signature covers the exact bytes we sent.
app.post("/smslenz-callback", express.raw({ type: "application/json" }), (req, res) => {
const sig = Object.fromEntries(
(req.get("X-SMSlenz-Signature") || "").split(",").map(part => part.split("="))
);
const expected = crypto.createHmac("sha256", "YOUR_API_KEY")
.update(`${sig.t}.${req.body}`)
.digest("hex");
const valid = sig.v1 && sig.v1.length === expected.length
&& crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig.v1))
&& Math.abs(Date.now() / 1000 - Number(sig.t)) <= 300;
if (!valid) return res.sendStatus(400);
const event = JSON.parse(req.body);
// Skip event.event_id if you've already processed it, then handle event.event
res.sendStatus(200);
});
import hashlib, hmac, json, time
from flask import Flask, abort, request
app = Flask(__name__)
@app.post("/smslenz-callback")
def smslenz_callback():
body = request.get_data() # raw bytes, exactly as sent
sig = dict(part.split("=", 1) for part in request.headers.get("X-SMSlenz-Signature", "").split(",") if "=" in part)
expected = hmac.new(b"YOUR_API_KEY", f"{sig.get('t')}.".encode() + body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig.get("v1", "")) or abs(time.time() - int(sig.get("t", 0))) > 300:
abort(400)
event = json.loads(body)
# Skip event["event_id"] if you've already processed it, then handle event["event"]
return "", 200
Find your user ID and API key on the API Settings page.
See something missing? Contact us and let us know. We are always happy to help with integrations.