On-demand premium SMS lets you charge subscribers per-message for content they request — quizzes, sports scores, news clips, jokes, horoscopes. The customer texts a keyword to your shortcode, you reply with the paid content, and the carrier collects the fee on your behalf.

POST https://sozuri.net/api/v1/messaging/sendpremium
What you earn, and what it costs to start: shortcode setup and monthly fees, the volume-tiered revenue share, and a calculator that shows your net per message — see Premium SMS pricing. The same rates apply to both on-demand and subscription messages.

How the flow works

  1. The subscriber texts your keyword (e.g. GOAL) to your shortcode.
  2. Sozuri pushes an incoming webhook to your application with a linkId identifying this request.
  3. You reply via the API, passing the same linkId back — this is what triggers the premium charge.
  4. Sozuri sends a delivery webhook when the premium SMS reaches the subscriber and the carrier confirms the charge.
On-demand premium SMS requires an approved premium shortcode and registered keywords. Apply for both from your dashboard at Numbers › Apply for a shortcode.
Required headers
POST /api/v1/messaging/sendpremium HTTP/1.1
Host: sozuri.net
Authorization: Bearer Your_Project_API_KEY
Content-Type: application/json
Accept: application/json

Request parameters

FieldRequiredTypeDescription
projectYesStringThe Sozuri project that owns the API key.
fromYesStringYour premium shortcode (e.g. 23546).
numberYesStringThe subscriber’s phone number. 0722-503-129, +254722503129 and 722503129 all normalise to 254722503129. Anything that is not a Kenyan mobile number is refused with Unsupported Number.
channelNoStringAccepted and ignored. Kept for compatibility with older integrations.
typeNoStringAccepted and ignored — the product type comes from the offer itself, not the request.
messageYesStringThe premium content you’re sending the subscriber.
keywordRecommendedStringThe activation keyword of the offer you are replying on. Send it whenever one shortcode carries more than one premium offer — it is what tells us which one this message belongs to. premium_id is accepted as an alternative.
linkIdYesStringThe ID Sozuri delivered in the incoming-keyword webhook. Mandatory for billing this message as on-demand.
campaignNoStringOptional label for grouping reports.
apiKeyStringYour project API key. Recommended: pass as a Bearer token instead.

Response parameters

Synchronous JSON response confirming the premium message was accepted for delivery:

FieldTypeDescription
messageData.messagesNumberTotal messages accepted.
recipients[].messageIdStringUnique Sozuri ID for this message. This is the same id the delivery webhook reports, so store it to correlate the two.
recipients[].toStringThe recipient’s phone number.
recipients[].statusStringAcceptance status — accepted, unknown_number etc. Not the final delivery status.
recipients[].statusCodeStringNumeric status — see status codes.
recipients[].messagePartNumberSMS parts used. One GSM-7 part is 160 characters.
recipients[].keywordStringThe offer’s activation keyword.
recipients[].linkIdStringEchoes back the linkId this reply was billed against.
recipients[].typeStringAlways ondemand here.

Sample request

POST /api/v1/messaging/sendpremium HTTP/1.1
Host: sozuri.net
Authorization: Bearer LOx5JPdqf0lvf.......R9X9XDJ4PFxRqVrt9dx83cWiwfTQMF
Content-Type: application/json
Accept: application/json

{
    "project": "my project",
    "from": "23546",
    "number": "2547251642xx",
    "campaign": "Promo Nai",
    "channel": "premium",
    "message": "Test SMS.",
    "type": "ondemand",
    "linkId": "54785454",
    "keyword": "Omoka"
}
POST /api/v1/messaging/sendpremium HTTP/1.1
Host: sozuri.net
Authorization: Bearer LOx5JPdqf0lvf45EZAQMJ.......SUzyxR9X9XDJ4PFxRqVrt9dx83cWiwfTQMF
Content-Type: application/json
Accept: application/xml

<request>
    <project>my project</project>
    <from>23546</from>
    <number>2547251642xx</number>
    <campaign>Promo Nai</campaign>
    <channel>premium</channel>
    <message>Test SMS.</message>
    <type>ondemand</type>
    <linkId>54785454</linkId>
    <keyword>Omoka</keyword>
