SMS Channel

Send and receive SMS through the same Webhook & Send API used for the other channels — same authentication, idempotency, signing secret, and response shape. This page covers the SMS-specific details; the shared envelope, signing, and core actions live on the Webhook & Send API page.

Add-on · $29/month · Requires Professional+ plan

Overview

SMS uses the same authentication, idempotency, signing secret, and response shape as every other channel — just a different event type and an extra routing field (to_phone).

ℹ️

Requirements: SMS access on your tier (Professional or Business) and a provisioned Twilio number under Settings → SMS in the portal. Without a provisioned number, the sms.message_received checkbox on the Webhook & Send API page is disabled.

Inbound vs. outbound

DirectionHow it worksCounts toward SMS limit?
Inbound (customer texts your number)Twilio → ChatGenius → HMAC-signed POST to your endpoint with event_type: "sms.message_received"No — inbound is free
Outbound (you send via Send-Reply API)Your server → ChatGenius → Twilio → recipient. Triggered by action: "send_message" with to_phoneYes — each successful outbound API call counts as 1 toward your monthly SMS quota, regardless of how many segments Twilio splits the message into

Supported actions

SMS supports only the send_message action. The react, unreact, and typing_on actions are Meta-only and are rejected when used with an SMS conversation.

Pure-relay mode

To handle all SMS conversations yourself with no AI from ChatGenius, either disable the SMS AI Assistant under Settings → SMS → AI Assistant (SMS-only), or enable Global External Reply Mode to pause AI across every channel at once (see Global External Reply Mode). With relay on:

  • Inbound SMS still hits your endpoint with the full payload — signed, validated, ready to process
  • ChatGenius does not generate any AI reply
  • You can still send replies via the Send-Reply API (using to_phone or conversation_id)
  • The conversation and message history are still recorded in your portal so your team has visibility

Inbound Payload

When a customer texts your provisioned ChatGenius number, ChatGenius forwards an sms.message_received event with the standard envelope plus an SMS-specific message.sms sub-block. source.channel is always twilio_sms.

JSON — sms.message_received example
{
  "event_id":    "7f7a80c84bf17c4b5b72974c97365a77",
  "event_type":  "sms.message_received",
  "occurred_at": "2026-04-27T23:22:10Z",
  "source": {
    "platform": "sms",
    "channel":  "twilio_sms"
  },
  "conversation": {
    "id": 119301
  },
  "customer": {
    "platform_user_id": "+15551234567",
    "phone":            "+15551234567"
  },
  "message": {
    "id":          "SMdb4342728f73bca6a83770a705e27b31",
    "text_raw":    "Hi, what time do you open Saturday?",
    "attachments": [],
    "sms": {
      "to_phone":     "+15559876543",
      "from_phone":   "+15551234567",
      "num_segments": 1,
      "num_media":    0,
      "encoding":     "GSM-7"
    }
  },
  "meta": {
    "version":         "v1",
    "payload_profile": "full",
    "generated_at":   "2026-04-27T23:22:10Z"
  }
}

SMS-specific fields

FieldTypeDescription
customer.platform_user_idstringThe customer's phone number in E.164 format. Always present.
customer.phonestringSame as platform_user_id — a convenience for SMS payloads. Present in full payload profile only.
message.idstringThe Twilio MessageSid. Stable identifier; use for deduplication.
message.text_rawstringThe full SMS body. Multi-segment SMS arrives concatenated — you receive the joined text, not individual segments.
message.sms.to_phonestringYour ChatGenius (Twilio) number that received the SMS, in E.164 format
message.sms.from_phonestringThe customer's phone number in E.164 format (same as customer.platform_user_id)
message.sms.num_segmentsintegerHow many SMS segments the inbound message used (informational)
message.sms.num_mediaintegerThe number of media items Twilio reported. The count is forwarded for visibility, but MMS media URLs are not included in v1 — message.attachments is always an empty array for SMS in v1.
message.sms.encodingstringGSM-7 or UCS-2 — detected from the message body content

Signature verification, retry behavior, and timestamp validation are identical to every other channel — same X-SumGenius-Signature header, same per-account signing secret, same algorithm. See Verifying Signatures.

Sending SMS

Send an SMS by calling the same Send-Reply endpoint used for DMs, with to_phone instead of conversation_id. The platform is auto-set to sms.

POST /api/meta/webhook-send.php

Request body

