Text messaging
Your agents text the person on a call, answer the messages that come back, and run campaigns, all through a Twilio or Africa's Talking account you own.
Overview
Text messaging runs on a carrier account of your own. Aethex never owns the sender: every message leaves under a number or a registered name that belongs to you, and the carrier bills you for the traffic directly.Four things produce a text, and they share one set of rules: the same senders, the same opt-out list, the same carrier verdicts.- An agent, mid-call. The agent picks one of your stored messages and fills in its blanks, so the caller gets a booking link or a confirmation while still on the phone. It lands on that customer's thread, so whoever picks the conversation up afterwards can see what was sent.
- An agent, in a text conversation. Someone texts a number that answers, and the agent replies from that thread's own history.
- An operator, from the inbox. Free text, written by a person, on a thread that already exists.
- A campaign. One body with per-recipient blanks, sent down a list at a rate you choose.Everything a given customer sends or receives on one of your lines lands on a single thread, which the inbox and the text conversations API read from.
Twilio and Africa's Talking
Two carriers can sit behind the channel, and they differ in what a sender identity actually is.Twilio. There is nothing to link twice: the Twilio account you registered for voice sends your texts as well. You only say which of your numbers may send, and which one answers. Twilio is asked at that moment whether the number can really carry texts, so a line bought for voice is refused up front rather than failing later on the wire.Africa's Talking. The identity travels on the credential rather than on a number. You link a messaging account (username, API key, registered sender) and messages leave under that sender. Two kinds exist: an alphanumeric sender name such as ACME, which is outbound only because a name cannot be replied to, and a short code, which can also receive. Register the short code as one of your numbers, point it at an agent, and paste the callback URL into your carrier dashboard. That URL is handed to you once, when you link the account or ask for a new one, and never shown again; where the deployment has no webhook host of its own it comes back as a path, for you to put your own host in front of. A short code is unique across the platform and the first registration wins, so register yours before you advertise it.An account can hold both. Which one a given call sends through is settled by the order in the next section, never by which was configured first.
| What differs | Twilio | Africa's Talking |
|---|---|---|
| What you link | Nothing extra: the account you already registered for voice. | A messaging account: username, API key, registered sender. |
| What the recipient sees | One of your phone numbers. | Your registered sender name, or your short code. |
| Can it receive replies | Yes, on any number you grant the answering role. | A short code can. An alphanumeric sender name cannot. |
| Where you configure it | PATCH /phone-numbers/{id} | POST /messaging-accounts, then POST /phone-numbers/africastalking/register |
curl -X POST https://api.aethexai.com/api/v1/messaging-accounts \
-H "X-API-Key: $AETHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"provider": "africastalking",
"username": "your_at_username",
"api_key": "<carrier API key>",
"sender": "ACME",
"label": "Acme Ghana"
}'
# The response carries inbound_callback_url once, and never again.
# Paste it into Africa's Talking: SMS -> Callback URLs -> Incoming Messages.
# If it comes back as a path rather than a full URL, put your own host in front.Which sender a message leaves under
Sending is two separate facts, and it pays to keep them apart.The grant, sms_sender on a phone number, means *this line may send*. Several of your numbers can carry it, and the carrier's opinion of each one is checked at the moment you grant it.The default, sms_sender_default, means *this is the line we fall back to*. Exactly one number per account holds it, and it has to be one of the numbers that send. The first number you let send becomes it, there being nothing to choose between; granting a second one leaves the default where it is, because which line that should be is your call.On top of those, an agent can name the line it texts from: sms_sender_number_id, validated when you write it, so a sales agent texts from the sales line whatever the account default happens to be.At call time the three candidates are read in a fixed order, the most specific first. A number released or stood down after an agent named it falls through to the next candidate rather than taking the whole account down with it. If nothing resolves, the call simply cannot text and the agent is never offered the tools.Standing a line down is accepted rather than refused, since holding several senders is the point of the grant. The agents that named it keep texting, from the account default, which is a different sender on the recipient's handset: when a change takes the grant off a line an agent still names, the response says how many moved, in agents_now_on_default.
# Several numbers may carry the sending role. Exactly one is the default.
curl -X PATCH https://api.aethexai.com/api/v1/phone-numbers/<SALES_NUMBER_ID> \
-H "X-API-Key: $AETHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sms_sender": true, "sms_inbound": true}'Receiving: threads, STOP and handoff
A number answers texts once you grant it the answering role (sms_inbound) and bind an agent to it. Granting it does not touch call routing: a line whose calls run through your own SIP trunk, or anywhere else, can still answer texts, and the calls stay exactly where you pointed them. The response says as much in calls_routed_elsewhere, naming what still carries them, so binding an agent is never mistaken for bringing the calls back.Every inbound message passes three gates before an agent composes anything.STOP comes first, ahead of whatever else the message might mean. STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, ARRET and ARRÊT opt the person out; START and UNSTOP bring them back. They are told once, and the message names START so the way back is never a guess. An opt-out covers your whole account, not just the line they wrote to, and it is re-read immediately before every single send, including a text an agent is about to send mid-call.A request for a person (*talk to a human*, *real person*, *representative*) stores the message, answers once, and stops the agent replying until an operator hands the thread back. The agent's webhook receives sms.handoff.requested, so nobody has to be watching the inbox.A thread switched to `human` mode belongs to your operator, and the agent stays silent on it.Past those three, the agent answers from the thread's own history, inside a 24-hour window. A thread answered 20 times in 10 minutes goes to a person instead of round again, with the same sms.handoff.requested: when the far end is also automatic, neither side has any reason to stop.
The message library
An agent never composes a text. You write the messages; the agent picks one by name and fills in its blanks. A model free to write its own text would be a messaging gateway under your sender identity, steerable by whoever is on the line.A message is a name, a body of up to 480 characters, and {blanks} the agent fills with values of up to 120 characters each. You can hold 25 active messages at a time, across the whole account rather than per agent: every active message is described to the agent when the call starts, so the ceiling is what one agent can usefully hold in mind, not what we can store. Retiring a message with PATCH /sms-messages/{id} frees a slot and keeps the message readable, so a text you sent last year still makes sense in the thread. Renaming is deliberately absent: a prompt refers to a message by name, so retire and recreate instead.Two built-in tools draw on the library during a call. send_message sends one of your messages. send_booking_link texts a single-use scheduling link, which exists for the one thing a voice agent reliably cannot do: take an email address down over the phone. Both share one allowance of 30 texts per call, the same message goes out at most twice in a call by default (sms_max_sends_per_message on the agent changes it, up to 30), and every send re-reads the opt-out list first.POST /sms-messages/{id}/test sends a stored message to your own handset so you can check the wording. Five an hour, and it deliberately opens no thread, so checking your own copy does not turn you into someone's customer.
curl -X POST https://api.aethexai.com/api/v1/sms-messages \
-H "X-API-Key: $AETHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "booking_confirmation",
"body": "Hi {first_name}, your appointment is confirmed for {time}. Reply STOP to opt out.",
"label": "Booking confirmation"
}'Campaigns
A campaign is one body, a list of recipients carrying their own values, and a rate. Create it and start it by hand, or give it a scheduled_at and let it start itself.Two things are checked when you create it, rather than discovered slowly one recipient at a time: the number must be able to send, and it must be able to answer, because someone replying STOP has to reach somebody. Pass acknowledge_no_replies: true to waive the second one knowingly.Recipients move pending → sending → sent | failed | skipped, and the opt-out list is re-read for each one. A campaign only ends in failed for a definitive reason; anything you can fix pauses it instead and puts the reason on the campaign.sent means the carrier accepted the message, not that it arrived. Every recipient carries the carrier's own message id, which is what you quote in your carrier's console when you need to trace one.
curl -X POST https://api.aethexai.com/api/v1/sms-campaigns \
-H "X-API-Key: $AETHEX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phone_number_id": "<SENDING_NUMBER_ID>",
"name": "September reminder",
"body": "Hi {first_name}, your appointment is at {time}.",
"recipients": [
{"to": "+233201234567", "values": {"first_name": "Ama", "time": "3pm"}}
],
"rate_per_minute": 60
}'Limits
Everything the channel bounds, in one place.
| What | Value | Applies to |
|---|---|---|
| Stored message body | 480 | Characters, checked again once the blanks are filled. Same ceiling for a campaign body. |
| One filled-in value | 120 | Characters, per blank the agent fills. |
| A free-text reply | 1600 | Characters, for an operator reply and for an agent's reply in a thread. Longer text is split into carrier segments. |
| Texts per call | 30 | A backstop rather than a budget: the same message goes out at most twice in one call by default (sms_max_sends_per_message on the agent changes it), and both tools share the one allowance. |
| Active stored messages | 25 | For the whole account, not per agent. Retiring one frees a slot and leaves it readable. |
| Test sends | 5 / h | Per account, on POST /sms-messages/{id}/test. |
| Agent replies per thread | 20 / 10 min | Past this the thread goes to a person instead of round again. |
| Conversation window | 24 h | How far back an agent reads its own thread when replying. |
| Campaign rate | 1-600 / min | Messages per minute, your choice at creation. |
What is not included
Named plainly, because most questions about this channel turn out to be one of these.- No webhook into your system when a customer texts in. There is no sms.received event; read the inbox endpoints instead.
- No delivery receipts yet. The carrier's message id is stored for every business-initiated send, and it is what you paste into your carrier's console meanwhile.
- An agent never writes its own text. Deliberate, and not something we plan to change.
- A text someone sends you during a call does not reach that call. It opens its own thread, handled separately, so a caller can end up talking to two things at once.
- The sending tools cannot be driven from n8n. Both names are refused by the binding schema.Your carrier bills you for the traffic on your own account. Business-initiated messages sent through Aethex also draw credits: see Billing.
Next steps
- Text messages API: the carrier account and the message library, field by field.
- Text conversations API: threads, the inbox and campaigns.
- Phone numbers: registering the lines these roles are granted on.
- Africa's Talking: linking that carrier, step by step.