</request>
<?php

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_URL => "https://sozuri.net/api/v1/messaging/sendpremium",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_POSTFIELDS => json_encode([
        "project"  => "my project",
        "from"     => "23546",
        "to"       => "2547251642xx",
        "campaign" => "Promo Nai",
        "channel"  => "premium",
        "message"  => "Test SMS.",
        "type"     => "ondemand",
        "linkId"   => "54785454",
        "keyword"  => "Omoka",
    ]),
    CURLOPT_HTTPHEADER => [
        "Accept: application/json",
        "Authorization: Bearer LOx5JPdqf0lvf45EZAQMJm85OSUzyxR9X9XDJ4PFxRqVrt9dx83cWiwfTQMF",
        "Content-Type: application/json",
    ],
]);
echo curl_exec($curl);
curl_close($curl);
const response = await fetch("https://sozuri.net/api/v1/messaging/sendpremium", {
    method: "POST",
    headers: {
        "Authorization": "Bearer LOx5JPdqf0lvf45EZAQMJm85OSUzyxR9X9XDJ4PFxRqVrt9dx83cWiwfTQMF",
        "Content-Type": "application/json",
        "Accept": "application/json"
    },
    body: JSON.stringify({
        project: "my project",
        from: "23546",
        number: "2547251642xx",
        campaign: "Promo Nai",
        channel: "premium",
        message: "Test SMS.",
        type: "ondemand",
        linkId: "54785454",
        keyword: "Omoka"
    })
});
console.log(await response.json());
require 'uri'
require 'net/http'
require 'json'

uri  = URI("https://sozuri.net/api/v1/messaging/sendpremium")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer LOx5JPdqf0lvf45EZAQMJm85OSUzyxR9X9XDJ4PFxRqVrt9dx83cWiwfTQMF"
request["Content-Type"]  = "application/json"
request["Accept"]        = "application/json"
request.body = {
    project: "my project", from: "23546", number: "2547251642xx",
    campaign: "Promo Nai", channel: "premium", message: "Test SMS.",
    type: "ondemand", linkId: "54785454", keyword: "Omoka"
}.to_json

puts http.request(request).body
import requests

response = requests.post(
    "https://sozuri.net/api/v1/messaging/sendpremium",
    headers={
        "Authorization": "Bearer LOx5JPdqf0lvf45EZAQMJm85OSUzyxR9X9XDJ4PFxRqVrt9dx83cWiwfTQMF",
        "Content-Type": "application/json",
        "Accept": "application/json",
    },
    json={
        "project": "my project", "from": "23546", "number": "2547251642xx",
        "campaign": "Promo Nai", "channel": "premium", "message": "Test SMS.",
        "type": "ondemand", "linkId": "54785454", "keyword": "Omoka",
    },
)
print(response.json())
HttpResponse<String> response = Unirest.post("https://sozuri.net/api/v1/messaging/sendpremium")
    .header("Authorization", "Bearer LOx5JPdqf0lvf45EZAQMJm85OSUzyxR9X9XDJ4PFxRqVrt9dx83cWiwfTQMF")
    .header("Content-Type",  "application/json")
    .header("Accept",        "application/json")
    .body("{\"project\":\"my project\",\"from\":\"23546\",\"to\":\"2547251642xx\","
        + "\"campaign\":\"Promo Nai\",\"channel\":\"premium\",\"message\":\"Test SMS.\","
        + "\"type\":\"ondemand\",\"linkId\":\"54785454\",\"keyword\":\"Omoka\"}")
    .asString();
var client  = new RestClient("https://sozuri.net/api/v1/messaging/sendpremium");
var request = new RestRequest(Method.POST);
request.AddHeader("Accept",        "application/json");
request.AddHeader("Content-Type",  "application/json");
request.AddHeader("Authorization", "Bearer LOx5JPdqf0lvf45EZAQMJm85OSUzyxR9X9XDJ4PFxRqVrt9dx83cWiwfTQMF");
request.AddParameter("application/json",
    "{\"project\":\"my project\",\"from\":\"23546\",\"to\":\"2547251642xx\","
  + "\"campaign\":\"Promo Nai\",\"channel\":\"premium\",\"message\":\"Test SMS.\","
  + "\"type\":\"ondemand\",\"linkId\":\"54785454\",\"keyword\":\"Omoka\"}",
    ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
curl -X POST https://sozuri.net/api/v1/messaging/sendpremium \
    -H 'Content-Type: application/json' \
    -H 'Accept: application/json' \
    -H 'Authorization: Bearer LOx5JPdqf0lvf45EZAQMJm85OSUzyxR9X9XDJ4PFxRqVrt9dx83cWiwfTQMF' \
    -d '{
        "project": "my project",
        "from": "23546",
        "number": "2547251642xx",
        "campaign": "Promo Nai",
        "channel": "premium",
        "message": "Test SMS.",
        "type": "ondemand",
        "linkId": "54785454",
        "keyword": "Omoka"
    }'
Sample JSON response
{
    "messageData": { "messages": 1 },
    "recipients": [
        {
            "messageId": "MSGBLK6012A7E8B90A21611835368",
            "number": "2547251642xx",
            "status": "accepted",
            "statusCode": "11",
            "keyword": "Omoka",
            "messagePart": 1,
            "type": "ondemand"
        }
    ]
}