FieldRequiredDescription
actionoptionalDefaults to send_message. SMS supports only this action.
idempotency_keyrequiredYour unique key for this request (max 128 chars). Same key returns "status": "duplicate" — no second SMS sent.
to_phonerequired*Recipient phone number in E.164 format (e.g. +15551234567). *Required if conversation_id is not provided. Numbers are auto-normalized — spaces, dashes, parens, and a leading 1 without + are accepted.
conversation_idrequired*The conversation.id from a received SMS event. *Use this OR to_phone.
message_textrequiredThe SMS body. Max 1,600 chars (GSM-7) or 1,530 chars (UCS-2). See Encoding & Segments.

The sender number is automatically set to your provisioned sms_phone_number. You cannot specify from_phone — sender selection is server-controlled to enforce A2P 10DLC compliance.

curl
curl -X POST https://sumgenius.ai/api/meta/webhook-send.php \
  -H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "refill-reminder-001",
    "action":          "send_message",
    "to_phone":        "+15551234567",
    "message_text":    "Your prescription is ready for pickup. Reply STOP to opt out."
  }'

SMS-specific success response fields

SMS responses follow the same Response Format as DM responses, with these additions:

FieldDescription
platformAlways "sms"
to_phoneEchoes the recipient phone number (only present when to_phone was sent in the request)

Encoding & Segments

SMS uses two encodings depending on the characters in your message body. ChatGenius detects the encoding automatically and validates against the corresponding cap.

EncodingTriggered bySingle segmentConcatenated segmentHard cap
GSM-7Latin alphabet, common punctuation, basic European accents160 chars153 chars1,600 chars
UCS-2Any emoji, non-Latin script (Arabic, Chinese, etc.), or smart quotes70 chars67 chars1,530 chars
⚠️

One emoji can drop your character limit by more than half. A 160-character GSM-7 message that fit in 1 segment becomes a 70-char-per-segment UCS-2 message the moment a single emoji is added — same text, now 3 segments. Segments matter for carrier cost and throughput, but ChatGenius counts each successful outbound Send-Reply API call as 1 SMS quota unit.

Quota counting rules

  • Inbound SMS — free, never counted (regardless of segments)
  • Outbound SMS — each successful Send-Reply API call counts as 1 toward your monthly quota, regardless of how many segments Twilio splits the message into
  • A 200-char GSM-7 message = 1 quota unit (Twilio sends 2 segments; we count 1)
  • A 200-char UCS-2 message = 1 quota unit (Twilio sends 3 segments; we count 1)
ℹ️

Segment count still matters for Twilio's own carrier-level throughput limits — sending many long messages quickly can hit Twilio's per-second segment cap before it hits our monthly quota. Keep messages concise where possible.

Opt-out handling (STOP / START)

SMS opt-out is handled automatically at multiple layers — you don't implement any of this:

  • Twilio carrier-level Advanced Opt-Out: when a recipient texts STOP, Twilio blocks all future outbound SMS to that number from your Messaging Service, regardless of any application-layer check.
  • ChatGenius app-level fast-fail: we mirror the opt-out so future API calls return 410 with "Recipient has opted out (replied STOP)." immediately, without consuming a Twilio API call.
  • Re-subscription: if the recipient texts START, UNSTOP, or YES, both layers re-enable sending automatically.

You should still include opt-out language in your initial messages (e.g. "Reply STOP to opt out.") to comply with carrier and CASL/TCPA requirements.

SMS Error Reference

SMS errors follow the same general error response shape. The codes specific to SMS are:

HTTPTriggerError message
400to_phone doesn't match E.164to_phone must be in E.164 format (e.g. +15551234567).
403SMS not enabled on your account tierSMS access is not enabled for this account.
410Recipient has opted out (replied STOP)Recipient has opted out (replied STOP).
412No Twilio number provisioned for the accountSMS not provisioned for this account. Provision a Twilio number first.
422Message exceeds GSM-7 (1,600) or UCS-2 (1,530) cap, or an unsupported action was usedmessage_text exceeds 1600 characters for SMS GSM-7 (1700 provided).
429Monthly SMS quota exceededMonthly SMS limit reached for this account.
502Twilio returned an error (network, invalid number, landline, region not enabled, etc.)SMS delivery failed: <Twilio error message>.
503SMS add-on is temporarily disabled platform-wideSMS webhook add-on is temporarily disabled.

Permanent failures (invalid recipient, opted out, over-length) are not retried — they go straight to dead_letter. Transient failures (Twilio 5xx, 429, network errors) are retried with the same backoff schedule used for Meta deliveries.