Skip to content

Consent & opt-out

RCS puts your verified brand on the message. That is the value, and it is also the exposure: a complaint lands on a named brand, not an anonymous short code.

Two things follow, and the platform enforces both for you.

A recipient who has opted out of your brand cannot be messaged. The send is refused with 400:

recipient has opted out of messages from this sender

It is checked before your account is debited, so a suppressed send costs nothing. Do not retry it — the answer will not change.

Suppression is per (number, brand)

Opting out of one brand is not opting out of every brand on the platform. Someone who unsubscribed from FirstDigits still receives messages from FD-Alerts, and from every other customer sending through us.

The brand key is your sender name, slugified: First Digits becomes first-digits. It is resolved from the sender that actually goes on the wire — so if your account uses sender replacement, consent is keyed to the substitute, not to what you passed.

When someone replies to your agent, the keyword is matched against the whole trimmed message and applied immediately — not on a sweep, because the entire point of an opt-out is that the next message does not go out.

ReplyEffect
STOP, CANCEL, END, QUIT, UNSUBSCRIBE, OPTOUT, OPT-OUT, STOPALLSuppressed for that brand.
START, UNSTOP, SUBSCRIBE, OPTIN, OPT-IN, RESUMERe-subscribed.

Matching is on the whole message, never a substring — “please do not stop sending these” does not unsubscribe anyone.

Opt-out events raised by the RBM platform itself (the user hitting the block button, rather than typing a word) are honoured the same way.

You need consent before the first message, and you need to be able to prove it. Two public, unauthenticated endpoints back a hosted consent page — the caller is the person opting in, not your server.

GET/ng/v1/rcs/consent/brands/{slug}

Terminal window
curl https://api.9bits.net/ng/v1/rcs/consent/brands/first-digits
{
"slug": "first-digits",
"brand": "FirstDigits",
"consent_text": "I agree to receive messages from FirstDigits on the number I have provided, by RCS and SMS. These may include one-time passcodes, transaction and delivery updates, appointment reminders and service notices. Message frequency varies. Message and data rates may apply. I can reply STOP at any time to unsubscribe, or HELP for assistance.",
"version": "v1-2026-09"
}

Render this text; do not hardcode your own. The wording shown and the wording stored come from the same source, so they cannot drift apart and leave you with a consent record that does not match what the user actually read.

Only approved senders resolve. An unknown slug is a 404 — which is the point: without that check, the page would mint verifiable-looking consent for any brand name someone typed into a URL.

POST/ng/v1/rcs/consent

Terminal window
curl -X POST https://api.9bits.net/ng/v1/rcs/consent \
-H "Content-Type: application/json" \
-d '{
"brand": "first-digits",
"msisdn": "2348020000000",
"accepted": true
}'
{ "status": "ok", "brand": "FirstDigits" }
FieldTypeRequiredDescription
brandstringThe brand slug.
msisdnstringThe number opting in. Normalised server-side; 8–15 digits.
acceptedbooleanMust be true.

Three deliberate behaviours:

  • accepted: false is refused, not stored as a weaker consent. There is no such thing — the ticked box is the consent.
  • The wording is rebuilt server-side from the resolved brand and is never taken from your request. A client that could post its own consent_text could store a record claiming the user agreed to anything.
  • The IP address and user agent are captured alongside the text and its version, as corroboration.

Errors are 400 with consent was not given or that does not look like a phone number, or 404 for an unknown brand.

  1. Collect consent before the first send, on a page that names the brand, the channels, what you will send, and how to stop. The consent_text from the brand endpoint says all four — that is why it reads the way it does.

  2. Serve the wording from the API, so a change to the text propagates to your page instead of silently invalidating your records.

  3. Let STOP work. Do not filter keywords out before they reach the agent, and do not try to talk someone out of it.

  4. Check your sends for recipient has opted out and remove that number from your own lists too. The platform blocks it, but your retry logic should not keep trying.

  5. Do not re-add someone from a CSV after they have unsubscribed. The suppression survives it, so the send just fails — but the intent is what gets you in trouble.

Every message and event from your agent is stored — the reply text, a tapped chip’s postback.data, and the raw payload — and surfaced in the dashboard.

There is no customer-facing endpoint to poll inbound RCS messages yet. If you need a live feed of replies for your own application, talk to support about the options.