aethexDocs

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.

OUTBOUNDfour things produce a textagent, mid-callagent, in a threadoperator, from the inboxcampaignSENDERyour number or nameCARRIERTwilio · ATCUSTOMERmobileINBOUNDthe customer writes backCUSTOMERmobileCARRIERTwilio · ATwebhookAETHEXopt-out · agent · limitsTHREADinbox · APIOne thread per customer per line, whichever direction the message travelled.
CARRIERS

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 differsTwilioAfrica's Talking
What you linkNothing extra: the account you already registered for voice.A messaging account: username, API key, registered sender.
What the recipient seesOne of your phone numbers.Your registered sender name, or your short code.
Can it receive repliesYes, on any number you grant the answering role.A short code can. An alphanumeric sender name cannot.
Where you configure itPATCH /phone-numbers/{id}POST /messaging-accounts, then POST /phone-numbers/africastalking/register
Heads up
An alphanumeric sender name is outbound only, and that is a property of the carrier rather than a setting you can change. Someone who receives one cannot reply, so their STOP never reaches you, which is why a campaign from a line that cannot answer is refused unless you waive it explicitly. For two-way conversations, use a short code or a number that takes inbound messages.
bash
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.
SENDERS

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.

1THE AGENT'S OWN NUMBERagents.sms_sender_number_idchosen for this agentnot set, or no longer sending2A LINKED MESSAGING ACCOUNTmessaging-accounts.sendera credential whose whole purpose is textingnot set, or no longer sending3THE ACCOUNT DEFAULTphone-numbers.sms_sender_defaultthe fallback for everything elsenot set, or no longer sendingNothing resolves → the call cannot text, and the texting tools are withheld.
Note
The order is fixed rather than "whichever exists", so an account holding more than one sender always gets the same answer. Releasing the line an agent names is what changes that agent's sender; nothing is silently re-pointed behind your back.
bash
# 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}'
INBOUND

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.

INBOUND TEXTon a line that answersOPT-OUTSTOP / STARTrecorded account-wideHANDOFFtalk to a humanagent stands downTHREAD MODEmode = humanyour operator answersAGENT REPLIESfrom the threadRead left to right: each gate can end the turn on its own, and STOP is asked first on purpose.
Heads up
Inbound delivery is the carrier's, not ours: a message that never reaches your carrier never reaches Aethex. Some number types deliver text unevenly across borders, US toll-free being the common surprise, so test the exact route you intend to use before building on it.
WHAT AN AGENT MAY SAY

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.

Note
The two tool names are reserved only once your account has what they need: a sender, plus messages or a scheduling connection. An agent that cannot text keeps a custom tool of the same name.
Store a message an agent may send
bash
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

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.

Create a campaign
bash
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.

WhatValueApplies to
Stored message body480Characters, checked again once the blanks are filled. Same ceiling for a campaign body.
One filled-in value120Characters, per blank the agent fills.
A free-text reply1600Characters, for an operator reply and for an agent's reply in a thread. Longer text is split into carrier segments.
Texts per call30A 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 messages25For the whole account, not per agent. Retiring one frees a slot and leaves it readable.
Test sends5 / hPer account, on POST /sms-messages/{id}/test.
Agent replies per thread20 / 10 minPast this the thread goes to a person instead of round again.
Conversation window24 hHow far back an agent reads its own thread when replying.
Campaign rate1-600 / minMessages 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.