Preview: SendMock has no stable release yet. These docs follow develop.
SendMock
SMS

Replies and WhatsApp

Reply as the contact, see your app's reply webhook and TwiML answers, and use WhatsApp and media.

Try it in the demo.

What it solves

  • Your app answers incoming SMS (STOP, YES, a support chat), but you cannot text your dev machine.
  • Your webhook verifies X-Twilio-Signature and answers with TwiML; you want to see both sides.
  • Your Twilio number has a fallback URL; you want to see it used when the main URL fails.

Reply as the contact

In a conversation, type in Reply as recipient and press Send reply (or Enter). Attach files adds images or files, which arrive as MMS or WhatsApp media. The message is stored as incoming and fires sms.inbound.

Where the reply goes

Like a real provider, SendMock looks at the number the contact wrote to (set up in Numbers):

  1. If the number belongs to a Messaging Service whose inbound handling is "Send a webhook", the service's request URL is called.
  2. Otherwise the number's inbound URL is called, in the format set on the number (Twilio, Vonage, Plivo, ... or native JSON), with X-Twilio-Signature for Twilio.
  3. If that call fails, the fallback URL is called with the same request plus ErrorCode and ErrorUrl, as Twilio does. Nothing is retried after that; use Resend reply webhook once your handler is fixed.
  4. A number without an inbound URL sends the message to rules only (Webhooks > Rules on sms.inbound). With both set, both receive it.

What your app receives

