Skip to content

Send your first RCS message

POSThttps://api.9bits.net/ng/v1/rcs/messages

Sends one RCS message to one recipient.

  1. Have an approved agent on your account — see Agents. A pending agent is rejected, not queued.

  2. Mint a 9bk_ key with the rcs:send scope in the dashboard under Settings → Security.

  3. Send to your own number first. Every call here is a real, billed send.

FieldTypeRequiredDescription
agentstringAn approved agent on your account.
destinationstringRecipient in international format, no +2348020000000.
contentstringThe message text. Required unless you send a richCard or carousel.
ttlstringGive up after this long, e.g. "300s".
richCardobjectA single card. See Rich messages.
carouselobjectTwo or more cards. See Rich messages.
suggestionsarrayTappable chips under the message. Valid alongside plain content.
Terminal window
curl -X POST https://api.9bits.net/ng/v1/rcs/messages \
-H "Authorization: Bearer $NINEBITS_RCS_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent": "FirstDigits",
"destination": "2348020000000",
"content": "Your code is 481920. It expires in 5 minutes.",
"ttl": "300s"
}'
{
"status": 200,
"referenceId": "8f2c41ba9d7e40a1b5c3"
}

Keep the referenceId. It is your handle on the message — it comes back as message_id in the delivery report, and it is how you find the message in the reports.

Everything below is checked before your account is debited, so a rejected send costs nothing.

What you seeWhat it meansFix
no agent is allocated to this account for "X"No agent by that name is on your account.Onboard one.
the agent for "X" is not live yet (status registered)Onboarded, still in review.Wait for approval.
recipient has opted out of messages from this agentThey replied STOP to this brand.Do not retry. See Consent.
invalid destination: …The number did not parse or classify.Check phone number format.
a carousel needs at least two cardsA one-card carousel.Send it as richCard.
tariff not configured for operator "mtn" on this accountNo RCS price for that network on your account.Contact support — this is a billing setup gap, not a code bug.
insufficient balance (status: 51)Balance below the cost, past your credit limit.Top up.

Everything down to the carousel rule arrives as HTTP 400 in the structured error envelope. The tariff gap is a 500 — it is our configuration, not your request:

{
"error": {
"type": "validation_error",
"code": "invalid_body",
"message": "the agent for \"FirstDigits\" is not live yet (status registered)"
}
}

The 200 only means the message was accepted and queued. It then moves:

Queued → Dispatched → Delivered
└→ Undelivered | Rejected

Set your webhook URL in the portal (Settings → Webhooks → RCS) and you get told; otherwise poll the reports. Both are covered in Delivery reports.