Textify Africa API
Bulk SMS, WhatsApp templates, OTP verification and delivery tracking, over a small set of JSON HTTP endpoints.
Textify Africa API
Textify Africa is a bulk SMS platform for businesses in Africa. The API lets you send transactional and marketing SMS at scale, verify users with one-time passcodes (OTP), and track delivery status in real time, all through a small set of JSON HTTP endpoints.
Send SMS or WhatsApp templates, request and manage sender names, buy credit, send and verify OTPs, and receive status webhooks, everything a normal integration needs. Admin-only actions (approving a sender name, managing bundle pricing, reseller payouts) stay in the Textify Africa portal.
What you can build
- Bulk & single SMS: send one message or thousands in a single request, immediately or scheduled for a future date.
- WhatsApp template messages: the same send endpoint can push approved WhatsApp templates instead of SMS, billed from a separate WhatsApp wallet.
- Delivery tracking: every message moves through a clear status lifecycle, queryable by ID or pushed to your webhook URL.
- OTP verification: generate and verify one-time passcodes for login, signup or transaction confirmation flows, delivered by SMS, WhatsApp, or optionally also email.
- Custom sender names: request, manage and assign your own approved brand name instead of sending under a shared system sender.
- Buy credit: top up your SMS balance programmatically, with the same pricing and payment flow as the portal's checkout.
- Multiple API keys: create a separate, independently revocable key per environment or integration, each with its own optional webhook URL.
How the API is organized
Every request and response follows the same conventions, described in the next few pages:
- Authentication: generate a long-lived API key from the portal.
- Base URL: the single host every endpoint is relative to.
- Request & Response Format: the JSON envelope every endpoint returns, and how pagination works.
From there, the reference is grouped by resource:
- SMS API: send SMS or WhatsApp templates, list messages, fetch a single message.
- OTP API: send and verify one-time passcodes, over SMS or WhatsApp.
- Sender Names: request, list, update and assign your own sender names.
- Credits: browse bundle pricing and buy SMS credit. Not yet mirrored on this page.
- Webhooks: receive message, sender name, and purchase status updates on your own server. Not yet mirrored on this page.
Quickstart
Once you have an API key (see Authentication), sending an SMS is one request:
curl -X POST https://portal.textify.africa/api/v1/messages \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sender_name": "MYBRAND",
"is_scheduled": false,
"messages": [
{ "receiver": "0712345678", "content": "Hello Asha, your order is ready" }
]
}'
Every response, success or failure, comes back in the same envelope shape, so you can write one response handler for the whole API. Continue to Authentication to get your API key.
Authentication
Every endpoint except a small set marked public requires an API key, sent as a standard Authorization header.
API Keys
Create non-expiring API keys from your account in the Textify Africa portal, not from the API itself: log in, open the API section, and click New API Key. Give it a name (defaults to "Default" if left blank) so you can tell keys apart, e.g. one per environment or integration. The raw key is shown once:
{
"success": true,
"status_code": 201,
"status_text": "Created",
"message": "api key has been created",
"data": {
"id": "1d9b7e3a-2f7e-4b3e-9d0a-8c1a6b2e4f5c",
"name": "Production",
"api_key": "txf_92c58d699610fc2233f871f05fcb5b78b5f07a483a449525b53c71732af0caf6"
}
}
The server only ever stores a SHA-256 hash of your key, never the raw value, so there is no way to retrieve it again later. If you lose it, create a new one.
An account can hold any number of active keys at once, there's no single global key that generating a new one invalidates. Create one key per integration or environment, rename or change a key's webhook URL at any time, and delete a single key without affecting any of the account's other keys. Deleting a key takes effect immediately, the very next request authenticated with it fails.
Using an API key
Send it in the Authorization header, prefixed txf_, on any protected endpoint:
curl https://portal.textify.africa/api/v1/messages \ -H "Authorization: Bearer txf_92c58d699610fc2233f871f05fcb5b78b5f07a483a449525b53c71732af0caf6"
API keys don't expire and aren't tied to a session, so there's no logout for them.
Per-key webhook URL
Each key can optionally carry its own webhook_url, set when you create it or changed later from the portal. Sender name and purchase status callbacks are routed to the webhook_url of whichever key was used to create that sender name request or purchase, falling back to your account's default webhook URL when that key has none set. Message delivery-status callbacks always use the account default, regardless of which key sent the message. See Webhooks for the full routing rules and payload shapes.
Authentication errors
A missing or invalid key, or a disabled/unverified account, returns 401 Unauthorized with a machine-readable error code:
{
"success": false,
"status_code": 401,
"status_text": "Unauthorized",
"message": "invalid api key",
"error": "invalid_token"
}
| Status | error | Meaning |
|---|---|---|
401 |
missing_token |
No Authorization header was sent. |
401 |
invalid_token |
The API key doesn't match any account. |
401 |
account_not_found |
The account tied to the key no longer exists. |
401 |
account_disabled |
The account has been disabled by an administrator. |
401 |
account_not_verified |
The account has not completed OTP verification yet. |
Base URL
Every endpoint in this reference is relative to a single base URL:
https://portal.textify.africa/api/v1
For example, sending an SMS means making a request to https://portal.textify.africa/api/v1/messages. Throughout this reference, paths are written relative to that base, e.g. POST /messages.
The API only accepts HTTPS requests. Plain HTTP requests are not supported in production.
Versioning
/api/v1 is the current and only stable version. Breaking changes will be introduced under a new version prefix rather than changing this one in place.
Request & Response Format
Requests
All request bodies are JSON. Send Content-Type: application/json on every POST, PUT, PATCH and DELETE request that includes a body, and authenticate with a bearer token as described in Authentication unless the endpoint is marked public.
curl -X POST https://portal.textify.africa/api/v1/messages \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "sender_name": "MYBRAND", "messages": [ { "receiver": "0712345678", "content": "Hi" } ] }'
Response envelope
Every endpoint, success or failure, responds with the same JSON envelope:
{
"success": true,
"status_code": 200,
"status_text": "OK",
"message": "message has been sent",
"data": { }
}{
"success": false,
"status_code": 400,
"status_text": "Bad Request",
"message": "no receiver has been provided",
"error": "validation_error"
}
success tells you which shape you got without inspecting the status code. On success, data holds the payload and error is omitted. On failure, error is a short machine-readable code you can branch on, and message is a human-readable description safe to log or, in most cases, show to a user.
Pagination
List endpoints (like GET /messages) accept these query parameters:
| Param | Default | Description |
|---|---|---|
page |
1 |
Page number. |
limit |
10 |
Records per page (max 1000). |
sort_by |
created_at |
Column to sort by (whitelisted per endpoint). |
sort_dir |
desc |
asc or desc. |
search |
None | Required on /search endpoints. |
The paginated result is nested inside the standard envelope's data field:
{
"success": true,
"status_code": 200,
"status_text": "OK",
"message": "messages retrieved",
"data": {
"data": [ { "id": "…", "status": "delivered" } ],
"total": 42,
"page": 1,
"limit": 10,
"total_pages": 5,
"has_next_page": true,
"has_previous_page": false,
"next_page": 2,
"previous_page": null
}
}
List and search endpoints only return records the caller owns. Textify admin accounts see everything.
HTTP status codes
| Status | Status Text | Meaning |
|---|---|---|
200 |
OK | Request succeeded. |
201 |
Created | A resource was created. |
400 |
Bad Request | Missing or invalid fields, see error and message. |
401 |
Unauthorized | Missing/invalid API key or disabled/unverified account. |
403 |
Forbidden | The account type doesn't have permission for this action. |
404 |
Not Found | The route or the requested record doesn't exist. |
405 |
Method Not Allowed | The HTTP method isn't supported on this path. |
500 |
Internal Server Error | Unexpected server-side failure. |
SMS API
Send single or bulk SMS (or WhatsApp template messages), list your message history, and fetch a single message by id. One SMS unit is 160 characters, longer content is automatically billed as multiple parts.
Send SMS
/messages
Creates and queues one or many messages atomically. The sender name (if provided) must be approved, enabled, and owned by, assigned to, or a shared default for the caller. Unknown recipients are auto-created as contacts. Balance (or postpaid limit) is checked and deducted in the same transaction. Admins send for free.
| Field | Type | Required | Description |
|---|---|---|---|
sender_name |
string | optional | An approved sender name you own or are assigned to. Omit to send without an attached sender identity. Ignored when channel is whatsapp. |
messages |
array | required | One or more { receiver, content } objects. Non-empty. |
messages[].receiver |
string | required | Recipient phone number. Normalized to 255… international format. |
messages[].content |
string | required | Message text. 160 characters = 1 billed SMS unit; longer content is billed as ceil(length / 160) parts. |
is_scheduled |
boolean | optional | Defaults to false. When true, scheduled_date is required. |
scheduled_date |
string (ISO 8601) | optional | Required when is_scheduled is true. When it's due, the message moves from scheduled to sending. |
channel |
string | optional | sms (default) or whatsapp. See Send via WhatsApp below for the fields that apply then. |
curl -X POST https://portal.textify.africa/api/v1/messages \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sender_name": "MYBRAND",
"is_scheduled": false,
"messages": [
{ "receiver": "0712345678", "content": "Hello Asha, your order is ready" }
]
}'{
"sender_name": "MYBRAND",
"is_scheduled": false,
"messages": [
{ "receiver": "0711111111", "content": "Hello Asha, your order is ready" },
{ "receiver": "0722222222", "content": "Hi, your order is ready" }
]
}{
"sender_name": "MYBRAND",
"is_scheduled": true,
"scheduled_date": "2026-07-20T08:00:00Z",
"messages": [
{ "receiver": "0712345678", "content": "Reminder: your appointment is tomorrow" }
]
}
Bulk messages: send to many receivers in one call by adding more entries to messages. Scheduled send: set is_scheduled and a future scheduled_date; the message is stored with status scheduled and picked up by the background sender once due.
Response
On success the endpoint returns 201 Created with no payload. The message is queued, not returned inline. Look it up afterwards with Get Message by ID or List Messages, or subscribe to status webhooks.
{
"success": true,
"status_code": 201,
"status_text": "Created",
"message": "messages have been created",
"data": null
}
Non-scheduled messages start as sending; scheduled ones stay scheduled until due. From there: sending → processing → sent → delivered | undelivered | failed. Delivery status is polled from the provider roughly every 30 seconds and pushed to your webhook_url when it changes.
Errors
| Status | error | Meaning |
|---|---|---|
400 |
validation_error |
No messages provided, missing scheduled_date, empty content, or an invalid receiver. |
400 |
creation_error |
Sender name isn't approved/enabled, isn't owned or assigned to the caller, or the balance/postpaid limit is insufficient. |
401 |
unauthorized / invalid_token |
Missing or invalid API key. |
Send via WhatsApp
The same POST /messages endpoint sends WhatsApp messages when channel is "whatsapp". Every WhatsApp send is a template send, freeform content isn't supported on this channel, use template_params per recipient instead.
| Field | Type | Required | Description |
|---|---|---|---|
channel |
string | required | Must be "whatsapp". |
template_id |
uuid | required | An approved WhatsApp template, created and approved in the portal's WhatsApp section. Every WhatsApp send is a template send, there's no freeform text. |
whatsapp_source |
string | optional | default (Textify's shared WhatsApp number) or own (your own registered number). Defaults to default. |
messages[].receiver |
string | required | Recipient phone number. Normalized to 255… international format. |
messages[].template_params |
array of strings | optional | Ordered body variable values for this recipient, matching the template's placeholders. |
{
"channel": "whatsapp",
"template_id": "7c1a2e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f",
"whatsapp_source": "default",
"messages": [
{ "receiver": "0712345678", "template_params": ["Asha", "TXF-4821"] }
]
}
WhatsApp sends are charged against your account's USD WhatsApp wallet balance at a live rate based on the template's category, not your regular SMS balance. Top up the wallet from the portal's WhatsApp section.
Errors
| Status | error | Meaning |
|---|---|---|
400 |
validation_error |
No messages provided, or a receiver is missing/invalid. |
400 |
creation_error |
template_id is missing, isn't approved, WhatsApp sending isn't configured for the account, whatsapp_source is "own" without an active registered number, or the WhatsApp wallet balance is insufficient. |
List Messages
/messages
Paginated list of messages the caller owns (or all messages, for admins).
| Field | Type | Required | Description |
|---|---|---|---|
page |
int | optional | Page number. Default 1. |
limit |
int | optional | Records per page. Default 10, max 1000. |
sort_by |
string | optional | One of status, count, is_scheduled, scheduled_date, created_at, updated_at. Default created_at. |
sort_dir |
string | optional | asc or desc. Default desc. |
status |
string | optional | Filter by exact status, e.g. delivered, sent, failed. |
scheduled |
boolean | optional | true returns only scheduled messages. |
curl "https://portal.textify.africa/api/v1/messages?page=1&limit=20&status=delivered&sort_dir=desc" \ -H "Authorization: Bearer $API_KEY"
{
"success": true,
"status_code": 200,
"status_text": "OK",
"message": "messages have been fetched",
"data": {
"data": [
{
"id": "6b2f6e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f",
"sender_name_id": "9a1c...",
"sender_name": "MYBRAND",
"contact_id": "3d4e...",
"phone_number": "255712345678",
"contact_name": "Asha M",
"content": "Hello Asha, your order is ready",
"count": 1,
"status": "delivered",
"is_scheduled": false,
"scheduled_date": null,
"message_id": "AT-8817263",
"failure_reason": null,
"created_by": "c62f7e2a-...",
"created_by_name": "jane@example.com",
"created_at": "2026-07-10T08:12:41Z",
"updated_at": "2026-07-10T08:13:05Z"
}
],
"total": 42,
"page": 1,
"limit": 20,
"total_pages": 3,
"has_next_page": true,
"has_previous_page": false,
"next_page": 2,
"previous_page": null
}
}
There's also GET /messages/search?search=…, which accepts the same pagination parameters and matches against message content and recipient.
Get Message by ID
/messages/{id}
Fetch a single message, including its joined sender name, recipient and contact name.
curl https://portal.textify.africa/api/v1/messages/6b2f6e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f \ -H "Authorization: Bearer $API_KEY"
{
"success": true,
"status_code": 200,
"status_text": "OK",
"message": "message has been fetched",
"data": {
"id": "6b2f6e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f",
"sender_name_id": "9a1c...",
"sender_name": "MYBRAND",
"contact_id": "3d4e...",
"phone_number": "255712345678",
"contact_name": "Asha M",
"content": "Hello Asha, your order is ready",
"count": 1,
"status": "delivered",
"is_scheduled": false,
"scheduled_date": null,
"message_id": "AT-8817263",
"channel": "sms",
"template_id": null,
"template_name": null,
"whatsapp_number": null,
"whatsapp_phone_number_id": null,
"next_check_at": null,
"failure_reason": null,
"created_by": "c62f7e2a-...",
"created_by_name": "jane@example.com",
"created_at": "2026-07-10T08:12:41Z",
"updated_by": null,
"updated_by_name": null,
"updated_at": "2026-07-10T08:13:05Z"
}
}
Message object
| Field | Type | Required | Description |
|---|---|---|---|
id |
uuid | required | Message id. |
status |
string | required | scheduled, sending, processing, processing_status, sent, delivered, undelivered, or failed. |
content |
string | required | Message body as sent. |
count |
int | required | Billed SMS units, ceil(length / 160). |
sender_name |
string | null | optional | Joined sender name, when one was attached. |
phone_number |
string | null | optional | Joined recipient number (255… format). |
contact_name |
string | null | optional | Joined contact name, when known. |
is_scheduled |
boolean | required | Whether this message was created as a scheduled send. |
scheduled_date |
string | null | optional | ISO 8601 timestamp the message is/was due. |
message_id |
string | null | optional | Provider-assigned message id, once sent. |
channel |
string | required | sms or whatsapp. |
template_id / template_name |
uuid | string | null | optional | The WhatsApp template used, when channel is whatsapp. |
whatsapp_phone_number_id |
string | null | optional | The Meta phone number id the WhatsApp message was sent from, when channel is whatsapp. |
failure_reason |
string | null | optional | Set when status is failed. |
created_at |
string | required | ISO 8601 creation timestamp. |
Errors
| Status | error | Meaning |
|---|---|---|
400 |
invalid_parameter |
The id path segment isn't a valid UUID. |
404 |
not_found |
No message exists with that id. |
OTP API
Send one-time passcodes for login, signup or transaction confirmation, and verify them server-side. OTPs are single-use and expire after 30 minutes.
Send OTP
/otps
Sends a 6-digit code by SMS (and by email, if email_address is provided). Without sender_name, the code is delivered for free via the system sender. With a custom, approved sender_name, delivery is billed like a normal message from the caller's balance. This is the flow resellers integrate with to send OTPs under their own brand.
| Field | Type | Required | Description |
|---|---|---|---|
phone_number |
string | required | Recipient phone number. Normalized to 255… international format. |
email_address |
string | optional | When set, the OTP is also emailed to this address. |
sender_name |
string | optional | An approved sender name you own. Omit to send free from the system sender ("Textify"). Ignored when channel is whatsapp. |
brand_name |
string | optional | Brand name interpolated into the OTP message. Defaults to "Textify Africa". |
channel |
string | optional | sms (default) or whatsapp. |
whatsapp_source |
string | optional | default (Textify's shared WhatsApp number) or own (your own registered number). Only used when channel is whatsapp. |
curl -X POST https://portal.textify.africa/api/v1/otps \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "0712345678",
"email_address": "customer@example.com",
"sender_name": "MYBRAND",
"brand_name": "My Brand"
}'{
"success": true,
"status_code": 200,
"status_text": "OK",
"message": "otp has been sent",
"data": null
}
POST /otps sits behind the same bearer-token auth as every other endpoint. If you pass a custom sender_name, the message is charged to the authenticated caller's balance. There's no way to send a billed, branded OTP anonymously.
Sending over WhatsApp
Set channel to "whatsapp" and whatsapp_source to pick which registered number sends it. sender_name is ignored on this channel:
curl -X POST https://portal.textify.africa/api/v1/otps \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "0712345678",
"brand_name": "My Brand",
"channel": "whatsapp",
"whatsapp_source": "default"
}'
There's no free system number for WhatsApp, every WhatsApp OTP is charged from the authenticated caller's WhatsApp wallet balance. Account-verification OTPs (registration, login, forgot-password) always stay on the SMS channel.
Errors
| Status | error | Meaning |
|---|---|---|
400 |
validation_error |
phone_number was missing or blank. |
400 |
creation_error |
sender_name isn't approved/owned, channel is whatsapp without an authenticated caller, or WhatsApp sending isn't available (misconfigured account/number). |
401 |
unauthorized / invalid_token |
Missing or invalid API key. |
Verify OTP
/otps/verify
Public
Consumes a code, each OTP can only be verified once. This endpoint is public, so it can be called directly from a client app during signup or login flows, without an API key.
| Field | Type | Required | Description |
|---|---|---|---|
code |
number | required | The 6-digit OTP code, as a JSON number (not a string). |
phone_number |
string | optional | Must match the number the OTP was sent to. Provide phone_number and/or email_address. |
email_address |
string | optional | Alternative match target if the OTP was also emailed. |
curl -X POST https://portal.textify.africa/api/v1/otps/verify \
-H "Content-Type: application/json" \
-d '{ "code": 484895, "phone_number": "0712345678" }'{
"success": true,
"status_code": 200,
"status_text": "OK",
"message": "otp has been verified",
"data": null
}
Errors
| Status | error | Meaning |
|---|---|---|
400 |
validation_error |
code was missing or 0. |
400 |
verification_error |
No matching, unexpired OTP was found for that code and phone/email. |
Sender Names
Request your own branded sender name instead of sending under the shared system sender, manage who on your account can use it, and track its review status.
A new sender name is created with status requested. An admin approves or declines it, you can't approve your own. Poll GET /sender-names/{id} or subscribe to the sender name status webhook to know when a decision is made.
Request a Sender Name
/sender-names
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | required | The sender name to request. Max 11 characters. |
reason |
string | required | Why you're requesting it, shown to the reviewing admin. |
is_default |
boolean | optional | Request this as your account's default sender, used when a Send SMS call omits sender_name. |
curl -X POST https://portal.textify.africa/api/v1/sender-names \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "MYBRAND",
"reason": "Marketing campaigns for my store"
}'{
"success": true,
"status_code": 201,
"status_text": "Created",
"message": "sender name has been created",
"data": {
"id": "9a1c1e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f",
"name": "MYBRAND",
"status": "requested",
"is_default": false,
"is_disabled": false,
"created_at": "2026-09-19T08:12:41Z"
}
}
Which SMS provider actually delivers messages under this name is decided by Textify when the request is reviewed, not by you at request time — there's no provider field to set, and it isn't included in the API response.
Errors
| Status | error | Meaning |
|---|---|---|
400 |
validation_error |
name or reason was missing, or name is longer than 11 characters. |
400 |
creation_error |
That name is already taken by another sender name. |
401 |
unauthorized / invalid_token |
Missing or invalid API key. |
List Sender Names
/sender-names
Returns sender names you created, are assigned to, or that are the shared default, plus your customers' if you're a reseller.
| Field | Type | Required | Description |
|---|---|---|---|
status |
string | optional | requested, approved, or declined. |
page / limit / sort_by / sort_dir |
— | optional | Standard pagination; see Request & Response Format. |
curl "https://portal.textify.africa/api/v1/sender-names?status=approved" \ -H "Authorization: Bearer $API_KEY"
{
"success": true,
"status_code": 200,
"status_text": "OK",
"message": "sender names have been fetched",
"data": {
"data": [
{
"id": "9a1c1e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f",
"name": "MYBRAND",
"status": "approved",
"is_default": false,
"is_disabled": false,
"created_at": "2026-09-19T08:12:41Z",
"created_by": "c62f7e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f",
"created_by_name": "you@example.com",
"updated_by": null,
"updated_by_name": null,
"updated_at": null,
"reseller_id": null,
"users": ["c62f7e2a-...", "d81a9b3c-..."]
}
],
"total": 1,
"page": 1,
"limit": 10,
"total_pages": 1,
"has_next_page": false,
"has_previous_page": false,
"next_page": null,
"previous_page": null
}
}
GET /sender-names/search?search=… accepts the same filters plus a search term matched against name and status. GET /sender-names/{id} fetches one by id.
Sender name object
Every response includes these fields, regardless of whose sender name it is:
| Field | Type | Required | Description |
|---|---|---|---|
id |
uuid | required | Sender name id. |
name |
string | required | The sender name text, as it appears to recipients. |
status |
string | required | requested, approved, or declined. |
is_default |
boolean | required | Whether this is the account's default sender. |
is_disabled |
boolean | required | Set by an admin independently of status, an approved name can still be disabled. |
created_at |
string | required | ISO 8601 creation timestamp. |
A sender name you don't own — visible only because it's a shared default — comes back with just the fields above. One you created, or one belonging to your own reseller customers, also includes the fields below.
| Field | Type | Required | Description |
|---|---|---|---|
created_by / created_by_name |
uuid / string | optional | Who requested it. |
updated_by / updated_by_name |
uuid | null / string | null | optional | The admin who last approved/declined/updated it, if any. |
updated_at |
string | null | optional | ISO 8601 timestamp of the last update. |
reseller_id |
uuid | null | optional | Set when it belongs to one of your customers, if you're a reseller. |
users |
array of uuids | optional | Which of your account's users are allowed to send with it. |
api_key_id |
uuid | null | optional | The API key that created it, if it was created via the API rather than the portal. |
Update a Sender Name
/sender-names/{id}
You can update a sender name you own; a reseller can also update their customers'.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | required | Max 11 characters. |
is_default |
boolean | optional | Whether this should be the account's default sender. |
curl -X PUT https://portal.textify.africa/api/v1/sender-names/9a1c1e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "MYBRAND", "is_default": true }'
Assign Users
/sender-names/{id}/assign
Replaces the full list of user ids allowed to send with this sender name, send the complete list each time, not just additions.
curl -X PATCH https://portal.textify.africa/api/v1/sender-names/9a1c1e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f/assign \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "users": ["c62f7e2a-...", "d81a9b3c-..."] }'
Delete a Sender Name
/sender-names/{id}
curl -X DELETE https://portal.textify.africa/api/v1/sender-names/9a1c1e2a-4b0e-4e9a-9a2e-1a2b3c4d5e6f \ -H "Authorization: Bearer $API_KEY"
DELETE /sender-names/bulk takes { "ids": ["…", "…"] } to delete several at once.
Support
Stuck on an integration, or waiting on a sender name review? Our engineers answer directly.