In Twilio's format (the default), the same "A message comes in" request Twilio sends, so the handler you run in production works unchanged:

  • POST as application/x-www-form-urlencoded, or GET with the fields in the query string when the number's method is GET. A Messaging Service always uses POST.
  • Fields: MessageSid, SmsSid, SmsMessageSid, AccountSid, MessagingServiceSid (when the number is in a service), From, To, Body, NumMedia, NumSegments, SmsStatus, ApiVersion, plus MediaUrl{N} and MediaContentType{N} for each file (links to SendMock's copy) and ProfileName and WaId on WhatsApp.
  • Headers: X-Twilio-Signature (signed with the auth token in Settings > SMS providers > Twilio), I-Twilio-Idempotency-Token and User-Agent: TwilioProxy/1.1.

What your app can answer

Answer with TwiML as text/xml, application/xml or text/html, as Twilio reads them. A text/plain answer is sent back to the contact as the reply text.

<Response>
  <Message statusCallback="/sms/status">Thanks, your booking is confirmed.</Message>
</Response>
TwiMLWhat happens
<Message> with text, or <Body> and <Media>a reply in the same conversation, from the number that was texted to the contact, delivered like any sent message
to and fromsend the reply to or from another number
statusCallbackstatus callbacks for the reply, in Twilio's format
action (and method)called once the reply is sent, with MessageStatus; its TwiML replaces the verbs left in the document
<Redirect> (and method)fetches TwiML from another URL; the verbs after it are skipped
an empty <Response/>, an empty body or 204no reply

Several <Message> verbs send several replies, in order. URLs can be relative to the URL that answered. One incoming message makes at most 10 TwiML requests, redirects and actions included, so two handlers that redirect to each other stop.

When the call fails

Failures carry Twilio's error codes, shown on the message and in Message details > Reply webhook:

What happenedCode
an answer other than 2xx, or none within 15 seconds11200 HTTP retrieval failure
the connection was refused or reset11205 HTTP connection failure
the body is not a TwiML <Response> document12100 Document parse failure
any other content type, such as JSON12300 Invalid Content-Type

The result, on screen

  • Under the incoming bubble: "Reply webhook 200", "Reply webhook 200, 1 TwiML reply", "Reply webhook failed: 11205 HTTP connection failure", "Fallback 200 after 11205", or "Rules only".
  • If your app answered with TwiML <Message>, the answer appears in the conversation with a TwiML reply badge that jumps to the message it answers.
  • Message details > Reply webhook shows each attempt (primary or fallback), the request with its form fields and headers (signature included), the response or the error with its Twilio code, the parsed TwiML verbs, and Resend reply webhook.

WhatsApp and media

  • Send to or from whatsapp:+8801700000000 through the Twilio API and the conversation shows the WhatsApp badge and styling.
  • Media (MediaUrl on Twilio, media_urls or inline media on the native API) shows as thumbnails and file chips in the bubble, with an MMS or Media badge and a media list in Message details.
  • Simulate read on an outgoing message fires sms.opened and turns the ticks blue, for read receipts. Only outgoing messages can be read: opening a message your app received answers 409.

WhatsApp Cloud API

Apps that call Meta's WhatsApp Business Cloud API directly send to SendMock the same way: change the host https://graph.facebook.com to the provider port, http://127.0.0.1:8026 (or http://127.0.0.1:8025/providers/whatsapp_cloud), and keep the path.

  1. Send as your app does, for example with curl:

    curl -X POST http://127.0.0.1:8026/v21.0/106540352242922/messages -H "Authorization: Bearer any" -H "Content-Type: application/json" -d '{"messaging_product":"whatsapp","to":"16505551234","type":"text","text":{"body":"Your order shipped"}}'
  2. The answer is Meta's: messaging_product, contacts with the wa_id, and messages with a wamid. id (message_status: "accepted" for a template).

  3. The message shows in SMS as a WhatsApp conversation from the provider's business number (+15550783881 unless you change the phone_number credential in SMS > Providers > WhatsApp Cloud).

Every message type is stored with a body you can read: text, templates (Template order_update (en_US) and their body parameters), images, video, audio, documents and stickers (their caption, with media links downloaded as the file), locations, contacts, interactive buttons and lists, and reactions. A request Meta would refuse (no messaging_product, no to, an unknown type, a missing text.body) gets Graph's error object with code 100 and nothing is stored. { "messaging_product": "whatsapp", "status": "read", "message_id": "wamid..." } marks the conversation read and answers { "success": true }. The magic numbers work here too: a refused number answers code 131026, "Message undeliverable".

Media works as on Meta: upload a file with POST /v21.0/<phone_number_id>/media (multipart, with messaging_product=whatsapp, type and file) and get its id; GET /v21.0/<media_id> answers its url, mime_type, sha256 and file_size; the url downloads the file; DELETE /v21.0/<media_id> removes it. Send an image, video, audio file, document or sticker by that id (or by a link), and the message carries the file. Uploaded files are kept like messages: pruned with them, cleared by a reset, and deleted with their project.

Webhooks to your app

Meta sends one app callback URL every status and every reply. Set it in SMS > Providers > WhatsApp Cloud, as the webhook_url credential, and SendMock does the same:

  1. Set webhook_url to your handler, for example http://127.0.0.1:3000/whatsapp/webhook, and save.
  2. On the Callbacks tab, press Verify callback URL. SendMock calls your URL with GET ?hub.mode=subscribe&hub.verify_token=<verify_token>&hub.challenge=<number>, as Meta does, and says whether your handler answered the challenge.
  3. Send a message. Each status (sent, delivered, read, and failed with error 131026 for an undeliverable number) is posted to your URL in Meta's whatsapp_business_account envelope, signed with X-Hub-Signature-256: sha256=<HMAC-SHA256 of the body with the app_secret credential>. They are retried and listed on the message like any status callback.
  4. Reply as the customer in the conversation (to the business number, +15550783881 by default). The reply is posted to the same URL as a messages change, signed the same way.

The envelope uses the phone_number_id and business_account_id credentials for metadata.phone_number_id and the entry id. The same format is the whatsapp.cloud template in Webhooks > Templates, for rules that send other events in Meta's shape (they read the app secret from the whatsapp_cloud_app_secret variable).

Step by step: Recipe: two-way SMS.

From the CLI, API and AI

On this page