Skip to content

Template

An SMS template is a single <rc-sms> element. It exists in two equivalent representations — XML and JSON — that convert to each other without loss. Either can be written by hand (or generated by an LLM), or assembled in code using the sms builder namespace.

Every template must include an unsubscribe footer — see Unsubscription. The examples below include it.

XML

The body of <rc-sms> is a plain SMS RFM string. Placeholders use the ::placeholder{…} directive; the content model documents the syntax for links and all placeholder types. The ::unsubscribe directive expands to the required footer — see Unsubscription.

xml
<rc-sms>Your order has shipped!
Account: ::placeholder{type="Subscriber" original="[Subscriber:email]" name="Email" value="email"}
::unsubscribe</rc-sms>

xmlToSms() parses this format into an SmsDocument; safeXmlToSms() is the non-throwing variant. Both validate structure — see Validation.

JSON

The JSON representation is the canonical format the Rule API accepts and returns. The content field is an SmsContentJson — a flat sequence of nodes; see Content for the full content model.

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "tagName": "rc-sms",
  "content": {
    "type": "sms",
    "content": [
      { "type": "message", "text": "Your order has shipped!\nAccount: " },
      {
        "type": "placeholder",
        "attrs": {
          "type": "Subscriber",
          "name": "Email",
          "original": "[Subscriber:email]",
          "value": "email"
        }
      },
      {
        "type": "message",
        "text": "[Subscriber:unsubscribe_text]",
        "attrs": { "is-unsubscribe": true }
      },
      {
        "type": "placeholder",
        "attrs": {
          "type": "Link",
          "original": "[Link:Unsubscribe]",
          "name": "Unsubscribe",
          "value": null,
          "is-unsubscribe": true
        }
      }
    ]
  }
}

Validate a JSON document with validateSmsDocument() (throws on failure) or safeValidateSmsDocument() (returns a result object) — see Validation.

Programmatic

Build the same document with createSmsDocument() and the sms builder namespace. createUnsubscribeNodes() produces the required footer — see Unsubscription.

typescript
import { sms, createSmsDocument } from '@rule/rcml';

const doc = createSmsDocument({
  content: sms.createContent({
    nodes: [
      sms.createMessageNode({ text: 'Your order has shipped!\nAccount: ' }),
      sms.createSubscriberPlaceholder({ field: 'email' }),
      ...sms.createUnsubscribeNodes(),
    ],
  }),
});

createSmsDocument also accepts an SMS RFM string directly as content — convenient when content is authored as a plain string or generated by an LLM:

typescript
const doc = createSmsDocument({
  content:
    'Your order has shipped!\n' +
    'Account: ::placeholder{type="Subscriber" original="[Subscriber:email]" name="Email" value="email"}\n' +
    '::unsubscribe',
});

createSmsDocument validates the document and throws SmsDocumentBuildError on failure. Use safeValidateSmsDocument for the non-throwing variant — see Validation.