Skip to content

Add send and receive messages guide - #819

Merged
Devon-White merged 5 commits into
mainfrom
devon/send-and-receive-messages
Oct 29, 2026
Merged

Devon-White merged 5 commits into
mainfrom
devon/send-and-receive-messages

Conversation

@Devon-White

@Devon-White Devon-White commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds one guide for sending SMS/MMS and handling incoming messages, following the machine-detection guide's approach-first layout. Closes #766.

  • New page: messaging/send-and-receive-messages. It covers:
    • SWML: text a caller during a call with send_sms, reply to incoming texts with reply, route replies by keyword with switch and request, and track delivery with status_url.
    • Relay: send_message/sendMessage, on_message/onMessage, a Relay Application topic on the number, and delivery listeners.
    • REST: POST /api/messaging/messages, status_callback with custom_variables, and the status callback fields.
    • Prerequisites: 10DLC/TCR and toll-free verification, free trial limits, and opt-outs, linked as warnings in Prepare.
  • Cross-links to the guide: the messaging overview's "Choose an API" cards, the WhatsApp send page, the Server SDKs Relay client guide (guides/relay-client), and fern/llms.txt.
  • Out of scope per the issue's direction: the Compatibility API and Call Flow Builder.
  • No changelog entry, since this is documentation work.

Notes for reviewers

  • @signalwire/sdk@2.0.5 has no messaging SWML methods and no REST method to send a message (its RestClient has no messages resource; 3.x adds one). Those two sections are Python + YAML/JSON and Python + cURL; TypeScript readers are pointed to the hosted script and the Relay sendMessage() section.
  • REST MMS media limit is 7, Relay and SWML allow 8. The REST Messages API rejects 8 media URLs, while Relay send_message, SWML send_sms, and SWML reply accept 8. The guide documents each surface's real limit. This looks unintended and needs an engineering decision. The REST reference's media description still says "Maximum 8 items" and is not changed here.
  • TS SwmlBuilder reference: its dynamic-verb list shows recordCall(), but 2.0.5 and 3.7.1 name builder methods after the schema verb (record_call(), send_sms()). Separate fix.
  • Python's SWML builder has no messaging methods either. The reply server uses add_verb("reply", …) with schema_validation=False, and the code comments say so. Neither SDK ships the messaging SWML schema.
  • Dashboard labels I couldn't verify: "Used For → Messaging", "External URL", and "Hosted Script" for messaging SWML Scripts.
  • Unconfirmed: whether the Relay client's token needs the Messaging permission.
  • Left alone:
    • The SMS overview's "Try it out" still uses the legacy @signalwire/realtime-api.
    • The Server SDKs guides/send-messages stub isn't in the nav.
    • The TS SDK's jsdoc sendMessage example uses to/from instead of toNumber/fromNumber. That's an SDK fix.
  • SDK version bump: on TS 3.7.1 the text-during-call sample's object-form calling.dial no longer type-checks, since it becomes positional dial(from, to, {...}). Handle it in the cross-guide version bump, not here.

Verification

  • yarn fern-md-check: all MDX files valid.
  • Samples: every sample was run or checked on the pinned versions, signalwire-sdk==3.4.1 and @signalwire/sdk@2.0.5.
    • JSON and YAML parse, and each YAML matches its JSON.
    • Python passes py_compile and pyflakes; TypeScript passes tsc --strict.
    • Builder output matches the JSON samples.
    • The Python reply server was run locally and returns the document over GET with basic auth.
  • Links and anchors: checked by hand against slug: frontmatter.

Preview

  • /docs/platform/messaging/send-and-receive-messages
  • /docs/platform/messaging
  • /docs/platform/messaging/whatsapp/send-messages
  • /docs/server-sdks/guides/relay-client

One guide for sending SMS/MMS and handling incoming messages across
SWML, Relay, and REST, with keyword routing and delivery tracking.
Links it from the messaging overview, WhatsApp send page, Server SDKs
Relay client guide, and llms.txt.

Closes #766
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

@hey-august hey-august mentioned this pull request Oct 22, 2026
1 of 13 tasks
@Manny-r31

Copy link
Copy Markdown
Collaborator

Review verdict: Questions (three items to settle before merge; branch is also behind main)

Checked against prime-rails main (775a2b5), mod_infrastructure (268f330) and the in-repo SDK references. CI passes. Verified: status callback fields and id semantics (relay/message_callback_serializer.rb, api/rest/messaging/message_serializer.rb), custom_variables (up to 20 pairs), verified caller IDs can't send, the 10DLC / toll-free / free-trial wording, Messaging SWML reply/switch/request and send_sms (swml.c), the Python Relay and REST calls, and all links and anchors.

