Splashify RCS

Docs

Open panel
Panel

Bots

Every RCS Panel account can operate multiple bots. Each bot has its own KYC application, its own provider-side identity, and its own transactions-per-second (TPS) budget. Wallet balance is shared: every bot debits the same wallet.

The typical customer lifecycle:

  1. Apply for a new bot via POST /rcs/apply.
  2. Wait for admin review, the KYC application is approved.
  3. Admin activates the bot with provider credentials, TPS, and price.
  4. The bot's status flips to active and it can send messages.

Template verification and media

Every submitted template is reviewed against RCS content policy before it can be used; the verdict lands on ai_review_status. Most are approved within 24 hours, though most clear sooner. Rich cards and carousels are fully supported, see the templates reference for the full field list and validator rules.

Media referenced by a template is stored on Splashify Pro's CDN so sends do not have to fetch from your origin.

GET /rcs/bots includes a provider field on every row, always filled. Treat it as an opaque label. To decide what a bot can do, read can_broadcast and can_check below, and check_bot_ok from the audience endpoint for number checks, rather than branching on provider.

Uniqueness rules

Two fields are unique across accounts to prevent brand impersonation:

  • GSTIN, one GSTIN cannot be used by two different accounts.
  • Brand website, one brand website cannot be used by two different accounts.

The same account can reuse both fields across multiple bots (multi-brand company case). The uniqueness check normalises values before comparison:

  • GSTIN, uppercase + trim.
  • Brand website, lowercase, strip http:// / https:// prefix, strip trailing /.

If either value is already registered under a different account, the POST /rcs/apply call returns 409 Conflict with:

json
{
  "success": false,
  "field": "gstin",
  "message": "This GSTIN is already registered under another RCS account. Contact support with proof of ownership if this is incorrect."
}

List your bots

http
GET /api/v1/rcs/bots
Authorization: Bearer <rcs_panel_jwt>

Response:

json
{
  "success": true,
  "bots": [
    {
      "bot_id": "e8bb61e0-8500-11ef-9c9a-4bc60d7a1e2f",
      "bot_name": "Acme Support",
      "provider": "jiocx",
      "agent_id": "acme_support_agent_01",
      "status": "active",
      "is_default": true,
      "tps": 100,
      "rcs_price": 0.35,
      "use_case": "transactional",
      "use_case_label": "Transactional",
      "can_broadcast": false,
      "can_check": true,
      "activated_at": "2026-07-10T12:00:00Z"
    },
    {
      "bot_id": "2a4d9a66-7e87-4057-bee7-662879612c21",
      "bot_name": "Acme Marketing",
      "provider": "jiocx",
      "agent_id": "acme_marketing_agent_01",
      "status": "active",
      "is_default": false,
      "tps": 200,
      "rcs_price": 0.45,
      "use_case": "promotional",
      "use_case_label": "Promotional",
      "can_broadcast": true,
      "can_check": true,
      "activated_at": "2026-07-23T09:15:00Z"
    }
  ]
}

agent_id is the opaque provider-side identifier for the bot; treat it as read-only in the panel API.

use_case and can_broadcast

use_case is the traffic type the bot is registered for with the carrier, normalised to one of promotional, transactional, otp, multi_use, or "" when it was never recorded. use_case_label is the same value formatted for display.

can_broadcast is the derived answer to "may this bot run a broadcast?": false for transactional and otp, true for everything else including an unrecorded registration.

Read the flag, do not re-derive it

can_broadcast exists so the rule lives in exactly one place. Deriving it yourself from use_case works right up until the rule changes, at which point your client and the API disagree and your users see a 403 the UI told them would not happen.

Broadcast, recurring-broadcast and retarget endpoints return 403 bot_not_promotional when can_broadcast is false, see Broadcasts. One-off sends are unaffected.

can_check

Pass a bot with can_check: true to a list's number check. A bot that is not live on the network yet can have can_check: true too: its check then stops at once and says so.

Get one bot

http
GET /api/v1/rcs/bots/{bot_id}

Apply for a new bot

Applying is how a bot comes into existence. There is no create-a-bot call: you submit a KYC application, the carrier reviews it, and the bot appears on the account when that clears.

It takes two steps, and they have to happen in that order. Files are uploaded first and return URLs; the application then references those URLs. The submit call is JSON and never carries a file.

Scope

POST /apply and POST /apply/upload need bots:write. Reading your own application back with GET /apply needs bots:read.

Step 1: upload the documents

http
POST /api/v1/rcs/apply/upload
Content-Type: multipart/form-data

Two form fields: field names which document this is, and file is the file itself.

bash
curl -X POST https://apis.splashifypro.com/api/v1/rcs/apply/upload \
  -H "Authorization: Bearer rcs_pk_live_..." \
  -F "field=bot_logo" \
  -F "[email protected]"
json
{ "success": true, "url": "https://folder.splashifypro.com/rcs-applications/.../logo.png" }

The rules below are the carrier's, not ours, and are rejected at upload rather than at submission so a mistake surfaces immediately:

The two image sizes are exact, not maximums. A 225 x 224 logo is refused.

