Contact lists
Team-member access: operators can create + delete lists and add or remove members; viewers can list + get but get
403 not_operatoron any mutation. Lists aren't scoped per bot, every member sees every list on the account. See the Team members reference.
A contact list is a named bag of recipient phone numbers. Every
broadcast picks one list and an
audience: rcs (the default)
leaves out the numbers the list's check found unreachable, not_rcs
sends only to those, and all sends to every number. Numbers that opted
out are never sent. See Audience.
A check is optional. Number checks work only with an active bot that supports them. Not every bot does. Through the API, a check needs your own active bot that supports number checks. The answers of a check count for broadcasts from every bot on your account, including bots that cannot check. A list that was never checked goes to every number except the people who opted out; numbers that cannot get RCS fail and are not charged.
POST /api/v1/rcs/contact-lists
GET /api/v1/rcs/contact-lists
DELETE /api/v1/rcs/contact-lists/{id}
POST /api/v1/rcs/contact-lists/{id}/members
GET /api/v1/rcs/contact-lists/{id}/members
DELETE /api/v1/rcs/contact-lists/{id}/members/{phone}
POST /api/v1/rcs/contact-lists/{id}/import # CSV import
POST /api/v1/rcs/contact-lists/{id}/check-reachability
GET /api/v1/rcs/contact-lists/{id}/audience?bot_id={bot_id}
DELETE /api/v1/rcs/contact-lists/{id}/unreachable
GET /api/v1/rcs/contact-lists/{id}/reachable.csv
POST /api/v1/rcs/contact-lists/{id}/create-reachable-listCreate a list
POST /api/v1/rcs/contact-lists
{ "name": "May launch, opted in" }Response:
{
"success": true,
"list_id": "a1b2…",
"name": "May launch, opted in",
"count": 0,
"created_at": "2026-05-27T09:14:00Z"
}Add members (bulk)
CSV-style uploads are passed as JSON. Phones follow the same rule as
Import CSV below; a phone holding several numbers adds each one.
POST /api/v1/rcs/contact-lists/{id}/members
{
"members": [
{ "phone": "+919876543210", "name": "Asha", "extra": { "city": "Mumbai" } },
{ "phone": "+919876543211", "name": "Rohan", "extra": { "city": "Pune" } }
]
}Up to 100,000 members per request. Members already in the list overwrite (idempotent, safe to retry on partial network failure).
Response:
{
"success": true,
"added": 2,
"skipped": 0,
"count": 2
}skipped counts numbers that are not a valid mobile, numbers Excel
changed into a short form (9.87654E+09), and rows that could not be saved.
List members
GET /api/v1/rcs/contact-lists/{id}/members?size=200size defaults to 200, max 1000. For huge lists prefer slicing by
phone prefix using the extra map at upload time.
A member carries reachable (true or false) only when the list's
answers that count stand behind it, the same answers the
audience counts and a broadcast
uses. A member with no answer that counts has no reachable. Answers
from a check made with a bot that does not support number checks are not
shown: those members read as not checked.
Delete a member
DELETE /api/v1/rcs/contact-lists/{id}/members/{phone}{phone} is the number with its + written as %2B. A number without a + is read
the same way as in Import CSV, so 9876543210 removes +919876543210.
Delete a list
DELETE /api/v1/rcs/contact-lists/{id}Removes the list plus every member. Broadcasts that already snapshotted this list at create time keep running. They don't read the list table after the snapshot.
Deleting a list also stops an import or a reachability check that is running on it.
Import CSV
Bulk-import a CSV of phones (and optional name / custom fields) into an existing list. Streams the upload, the file is never loaded fully in memory.
POST /api/v1/rcs/contact-lists/{id}/import
Content-Type: multipart/form-dataForm fields:
Mapping schema
{
"phone_col": 0,
"name_col": 1,
"custom_cols": [
{ "index": 2, "name": "city" },
{ "index": 3, "name": "plan" }
]
}Rules:
phone_colis required and must appear exactly once. Any other role picking the same column returns a400.name_colis optional; at most one column can bename.custom_cols[]accepts up to 10 entries. Eachnamebecomes a key in the member'sextramap, so the operator can address the fields at broadcast render time. Custom column names are trimmed to 40 chars.
If the first row of the CSV has no digits in any cell it is treated as a header row and skipped from the insert.
Without a +, only an Indian mobile is accepted: 10 digits starting 6 to 9, optionally with 0, 91
or 0091 in front, saved as +91 and the 10 digits. With a +, any country is kept as written, but
+91 must be followed by a 10-digit mobile. Other rows are reported in malformed_rows and skipped.
Sample request
curl -X POST https://api.splashifypro.com/api/v1/rcs/contact-lists/<list_id>/import \
-H "Authorization: Bearer <panel_jwt>" \
-F "[email protected]" \
-F "dedup=true" \
-F 'mapping={"phone_col":0,"name_col":1,"custom_cols":[{"index":2,"name":"city"}]}'Sample CSV
phone,name,city
+919876543210,Asha,Mumbai
9876543211,Rohan,Pune
918293834982,Kiran,Bengaluru
9812345678,Priya,ChennaiResponse
{
"success": true,
"inserted": 4890,
"duplicates_skipped": 33,
"malformed_rows": [
{ "row": 42, "phone": "abc", "reason": "not a valid Indian mobile number" },
{ "row": 118, "phone": "98765432", "reason": "not a valid Indian mobile number" }
],
"elapsed_ms": 4218
}When dry_run=true, inserted is 0 and an additional
would_insert field carries the count that WOULD have been written.
Dedup
Access
Operators + owners can import. Viewers get 403 not_operator.
Check reachability
POST /api/v1/rcs/contact-lists/{id}/check-reachability?bot_id={bot_id}Starts a background check of the list's numbers and answers 202
at once. The check runs with one bot, and its answers count for
broadcasts from every bot on your account. Pass the bot as ?bot_id=,
the X-Bot-Id header, or {"bot_id": "…"} in the body.
Through the API, only your own active bot that supports number checks
can run one. Without a bot, your first active bot that supports them is
used. To know whether a given bot is one, read check_bot_ok from the
audience endpoint, or check the bots
on your account.
In the panel, an account with no bot that supports number checks can
still check its lists: we run the check for you, and the list's check
shows no bot name. This applies only in the panel. An API call from such
an account is refused as before: 400 no_check_bot, or
400 bot_cannot_check when it names a bot.
Which numbers a check covers:
There is no way to check a reachable answer again. To send to those
numbers anyway, create the broadcast with audience: "all".
You never have to wait for a check to send: a broadcast created or
started while a check runs uses the answers saved when its recipients
are prepared. While a check runs, and after one fails or stops part way,
the answers from the check before it still count, together with the
answers the new check has saved. On a list with no answers that count
before the check (has_answers was false), there is no check before
it: the new answers count once the check finishes, and until then every
number reads as not checked.
A check of 5,000 numbers or more runs in the background on the network's side and can take up to
an hour; poll the list as for any check. Numbers the network does not answer for stay unchecked, and
the check finishes with a note saying how many. When the bot is not live on the network yet, the
check stops at once and reachability_check_error says Your bot is not live on the network yet, so its numbers could not be checked. Check again after it goes live. No answer from that check is used.
An account runs at most two checks of 5,000 numbers or more at a time. Another one stops at once,
with nothing checked, and reachability_check_error says Too many number checks of big lists are running on your account. Run this one when one of them finishes. Start it again when one finishes.
While a check runs, a second call gets 409 check_in_progress. The
Check RCS capability
page has the 202 body and how to follow progress.
A bot that cannot check is refused before anything starts:
{
"success": false,
"error": "bot_cannot_check",
"code": "bot_cannot_check",
"message": "This bot cannot check numbers. Pick another bot."
}Audience: who a broadcast reaches
GET /api/v1/rcs/contact-lists/{id}/audience?bot_id={bot_id}Counts who a broadcast of this list from this bot would reach, with the
same rule the send uses, for each audience at once (choices). Call it
before you create a broadcast, to show the numbers or to catch a send
nobody would get.
The rcs rule, per number (sendable, choices.rcs):
- On your opt-out list, for the account or for this bot: not sent.
- Found unreachable by the list's check, made with any of your bots: not sent.
- Failed as not on RCS in a broadcast from this bot in the last 30 days: not sent, unless the list's check, made after that failure, found it reachable. A failure on another of your bots does not count. See Reachability filter.
- Everything else is sent. That includes numbers never checked.
choices.not_rcs counts only the numbers found unreachable, and
choices.all every number; neither has the 30-day skip, and opted-out
numbers are never counted in any of them.
Only a bot that supports number checks can make that check, but its answers count for a broadcast from any of your bots, including one that cannot check. A check made earlier with a bot that does not support number checks, or with a bot you have since deleted, is ignored: the list counts as never checked.
bot_id can also come as the X-Bot-Id header. Without either, your
default bot is used. Viewers can call it.
Response:
{
"total": 1200,
"reachable": 950,
"unreachable": 180,
"unchecked": 70,
"checked_bot_id": "9c1b7f4e-2a11-4c8e-8e0e-3f5d1b2c9e01",
"checked_bot_name": "Acme Boutique",
"checked_at": "2026-09-26T10:42:00Z",
"same_bot": true,
"check_running": false,
"check_available": true,
"check_bot_ok": true,
"opted_out": 12,
"not_on_rcs": 8,
"sendable": 1000,
"reason": "",
"has_answers": true,
"choices": { "rcs": 1000, "not_rcs": 180, "all": 1188 },
"check_unfinished": false
}When reason is set, creating a broadcast
from this list and bot with audience: "rcs" is refused with
400 no_reachable_recipients. When choices.not_rcs is 0, not_rcs is
refused the same way, with the reason none_cannot_get; when
choices.all is 0, every audience is refused.
A count is reused for up to a minute while the list and its check stay the same.
Remove, export or copy by reachability
DELETE /api/v1/rcs/contact-lists/{id}/unreachable
GET /api/v1/rcs/contact-lists/{id}/reachable.csv
POST /api/v1/rcs/contact-lists/{id}/create-reachable-listThese three act on the answers of the list's latest finished check, whatever bot made it, as long as that bot supports number checks. A check made with a bot that does not support them does not count: the list is treated as never checked. After a check that finished with gaps, only the answers that check saved count.
DELETE …/unreachableremoves the numbers the check found unreachable. Numbers never checked stay. Response:{ "success": true, "deleted": 180, "remaining": 1020 }. When there is nothing to remove,deletedis0andmessagesays so.GET …/reachable.csvdownloads the numbers the check found reachable, as a CSV you can import again:phone,name, then your extra columns.POST …/create-reachable-listcopies those numbers into a new list. The body{ "name": "Acme Boutique VIP. Reachable" }is optional; the default name is the list's name followed by. Reachable. It answers201withlist_id,nameandcount.
They are refused with HTTP 409 instead of acting on answers that may be half written or older than the latest check:
{
"success": false,
"code": "no_finished_check",
"message": "This list needs a finished reachability check first. Run a check, then try again."
}create-reachable-list is refused before the new list is made, so a
refusal never leaves an empty list behind.