Recipes← all recipesView on GitHub

Forward an inbound SMS to other numbers

MessagingSMS forwarding to a group

Send every text that arrives on your number on to a list of other numbers with the Messaging reply method, prefixed with the sender, without replying to the sender.

Also called SMS forwarding, text message forwarding, forward texts to a cell phone, SMS to group

swmlwebhooksforwarding

The claim

The inbound message webhook carries from, to and body, and the spec requires all three. The handler answers with a Messaging SWML document holding one reply for every recipient on the list. to is the recipient, from is the number the text arrived on, and the body is the sender’s number, a colon, and their words.

Why it holds

reply is the method that sends a message from a messaging document. Its to defaults to message.from and its from to message.to, so a reply with no to answers the sender. Naming to sends it somewhere else, which is why the reference’s own example of this carries the heading “Forward an inbound message to another number”.

Three rules keep it a forwarder rather than a loop. The sender is left out, so the team can talk on the same line. The receiving number is left out, because texting the line that fired this webhook fires it again. A repeated entry in the list sends one copy, not two.

How it works

def forward_document(sender, received_on, body, recipients=None):
    steps = []
    if keyword(body) not in STOP_WORDS:
        for recipient in targets(sender, received_on, recipients):
            # `to` is what makes this a forward rather than a reply
            steps.append({"reply": {"to": recipient, "from": received_on,
                                    "body": forwarded_body(sender, body)}})
    return {"version": "1.0.0", "sections": {"main": steps}}

What a customer’s text renders, with two people on shift:

{"version": "1.0.0", "sections": {"main": [
  {"reply": {"to": "+15550100001", "from": "+15550001111",
             "body": "+14155550123: Is my bike ready?"}},
  {"reply": {"to": "+15550100002", "from": "+15550001111",
             "body": "+14155550123: Is my bike ready?"}}]}}

Two limits shape the body. An SMS body maxes out at 1600 characters, and the sender prefix counts, so a full-length inbound is truncated to leave room. A STOP is not forwarded at all. The carrier keywords are an opt-out from you, not a message for the team, and handle-opt-outs-yourself is where they go. The match is the trimmed, lowercased word with trailing punctuation removed, so STOP. counts and stop please does not.

The Messaging method set is reply, receive, execute, return, transfer, goto, label, switch and request. send_sms is not in it; that is the Calling method for texting from inside a call, which text-the-caller-during-the-call uses.

Limitations

The verifier proves the document, not delivery. A forwarded text from a 10DLC number is A2P traffic and needs a registered campaign, and the receiving line must be messaging-enabled to be a from.

Two forwarders pointed at each other stop, because each one drops the sender. A ring of three does not: every hop’s sender is the previous line, never the next one. Do not point one of these at another.

Media is dropped. An inbound MMS carries media URLs in the payload, and reply takes a media list of up to eight attachments. Carrying them over is the first extension; the URLs are platform-hosted and unguessable.

Recipients are a list in the environment. A roster that changes by shift is a lookup in targets, which is the one function that decides who gets a copy.

What to change first

Delete "to": recipient from the reply and run the verifier. The document is still a valid Messaging document and the assertion fails. That is the point: without to, reply answers the sender, and the forwarder becomes an echo.