Step 2: submit the application

http
POST /api/v1/rcs/apply
Content-Type: application/json

Required on submission. A draft may omit any of them; a submission may not:

Everything else is optional: company_gstin, company_address, company_zip, registered_company_name, company_hq_location, transaction_type, business_pan_url, cin_url, corporate_registration_url, electricity_bill_url, optin_video_url, brand_color, industry_type, languages, the phone_label / website_label / email_label display labels, and the contact_* fields.

bash
curl -X POST https://apis.splashifypro.com/api/v1/rcs/apply \
  -H "Authorization: Bearer rcs_pk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "bot_name": "Acme Support",
    "brand_name": "Acme",
    "bot_message_type": "Promotional",
    "short_description": "Order updates and offers from Acme",
    "bot_logo_url": "https://folder.splashifypro.com/.../logo.png",
    "banner_image_url": "https://folder.splashifypro.com/.../banner.png",
    "business_manager_name": "Priya Sharma",
    "brand_support_phone": "+919876543210",
    "support_email": "[email protected]",
    "brand_website": "https://acme.example",
    "privacy_url": "https://acme.example/privacy",
    "terms_url": "https://acme.example/terms",
    "contact_us_url": "https://acme.example/contact"
  }'
json
{
  "success": true,
  "status": "submitted",
  "bot_id": "e8bb61e0-8500-11ef-9c9a-4bc60d7a1e2f",
  "updated_at": "2026-07-15T09:12:33Z"
}

A missing required field returns 400 naming exactly which one:

json
{ "success": false, "message": "banner_image_url is required" }

Drafts

Send "save_as_draft": true to store a partial application. A draft does not allocate a bot_id and does not run the uniqueness gate, so you can iterate on it freely and submit when it is complete.

Resubmitting a rejected application

Include the existing bot_id and the call updates that application instead of starting a new one. Without it you get a second bot rather than a corrected first one, which is the mistake this field exists to prevent.

A rejection comes in one of two kinds. GET /api/v1/rcs/apply?bot_id=… tells them apart with resubmit_blocked, and the same flag is on every bot in GET /api/v1/rcs/bots:

Reading it back

http
GET /api/v1/rcs/apply

Returns the stored application and its current status, which is how you poll for the outcome of a review.

default_bot_id on /me

For clients still shipping the old single-bot request shape, the /rcs/me response now includes default_bot_id, the bot marked is_default=true, so you can seed dropdowns without an extra /rcs/bots call:

json
{
  "success": true,
  "user": {
    "user_id": "…",
    "default_bot_id": "e8bb61e0-8500-11ef-9c9a-4bc60d7a1e2f",
    "bots_count": 3,
    "active_bots_count": 2,
    "pending_bots_count": 1
  }
}

Sending a broadcast or one-off message without a bot_id field defaults to this bot for one release cycle; new integrations should pass the exact bot_id.

Change the default bot

http
POST /api/v1/rcs/bots/{bot_id}/default
Authorization: Bearer <rcs_panel_jwt>

Makes an active bot the account's default. No request body. The bot that was the default loses the flag in the same step, so the account always has exactly one.

The default bot is:

  • the bot a call uses when it has no bot_id: a single send, broadcast create, template review and the audience count
  • default_bot_id on /rcs/me
  • the bot the panel picks first in the broadcast, recurring broadcast and template composers, and on the number check and opt-out pages

Every change is recorded in the audit log as bot.default_changed.

json
{
  "success": true,
  "default_bot_id": "2a4d9a66-7e87-4057-bee7-662879612c21"
}

Calling it for the bot that is already the default returns the same 200 and changes nothing.

Who can call it

Only the account owner, from a signed-in panel session. Operators and viewers get 403 not_owner. An API key gets 403 api_key_endpoint_not_allowed at any scope, because this call changes where every request without a bot_id goes.

json
{
  "success": false,
  "code": "bot_not_active",
  "message": "Only an active bot can be the default."
}

When a bot is suspended

The admin team can suspend a single bot independently of the account. Only that bot is blocked, every other bot on the account keeps sending as normal. Broadcasts or one-off sends that target a suspended bot return HTTP 403 with the bot_not_active code (same shape you get for pending / rejected bots; see Errors reference).

The GET /rcs/bots/:bot_id and GET /rcs/bots responses expose two extra fields when the bot is suspended:

json
{
  "bot_id":         "e8bb61e0-…",
  "bot_name":       "acme_promotions",
  "status":         "suspended",
  "suspend_reason": "Repeated content-policy violations after two warnings, see support ticket #4210."
}

suspend_reason is a verbatim quote of the admin's stated reason so the panel can render it inline. The customer also gets a "Your RCS bot <name> has been suspended" email carrying the same reason.

Reactivation (admin flips the bot back to active) fires "Your RCS bot <name> is active again"; the suspend_reason field is cleared and sends unblock immediately.

Team-member access

Operators and viewers only see and act on bots that the account owner granted them. When their JWT bots scope excludes a bot, any endpoint that targets it returns 403 bot_not_assigned, see the Team members reference for the full role matrix.