Splashify RCS

Docs

Open panel
Panel

Check RCS capability

Before firing a broadcast or a one-off send, ask the gateway which of your recipients are actually RCS-capable. This lets you:

  • Save money, never spend rcs_per_msg_price on a number that RCS can't deliver to.
  • Route intelligently, send the unreachable slice down another channel while sending RCS to the rest.
  • Segment your list, build "RCS-ready" audiences by pre-checking your CRM export.

Endpoint

http
POST /api/v1/public/rcs/check-capability
Authorization: Bearer <api_key>
Content-Type: application/json
X-Bot-Id: <bot_uuid>              ← multi-bot accounts only

Picking a bot (multi-bot accounts)

Number checks work only with an active bot that supports them. Not every bot does; this endpoint refuses the others with 400 unsupported_provider_for_public_api.

If your account has more than one such bot, reachability depends on which bot's carriers you're checking against, bot A may be launched on Airtel but not Vi, bot B may be launched on both. You tell the endpoint which bot to check via one of two inputs (the header wins when both are set):

Without a bot, the endpoint uses your default bot when it supports number checks, and otherwise your first active bot that does.

X-Bot-Id is allowed on cross-origin requests, so a browser app can send it too.

Errors specific to the bot picker:

Prerequisites

  • RCS must be enabled on your account (an admin has provisioned an RCS agent + key). If not, the endpoint returns 403 with code: "rcs_not_active".
  • An active bot that supports number checks. See Picking a bot.
  • The same Basic-auth API key, IP allowlist and per-key rate limit as every other /public/* endpoint.

Request, single number

json
{
  "phone": "+919876543210"
}

Request, bulk (up to 10,000)

json
{
  "phones": [
    "+919876543210",
    "+918765432109",
    "+917654321098"
  ]
}

Bulk sizing: any number of phones from 1 up to 10,000 per request. Above 10,000 the request is rejected with 400. The platform automatically picks the fastest lookup path based on how many phones you send:

The API always returns the same response shape (reachable[] + unreachable[]) whichever path fires.

If both phone and phones are set, they're merged and deduped.

Response

json
{
  "success": true,
  "total": 3,
  "reachable_count": 2,
  "reachable": [
    "+919876543210",
    "+917654321098"
  ],
  "unreachable": [
    "+918765432109"
  ]
}

Order is preserved from the request (after dedup), so you can align input and output positionally if you're carrying per-recipient metadata alongside the phones.

Error responses

Latency

  • Single-number lookups return in ~200-500ms.
  • Bulk lookups (up to 10,000) return in a single response after every batch resolves; expect ~2-6s wall-clock for a full 10k call.

Idempotency + freshness

Reachability is a snapshot in time, a user who switches phones or disables RCS on their handset today may be unreachable tomorrow.

Recommendation:

  • Ad-hoc sends (order shipped, OTP, support reply): skip the pre-check, the direct /rcs/messages/send path already handles unreachable recipients gracefully (returns success: false per number with a provider reason) and doesn't debit the wallet.
  • Bulk broadcasts: pre-check nightly. Cache the result on your side for 24h. If a bulk broadcast is spread over a day, re-check before each batch fire.
  • Contact-list segmentation: re-check monthly is enough for most audiences.

Example, curl (bulk)

bash
curl -X POST https://api.splashifypro.com/api/v1/public/rcs/check-capability \
  -H "Authorization: Bearer $SPLASHIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Bot-Id: 9c1b7f4e-2a11-4c8e-8e0e-3f5d1b2c9e01" \
  -d '{
    "phones": [
      "+919876543210",
      "+918765432109",
      "+917654321098"
    ]
  }'

Example, Node.js

text
const res = await fetch(
  "https://api.splashifypro.com/api/v1/public/rcs/check-capability",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SPLASHIFY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ phones: crmExport.map((c) => c.phone) }),
  }
);
const { reachable, unreachable } = await res.json();

// Route the reachable slice to RCS...
await sendRCSBroadcast({ audience: reachable });
// ...and send the rest down whichever channel you fall back to.
await sendFallbackBroadcast({ audience: unreachable });

Example, Python

text
import os, requests

resp = requests.post(
    "https://api.splashifypro.com/api/v1/public/rcs/check-capability",
    headers={"Authorization": f"Bearer {os.environ['SPLASHIFY_API_KEY']}"},
    json={"phones": [c["phone"] for c in crm_export]},
    timeout=30,
)
resp.raise_for_status()
data = resp.json()
reachable = data["reachable"]
unreachable = data["unreachable"]

Fail-open guarantee

If the upstream RCS network is momentarily unavailable (network blip, throttled), the /rcs/messages/send endpoint does NOT block on a capability pre-check. It attempts the send anyway. This endpoint is purely a cost-optimization tool; your critical send paths keep working even when the capability service is degraded.

Handling very large lists (10 lakh+)

The public API caps at 10,000 phones per HTTP call. For a 10-lakh (1M) audit, batch client-side into 100 calls of 10k each:

text
import os, requests
from itertools import islice

def chunk(iterable, size):
    it = iter(iterable)
    while batch := list(islice(it, size)):
        yield batch

all_phones = [c["phone"] for c in crm_export]  # 1,000,000 entries
reachable, unreachable = [], []

for batch in chunk(all_phones, 10000):
    resp = requests.post(
        "https://api.splashifypro.com/api/v1/public/rcs/check-capability",
        headers={"Authorization": f"Bearer {os.environ['SPLASHIFY_API_KEY']}"},
        json={"phones": batch},
        timeout=60,
    )
    resp.raise_for_status()
    data = resp.json()
    reachable.extend(data["reachable"])
    unreachable.extend(data["unreachable"])

print(f"{len(reachable):,} reachable of {len(all_phones):,}")

Each 10k call runs in 2-6 seconds, so a full 100-batch sweep of 1M phones completes in about 5-10 minutes. If you're checking a stored list that already lives on our platform, use the contact-list async check below instead. It runs server-side without your process having to stay online.

Server-side check for stored contact lists

If your recipients already live in one of your platform contact lists, the panel exposes an ASYNC check that runs the full list server-side regardless of size. Kick off with:

http
POST /api/v1/rcs/contact-lists/{list_id}/check-reachability
X-Bot-Id: <bot_uuid>            ← multi-bot accounts only

The bot comes from the X-Bot-Id header, then the ?bot_id= query, then {"bot_id": "…"} in the body. With none of them, your first active bot that supports number checks is used. Only such a bot can run the check. A bot that does not support them gets 400 with code: "bot_cannot_check", and a call with no bot on an account that has none gets 400 with code: "no_check_bot". See Contact lists.

The bot the check runs against is stamped on the list row as last_reachability_check_bot_id so you (and your admins) can see "reachable count = 850 checked via Store Bot" instead of a context-free number.

Response is 202 Accepted with the resolved bot info:

json
{
  "success":     true,
  "status":      "started",
  "total":       1000000,
  "started_at":  "2026-08-07T12:04:00Z",
  "bot_id":      "9c1b7f4e-2a11-4c8e-8e0e-3f5d1b2c9e01",
  "bot_name":    "Store Bot",
  "incremental":   false,
  "resumed":       false,
  "keeps_can_get": false
}
  • The first check of a list checks every number. total is the number of members on the list.
  • keeps_can_get is true when the list already had answers that count. The check then keeps every reachable answer and checks only the numbers found unreachable and the numbers with no answer that counts. This is the same with any bot, and with or without ?force=true. There is no way to check a reachable answer again.
  • incremental is true when only numbers without an answer are checked: the last check used the same bot, finished cleanly, and the call has no ?force=true. ?force=true turns that off; it no longer checks every number.
  • resumed is true when the last check with this bot stopped part way. The new check keeps the answers it saved and checks the rest.
  • A check with a different bot no longer checks every number: it follows the same rule as keeps_can_get.
  • While a check runs, another call gets 409 with code: "check_in_progress" and, when known, the current progress.
  • A check never blocks a broadcast. A broadcast sent while one runs uses the answers saved so far, and the answers from the check before it. On a list with no answers before the check, its answers count once it finishes: until then every number reads as not checked.

Poll the list via GET /api/v1/rcs/contact-lists (or read the list detail) to observe progress:

json
{
  "list_id": "…",
  "count": 1000000,
  "reachability_check_started_at": "2026-08-07T12:04:00Z",
  "reachability_check_progress":   420000,
  "reachability_check_phase":      "probing",
  "reachability_check_total":      1000000,
  "last_reachability_check_at":    null,
  "reachable_count":               null,
  "last_reachability_check_bot_id":   "9c1b7f4e-2a11-4c8e-8e0e-3f5d1b2c9e01",
  "last_reachability_check_bot_name": "Store Bot"
}

reachability_check_phase says where the check is:

When last_reachability_check_at >= reachability_check_started_at the job is complete and reachable_count reflects the final tally. Then use GET /api/v1/rcs/contact-lists/{list_id}/reachable.csv to download the reachable subset.

The answers count for broadcasts from every bot on your account, not only the bot that ran the check. A broadcast from any of them with audience: "rcs" (the default) skips the numbers the check found unreachable; not_rcs sends only to those, and all to every number. To see what a send from a given bot would reach, call the audience endpoint.

A list whose latest check was made with a bot that does not support number checks reads as never checked: the list row carries none of the fields above, and its members carry no reachable answer. The saved answers are not used by any broadcast.