Incoming keyword webhook

When a subscriber texts your keyword to your premium shortcode, Sozuri pushes this webhook to the callback URL on the premium service (set it under Premium Services; a comma-separated list is allowed and each URL receives a copy). Capture the linkId — your reply must quote it, and it is the only thing that authorises the charge.

{
    "project": "yourproject_name",
    "shortcode": "20555",
    "keyword": "QUIZ",
    "number": "254748250794",
    "messageId": "00073110187845156614979911",
    "network": "safaricom",
    "type": "linkNotification",
    "linkId": "00073110187845156614979911",
    "message": "QUIZ",
    "status": "QUIZ",
    "timestamp": 1774448390,
    "authKey": "your_project_auth_key"
}
A linkId is short-lived. Reply while the subscriber is still expecting the content — a stale one is refused with carrier code 858 (LinkId Expired or Does Not Exist) and nothing is charged. Every request and its reply, and which requests are still unanswered, are on the On-demand premium page in your dashboard.

Delivery status webhook

Sozuri sends a delivery callback when the carrier confirms (or rejects) your premium message:

{
    "project": "yourproject_name",
    "shortcode": "20555",
    "keyword": "QUIZ",
    "number": "254748250794",
    "messageId": "SOZPREM68B6F1A2C3D4E",
    "network": "safaricom",
    "type": "premiumDelivery",
    "status": "DeliveryToTerminal",
    "timestamp": 1774448390,
    "authKey": "your_project_auth_key"
}

messageId is the recipients[].messageId the send returned, so the two can be joined directly. status is the carrier's own delivery description — DeliveryToTerminal means delivered and charged; an absent-subscriber or delivery-impossible description means it was not.

Error responses

On-demand premium SMS uses the SDP error envelope: { "status": "error", "description": "…" }, always with HTTP 200. Auth errors (Unknown project.) follow the SMS envelope and are documented on the Authentication page.

descriptionWhat it means
Unknown or Inactive Premium serviceNo active premium offer matches the from shortcode / keyword you sent.
linkId is required for an on-demand premium messageOn-demand replies must quote the linkId from the incoming webhook.
Unknown link idWe have no record of that linkId for this offer. Check you are sending the one from the most recent request.
Unsupported NumberNot a Kenyan mobile number.
The link id has expired… (carrier code 858)The carrier refused the charge because the session had closed. Ask the subscriber to text the keyword again.
The subscriber does not have enough airtime… (carrier code 826)Nothing was charged and nothing was delivered.
This shortcode is blacklisted by the carrier (carrier code 827)Contact support — no message on this shortcode will be accepted.

Where a carrier code is shown it is Safaricom's own statusCode, passed through so you can branch on it.

ConditionHTTPResponse body
Keyword/shortcode pair doesn’t match an active premium service on this project. 400
{
    "status": "error",
    "description": "Unknown, Expired, or Inactive Premium Service"
}
The linkId on a paid reply doesn’t match an incoming-keyword event we issued. 400
{
    "status": "error",
    "description": "wrong link id"
}
Offer reference not found (e.g. you sent an offer_code that’s not configured). 400
{
    "status": "error",
    "statusCode": 1,
    "description": "Offer not found"
}
Destination phone number is malformed or not in supported E.164 format. 400
{
    "status": "error",
    "description": "Unsupported Number"
}
For status code reference see Status codes & response envelopes. For asynchronous delivery callbacks see Delivery webhook statuses.

Use cases

Where on-demand premium SMS earns its keep.

Sports scores & alerts

Subscriber texts GOAL to get the latest score on demand — charge per request, no app required.

23546
SportsZone
GOAL 20:14
Man City 2-1 Arsenal (78'). Haaland scored from a penalty. Send GOAL for next update. 20:14

News & horoscopes

Daily snippets of paid content on demand — the foundation of the original mobile content businesses.

23546
Daily Horoscope
LEO 07:01
Leo: Today brings unexpected good fortune in work matters. Trust your instincts. 07:01

Quizzes & trivia

Pay-per-question trivia campaigns on radio or TV — the keyword becomes the call-to-action.

🎯
23546
Drive-Time Trivia
Q3: Which country won the 2024 AFCON?
A - Nigeria
B - Cote d’Ivoire
C - South Africa 08:42
B 08:43
Correct! You’ve banked KES 200 in airtime. Next question at 09:00. Keep listening! 08:43

Monetise your content via SMS.

Apply for a premium shortcode and we&rsquo;ll handle billing, settlement and carrier paperwork end-to-end.