Opt-outs
Your opt-out list holds the people who have asked not to be messaged, one entry per recipient per channel. MsGine checks it on every SMS, WhatsApp and email send, so an opted-out recipient is never messaged and never charged. Use these endpoints to keep the list in sync with your own systems.
The list belongs to your account. Opting out of one business does not opt a person out of any other business on MsGine.
All paths are under https://api.msgine.net/api/v1.
| Method | Path | Scope | Purpose |
|---|---|---|---|
GET | /developers/opt-outs | READ_CONTACTS or MANAGE_CONTACTS | List entries |
POST | /developers/opt-outs | MANAGE_CONTACTS | Opt an address out |
DELETE | /developers/opt-outs/:id | MANAGE_CONTACTS | Allow messages again |
FULL_ACCESS grants every scope.
Channels
| Channel | Address |
|---|---|
sms | Phone number |
whatsapp | Phone number |
email | Email address |
An opt-out applies to one channel. Opting a number out of sms does not stop WhatsApp messages to it; add a whatsapp entry as well if you want both. Push notifications are not covered by the opt-out list.
Opt an address out
curl -X POST https://api.msgine.net/api/v1/developers/opt-outs \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"address": "0701234567",
"reason": "Asked by phone not to receive promotions"
}'| Parameter | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | sms, whatsapp or email |
address | string | Yes | Phone number, or email address for email. Max 320 characters |
reason | string | No | Free text, max 500 characters |
Status Code: 201 Created
{
"id": "4c3b2a19-8f7e-4d6c-b5a4-3e2d1c0b9a88",
"userId": "0f6c1a6e-7a3e-4b8e-8a43-0d3f2c9b1e22",
"channel": "sms",
"address": "256701234567",
"source": "api",
"reason": "Asked by phone not to receive promotions",
"createdAt": "2026-11-03T10:00:00.000Z",
"updatedAt": "2026-11-03T10:00:00.000Z"
}Entries added through this API have "source": "api". The address is returned in its normalised form (below), not as you sent it.
Opting out an address that is already on the list is harmless: you get the existing entry back and nothing else happens. An address that is not a valid phone number (or email address, for email) returns 400.
How addresses are matched
Addresses are normalised before they are stored and before every send, so one person is one entry however the number was written. For phone numbers, spaces, -, (, ), . and + are ignored and Ugandan local forms are converted to international digits:
| You write | Stored and matched as |
|---|---|
+256701234567 | 256701234567 |
256701234567 | 256701234567 |
0701234567 | 256701234567 |
701234567 | 256701234567 |
Other international numbers are matched as their digits, 10 to 15 long (for example 254712345678). Email addresses are matched in lower case, so Amina@Example.com and amina@example.com are the same entry.
List entries
curl "https://api.msgine.net/api/v1/developers/opt-outs?channel=sms&page=1&limit=50" \
-H "X-Api-Key: YOUR_API_KEY"| Parameter | Type | Description |
|---|---|---|
channel | string | Only this channel |
page | number | Default 1 |
limit | number | Default 50, max 200 |
Status Code: 200 OK
{
"data": [
{
"id": "4c3b2a19-8f7e-4d6c-b5a4-3e2d1c0b9a88",
"userId": "0f6c1a6e-7a3e-4b8e-8a43-0d3f2c9b1e22",
"channel": "sms",
"address": "256701234567",
"source": "api",
"reason": "Asked by phone not to receive promotions",
"createdAt": "2026-11-03T10:00:00.000Z",
"updatedAt": "2026-11-03T10:00:00.000Z"
}
],
"total": 1,
"page": 1,
"pages": 1
}Newest first.
Allow messages again
curl -X DELETE https://api.msgine.net/api/v1/developers/opt-outs/OPT_OUT_ID \
-H "X-Api-Key: YOUR_API_KEY"Returns 204, or 404 if the entry is not yours.
Only with consent
Remove an entry only when the recipient has told you they want messages again. Messaging someone who opted out can breach anti-spam rules and damages your sender reputation.
What happens when you send to an opted-out recipient
SMS, WhatsApp and email sends filter the list before anything is charged.
- Some recipients opted out. They are dropped. The message goes to the rest and you are charged only for those.
- Every recipient opted out. Nothing is sent or charged and the request returns
400:
{
"statusCode": 400,
"error": "validation_error",
"message": "Every recipient has opted out of messages from this account",
"optedOut": ["+256701234567"]
}optedOut lists the recipients that were dropped.
For email this applies when every to address has opted out.
Check the list before sending, or treat this specific 400 as "skipped", not as a failure to retry.
Inside a workflow, an opted-out recipient does not fail the run: the sending step records { "skipped": "opted_out" } and the run carries on with the next step.
Events
Opting an address out or back in records a contact.opted_out or contact.opted_in platform event, so a workflow can react, for example by moving the contact to a group. The payload holds channel, address (normalised), source and contactId (set when the address belongs to one of your contacts, otherwise null). Opting out an address that is already listed, or removing one that is not, records nothing.
{
"channel": "sms",
"address": "256701234567",
"source": "api",
"contactId": "6a1f…"
}Email suppressions
Separate from your opt-out list, MsGine keeps a global list of email addresses that bounced or complained. Sends to those addresses are blocked for every account and you do not manage that list through this API.