Skip to content
Splashify Pro
Docs

Send SMS

Send one SMS to an Indian mobile number from your own system. It uses the same API key as the rest of the Public API, and one of your approved SMS templates.

This works for every kind of DLT SMS: promotional, transactional, service implicit (OTPs and alerts) and service explicit. You never choose the type in the request. It comes from the template, and the template also decides the sender ID and the price.

Note: SMS must be set up in the app and the SMS card must show Active. See Set up SMS. The template you send with must be Approved. See SMS templates.

Endpoint

POST

https://api.splashifypro.com/api/v1/public/sms/send

Headers

See Authentication for how to get your key.

Request body

json
{
  "to": "+919876543210",
  "dlt_template_id": "1107160000000000001",
  "variables": ["Aarav", "482913"]
}

Template variables

Your template text marks each part that changes with {#var#}, exactly as registered on your DLT portal. Send one value for each, in the order they appear.

A template registered as:

text
Dear {#var#}, your Acme Boutique login code is {#var#}. Do not share it.

sent with "variables": ["Aarav", "482913"] arrives as:

text
Dear Aarav, your Acme Boutique login code is 482913. Do not share it.
  • Each value can be up to 30 characters and cannot be empty. This is a DLT rule. Spaces at the start and end of a value are removed.
  • Send exactly as many values as the template has {#var#}. Too many or too few is refused with 400 and code: "invalid_variables", and nothing is sent.
  • Everything outside the variables must stay as registered. You cannot change the fixed text from the API.

Examples

cURL

bash
curl -X POST https://api.splashifypro.com/api/v1/public/sms/send \
  -H "Authorization: Basic YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: login-code-AB-1042" \
  -d '{
    "to": "+919876543210",
    "dlt_template_id": "1107160000000000001",
    "variables": ["Aarav", "482913"]
  }'

Node.js / TypeScript

text
async function sendLoginCode(phone: string, name: string, code: string) {
  const res = await fetch("https://api.splashifypro.com/api/v1/public/sms/send", {
    method: "POST",
    headers: {
      "Authorization": `Basic ${process.env.SPLASHIFY_API_KEY!}`,
      "Content-Type":  "application/json",
    },
    body: JSON.stringify({
      to:              phone,                 // "+919876543210"
      dlt_template_id: "1107160000000000001",
      variables:       [name, code],
    }),
  });

  const data = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${data.code ?? data.error ?? ""}: ${data.message ?? ""}`);
  if (data.status === "rejected") throw new Error(`Not sent: ${data.message}`);
  // "sent", or "queued" when it is not confirmed yet. Do not resend a queued SMS.
  return data.message_id; // keep it to match delivery reports
}

Python

python
import os, requests

def send_login_code(phone: str, name: str, code: str) -> str:
    r = requests.post(
        "https://api.splashifypro.com/api/v1/public/sms/send",
        headers={
            "Authorization": f"Basic {os.environ['SPLASHIFY_API_KEY']}",
            "Content-Type":  "application/json",
        },
        json={
            "to":              phone,
            "dlt_template_id": "1107160000000000001",
            "variables":       [name, code],
        },
        timeout=30,
    )
    data = r.json()
    if r.status_code != 200:
        raise RuntimeError(f"{r.status_code} {data.get('code') or data.get('error')}: {data.get('message')}")
    if data.get("status") == "rejected":
        raise RuntimeError(f"Not sent: {data.get('message')}")
    # "sent", or "queued" when it is not confirmed yet. Do not resend a queued SMS.
    return data["message_id"]

Successful response (200 OK)

json
{
  "success": true,
  "accepted": true,
  "message_id": "3f6c1a52-8d4e-4b7a-9c21-5e0f7a9b1c34",
  "status": "sent",
  "parts": 1,
  "unicode": false,
  "charged": 0.16,
  "balance_after": 1249.84
}

The price per part depends on the template's type. See Pricing and billing.

When the SMS is not accepted

A request that was correct but could not be sent still returns 200, with accepted: false, success: false and a message that says why. It is not charged.

json
{
  "success": false,
  "accepted": false,
  "message_id": "7b0e2c91-4f3a-4d86-8e57-2a9c6d1f0b48",
  "status": "rejected",
  "parts": 1,
  "unicode": false,
  "charged": 0,
  "balance_after": 0,
  "message": "That mobile number does not look valid. Check it and try again."
}

With status: "rejected", retrying straight away usually gets the same answer. Log the message, check the number, and try again later.

With status: "queued", we could not confirm the send yet. It is not charged now. Do not send it again, or the person may get it twice. Check it later with SMS status.

Retry safely

If your request times out or you get a 5xx, you cannot tell whether the SMS went. Send an Idempotency-Key header, and you can send the same request again with the same key:

  • If the first request went through, you get its answer back with "replayed": true and the header Idempotency-Replayed: true. Nothing is sent or charged again. This includes an answer with accepted: false, so to try again after fixing the problem, use a new key.
  • If the first request failed with an error status, the key is not kept, and the retry is a normal send.
  • If the first request is still running, you get 409 with code: "idempotency_in_progress". Try again shortly with the same key.
  • The same key with a different body gets 422 with code: "idempotency_key_reused".

A key is kept for 24 hours. Make one per SMS you decide to send, such as an order number, and reuse it on every retry of that SMS. See Idempotency.

Opt-outs

When someone asks not to get your messages, sends to their number are refused with 403 and code: "opted_out". Treat that as final and do not retry it. Nothing is sent or charged.

Errors

Errors from this endpoint look like this. Switch on code, and show message to a person:

json
{
  "success": false,
  "code": "sms_not_set_up",
  "message": "Set up SMS first."
}

Some errors come from the checks every Public API request goes through, and look different:

Rate limits

Two limits apply to this endpoint:

  • Your plan's rate limit for all Public API requests. Over it, you get 429 with result: false and a message.
  • 60 single sends a minute for your account. Over it, you get 429 with this body and a Retry-After: 60 header:
json
{
  "error": "rate_limit_exceeded",
  "retry_after": 60,
  "limit": 60
}

To send more than 60 a minute, use Send bulk SMS.

DLT in plain words

  • The type comes from the template. A promotional template sends a promotional SMS, and a service implicit template sends a service implicit SMS. You cannot change it per message.
  • The sender ID comes from the template. Each template is registered with one sender ID, such as ACMEOT.
  • Variables fill in order, up to 30 characters each.
  • Indian mobile numbers only.
  • Promotional SMS do not reach numbers on Do Not Disturb. Use a transactional or service template for messages people asked for.

Next