Questions

  1. "up to 8 public media URLs" for REST MMS (send-and-receive-messages.mdx ~L859). app/concepts/api/rest/messaging/message/contracts/create.rb:118 is return if media.count < RelayMessage::MAX_MEDIA (MAX_MEDIA = 8), so 8 URLs are rejected and the REST maximum is effectively 7. Relay and SWML reply use > MAX_MEDIA and allow 8. Either the doc should say 7 for REST, or this is an off-by-one in prime-rails that the owning team should fix. Flagging so we don't publish "8" for REST until that's decided.
  2. TypeScript REST signatures (~L209, L857-858): client.calling.dial({from, to, swml}) and "The TypeScript SDK can't send a message over REST yet". The in-repo TS reference documents positional calling.dial(from, to, options) and a working client.messages.create(to, from, {...}). make-and-receive-calls.mdx (L212) carries a Note that 2.0.5 takes an options object; suggest the same Note here and "in 2.0.5" instead of "can't ... yet".
  3. TS builder .send_sms({...}) (~L201): the TS SwmlBuilder reference says dynamic verbs are camelCase (recordCall()), so this would be .sendSms(...). Does 2.0.5 accept the snake_case form?

Minor

  1. The state table (~L56) omits read (models/relay_message.rb:23-31), which WhatsApp messages can reach and the guide links to WhatsApp.
  2. The Relay "Track delivery" snippet attaches message.on(...) after await send_message returns, so early states (initiated, sent) can be missed. Suggest on_completed in send_message() or a short note.

- Add the read state for WhatsApp messages to the state table
- Document REST MMS media limit as 7 and Relay as 8, matching the backend
- Add the 2.0.5 dial() signature Note and fix the TypeScript REST gap wording
- Show the Relay delivery listener in full programs, registered before any await
@Devon-White

Devon-White commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

@Manny-r31 thanks for the review. Merged main in first (5f54b53), then addressed everything in d12ed3e.

  1. REST MMS media limit. Confirmed: the REST Messages API rejects a request with 8 media URLs (its error text still says "up to 8"), while a Relay send_message, a SWML send_sms, and a SWML reply all accept 8 and reject 9. The guide now documents each surface's real limit: "up to 7" with a 422 for 8 or more under REST, and "up to 8" under Relay. The 7-vs-8 difference looks unintended and needs an engineering decision; if REST is aligned REST to 8, the REST sentence goes back to 8. The REST reference's media description still says "Maximum 8 items"; I left the spec alone in this PR and listed it under Notes for reviewers.
  2. TypeScript REST signatures. In @signalwire/sdk@2.0.5, client.calling.dial() takes one options object (dial(params?: any), posted as-is), so the sample stays as written and type-checks. Positional dial(from, to, options) only arrives in later releases (3.x). I added the same dial() signature Note as make-and-receive-calls. 2.0.5's RestClient has no messages resource at all, since messages.create(to, from, …) also arrives in 3.x. The sentence now reads "@signalwire/sdk@2.0.5 has no method for this request" and points TypeScript readers to Relay sendMessage() or a direct request.
  3. .send_sms({...}) vs .sendSms(...). No change. 2.0.5's SwmlBuilder installs one method per schema verb under the verb's own name, and its generated typings declare send_sms(config). There's no sendSms, and 3.7.1 is the same. I ran both builder chains (Python and TypeScript), and their output matches the JSON sample exactly. The TS SwmlBuilder reference's recordCall() example is wrong for the same reason (it's record_call()); that's a separate fix outside this PR.
  4. read state. Added. A WhatsApp message can move sent → delivered → read, and SMS and MMS never reach it. The table has a read row, and the "final" sentence now says delivered/undelivered/failed are final for SMS and MMS, while a WhatsApp message can still go from delivered to read.
  5. Relay "Track delivery" ordering. Restructured, with a short note. In both SDKs, send_message()/sendMessage() only starts tracking the message after the send request returns. A state reported before that never reaches any listener, and on_completed doesn't help either: it's attached at the same moment and only fires on a terminal state. The real hazard is placing message.on(...) after another await (such as wait()). The section now shows the full send program in both languages, with message.on(...) registered right after the send call returns and before any other await (highlighted). The prose says why. on_completed/onCompleted is still mentioned for acting on the final state.

Checks run: yarn fern-md-check (all MDX valid). I extracted every block: Python py_compile + pyflakes on signalwire-sdk==3.4.1 (the only finding is the intentional changed-lines snippet under Track delivery via REST), TypeScript tsc --strict against @signalwire/sdk@2.0.5, JSON/YAML parse, and bash -n. Every YAML matches its JSON. The Python and TypeScript builder chains match the JSON sample. Anchors for the touched sections were checked by hand.

@Devon-White
Devon-White merged commit 21e1063 into main Oct 29, 2026
2 checks passed
@Devon-White
Devon-White deleted the devon/send-and-receive-messages branch October 29, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Send and receive messages

3 participants