# The nativeMsg Playbook

Welcome to your team’s developer platform

<h2 align="center">📚 The nativeMsg Playbook 📚</h2>

<h4 align="center">Your go-to resource for all things nativeMsg</h4>

<p align="center">From initial setup through advanced deployment, this guide brings together implementation documentation, messaging frameworks, and resources like the RCS Experience Schema.<br><br>It's everything you need to build, launch, and deploy RCS for Business experiences with nativeMsg.</p>

<table data-card-size="large" data-view="cards"><thead><tr><th align="center"></th><th align="center"></th><th align="center"></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td align="center"><h4><i class="fa-circle-house">:circle-house:</i></h4></td><td align="center"><strong>Get Started</strong></td><td align="center">Get started with the developer platform in 5 minutes.</td><td><a href="https://playbook.nativemsg.com/get-started/">Getting Started</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2F2ozQ3eIVA3S1QJO6BxLD%2Fget%20started%20light.png?alt=media&amp;token=50340f9d-a3fc-4474-af93-b18f0d03f512">get started light.png</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FzxeXl7r11WAeDibOhzhb%2Fget%20started%20dark.png?alt=media&amp;token=d925a84b-3d63-4239-a93b-abcc8f2e454c">get started dark.png</a></td></tr><tr><td align="center"><h4><i class="fa-square-code">:square-code:</i></h4></td><td align="center"><strong>RCS Experience Schema</strong></td><td align="center">Learn more about hosting the developer platform.</td><td><a href="https://playbook.nativemsg.com/rcs-experience-schema/">RCS Experience Schema</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FMw4R3eQG7lkVXUeyqREW%2Fschema%20light.png?alt=media&amp;token=93d3c16f-34f4-498c-b413-085339616dec">schema light.png</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FJ5GSTtUutxzR1B1u5Shu%2Fschema%20dark.png?alt=media&amp;token=1ffd356b-f794-4edf-aab7-648a9f69c0bf">schema dark.png</a></td></tr><tr><td align="center"><h4><i class="fa-terminal">:terminal:</i></h4></td><td align="center"><strong>API reference</strong></td><td align="center">Browse, test, and implement APIs easily. </td><td><a href="https://docs.nativemsg.com/">https://docs.nativemsg.com/</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FuZjlYcClKk4HMs5hGraD%2Fapi%20light.png?alt=media&amp;token=281996bb-d634-4499-aca3-f637eb5f3596">api light.png</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FRr4uhUA0WMWW1bFLLoaC%2FAPI%20dark.png?alt=media&amp;token=30e0ed23-1200-42d6-a187-f1465456f1bc">API dark.png</a></td></tr><tr><td align="center"><h4><i class="fa-book-open-cover">:book-open-cover:</i></h4></td><td align="center"><strong>User Guides</strong></td><td align="center">Everything you need to know to set up and manage messaging campaigns on the platform.</td><td><a href="https://support.nativemsg.com">https://support.nativemsg.com</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FCPyGKB6uZnUchp5ZNB8g%2Fuser%20guides%20light.png?alt=media&amp;token=bc2cd1ab-0b12-49dd-a535-4d972c3995fe">user guides light.png</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FfOuyBhawOIGhFgnBTw4G%2Fuser%20guides%20dark.png?alt=media&amp;token=2e8a9324-ac47-4046-b18d-a44553a53ea9">user guides dark.png</a></td></tr></tbody></table>

***

<h3 align="center">Quick Links</h3>

<table data-view="cards"><thead><tr><th align="center"></th><th align="center"></th><th align="center"></th><th data-hidden data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td align="center"><h4><i class="fa-user-question">:user-question:</i></h4></td><td align="center"><strong>FAQs</strong></td><td align="center">You've got questions? We've compiled answers. Check them out here.</td><td><a href="https://support.nativemsg.com/support/solutions/67000259882">https://support.nativemsg.com/support/solutions/67000259882</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FXPkv1sWr79QcXVqDIuPv%2FFAQs.png?alt=media&amp;token=3d988b99-c20e-4101-b315-a868fd7587bd">FAQs.png</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FDJzKEp5YWneZVc0oO6xw%2Ffaq%20dark.png?alt=media&amp;token=b59374fd-7b68-44e6-a6a3-5218d7600f1c">faq dark.png</a></td><td><a href="https://support.nativemsg.com/support/solutions/67000259882">https://support.nativemsg.com/support/solutions/67000259882</a></td></tr><tr><td align="center"><h4><i class="fa-square-code">:square-code:</i></h4></td><td align="center"><strong>For Devs</strong></td><td align="center">Here are some resources for our more tech-forward users.</td><td><a href="https://docs.nativemsg.com/#introduction">https://docs.nativemsg.com/#introduction</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FiKSe4Gfcqm54Cfk4eNp9%2FFor%20Devs.png?alt=media&amp;token=65077bbb-d38b-4639-afca-b54376e165b2">For Devs.png</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2Fs3dqXD7wZtWSrRDmgaxC%2Ffor%20devs%20dark.png?alt=media&amp;token=3ad02da4-780b-42ac-838c-d00dd32d2abe">for devs dark.png</a></td><td><a href="https://docs.nativemsg.com/#introduction">https://docs.nativemsg.com/#introduction</a></td></tr><tr><td align="center"><h4><i class="fa-screwdriver-wrench">:screwdriver-wrench:</i></h4></td><td align="center"><strong>Submit a Ticket</strong></td><td align="center">Need help troubleshooting an issue? Reach out to our support team.</td><td><a href="https://support.nativemsg.com/support/tickets/new">https://support.nativemsg.com/support/tickets/new</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FXIUxUCv0GXb2VTxx2kCO%2FTicket.png?alt=media&amp;token=a4b5a2c9-74bb-486a-b805-17c8cea71928">Ticket.png</a></td><td><a href="https://4146134891-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoyaVJJzTIB8ApB9CLuzM%2Fuploads%2FTCnDljBBQBkY2NhGvFdz%2Fticket%20dark.png?alt=media&amp;token=c1aa32ab-1a92-476c-8215-1051a4b50466">ticket dark.png</a></td><td><a href="https://support.nativemsg.com/support/tickets/new">https://support.nativemsg.com/support/tickets/new</a></td></tr></tbody></table>

***

<h2 align="center">Need more help?</h2>

<p align="center">If you can't find what you need here, please reach out at <a href="mailto:support@nativemsg.com">support@nativemsg.com</a>. <br>We look forward to assisting you!</p>


# Welcome to nativeMsg

Your central guide for platform setup, conversational design, and maximizing customer engagement across RCS and other texting channels.

{% hint style="info" %}
💡 Tip: Save this page to your bookmarks. You’ll use it throughout onboarding!
{% endhint %}

<table data-view="cards" data-full-width="false"><thead><tr><th align="center"></th><th align="center"></th><th data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th></tr></thead><tbody><tr><td align="center"><h4><i class="fa-traffic-light-go">:traffic-light-go:</i></h4></td><td align="center"><strong>Get Started</strong> ➔</td><td><a href="/get-started/get-started/welcome">Welcome &amp; Overview</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2F9Vzg9mQnKhiLpIKtOoDR%2FChatGPT%20Image%20Oct%208%2C%202025%20at%2001_10_55%20PM.png?alt=media&amp;token=c3c7035e-70a1-4a4c-b1e7-58dc398a10ce">ChatGPT Image Oct 8, 2025 at 01_10_55 PM.png</a></td></tr><tr><td align="center"><h4><i class="fa-clipboard-list-check">:clipboard-list-check:</i></h4></td><td align="center"><strong>Pre Flight Checklist</strong> ➔</td><td><a href="/get-started/get-started/pre-flight-checklist">Pre-Flight Checklist</a></td><td><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FPZB53n8EJGFeOPZ542uS%2FGemini_Generated_Image_32x4lm32x4lm32x4.png?alt=media&amp;token=7960dc6c-2227-4666-9742-7bfa40a5ed5d">Gemini_Generated_Image_32x4lm32x4lm32x4.png</a></td></tr><tr><td align="center"><h4><i class="fa-folder-heart">:folder-heart:</i></h4></td><td align="center"><strong>Onboarding</strong> ➔</td><td><a href="/get-started/get-started/onboarding-steps">Onboarding Steps</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2F2Knx7tZoSxuV31o3PuOw%2FChatGPT%20Image%20Oct%208%2C%202025%20at%2001_07_52%20PM.png?alt=media&amp;token=12c40e50-e1a4-47a1-a6f4-33fc71589a99">ChatGPT Image Oct 8, 2025 at 01_07_52 PM.png</a></td></tr><tr><td align="center"><h4><i class="fa-rocket-launch">:rocket-launch:</i></h4></td><td align="center"><strong>Campaign Setup</strong> ➔</td><td><a href="/get-started/quickstart/functional-overview">Quickstart</a></td><td><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FJutzyOeUfX2cablqK1Ny%2FGemini_Generated_Image_bkrxy5bkrxy5bkrx.png?alt=media&amp;token=0563a795-99e5-4e89-8310-a374aa1b7cc2">Gemini_Generated_Image_bkrxy5bkrxy5bkrx.png</a></td></tr><tr><td align="center"><i class="fa-comment-question">:comment-question:</i></td><td align="center"><strong>Resources</strong> ➔</td><td><a href="/get-started/get-started/support-and-resources">Support &amp; Resources</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FdTV1LsTN6OMzeZ1vB5bG%2FChatGPT%20Image%20Oct%208%2C%202025%20at%2001_14_41%20PM.png?alt=media&amp;token=d8639ee3-167f-4eba-8672-b5ab87e6d7ec">ChatGPT Image Oct 8, 2025 at 01_14_41 PM.png</a></td></tr><tr><td align="center"><h4><i class="fa-head-side-gear">:head-side-gear:</i></h4></td><td align="center"><strong>Knowledge Base</strong> ➔</td><td><a href="/get-started/knowledge-base/knowledge-base">📚 Knowledge Base</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FrK2Uyf38y33Lq5uouDkH%2FChatGPT%20Image%20Oct%208%2C%202025%20at%2003_52_23%20PM.png?alt=media&amp;token=e4b0fa08-88d7-47aa-9f96-2985e67b8814">ChatGPT Image Oct 8, 2025 at 03_52_23 PM.png</a></td></tr><tr><td align="center"><h4><i class="fa-question">:question:</i></h4></td><td align="center"><strong>Submit a Ticket</strong> ➔</td><td><a href="https://support.nativemsg.com/support/tickets/new">https://support.nativemsg.com/support/tickets/new</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FOfyq9pecB5u68oJpHOeJ%2FChatGPT%20Image%20Oct%208%2C%202025%20at%2004_01_47%20PM.png?alt=media&amp;token=a5a8648e-2c06-4ec7-8ee1-487060f1ea74">ChatGPT Image Oct 8, 2025 at 04_01_47 PM.png</a></td></tr></tbody></table>

Need help? We’re always here for you.

* 📧 <support@nativemsg.com>


# Welcome & Overview

We’re so excited to welcome you to nativeMsg!\
\
This guide is your step-by-step resource to help you go from kickoff to go-live smoothly and confidently.

You’ll meet your onboarding team, learn what to expect over the next few weeks, and get access to the resources you’ll need for success.

### The nativeMsg Experience

At nativeMsg, we help you connect with your customers through intelligent, automated conversations across messaging channels.

**Your onboarding journey focuses on three things:**

1. Getting your account ready
2. Setting up your messaging experience
3. Launching your first campaign with confidence

{% hint style="info" %}
💡 *Think of this guide as a roadmap. We’ll show you what to expect, what we’ll handle, and what we’ll need from you.*
{% endhint %}

### Access Your nativeMsg Team

| Role             | Name                    | What They Do                               |
| ---------------- | ----------------------- | ------------------------------------------ |
| Customer Success | <support@nativemsg.com> | Onboarding, Questions, & Technical Support |

### What to Expect

| Phase                               | Description                                                                                                    | Outcome                                         |
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| **Phase 1 — Kickoff & Alignment**   | Meet your Customer Success Manager, confirm goals, and align on scope, and deliverables.                       | Shared success plan and kickoff call complete.  |
| **Phase 2 — Setup & Configuration** | Our team helps you connect systems, verify domains, and ensure compliance with carrier and brand requirements. | Your environment is configured and verified.    |
| **Phase 3 — Validation & Testing**  | Together we validate message flows, templates, and opt-in experiences before launch.                           | Campaigns tested and approved for launch.       |
| **Phase 4 — Launch & Optimization** | You go live! We monitor delivery, engagement, and performance, then refine for success.                        | Successful campaign launch and ongoing support. |

### Get Started

* [ ] &#x20;Schedule your kickoff call
* [ ] &#x20;Decide who will be your key team contacts
* [ ] &#x20;Gather your brand assets (logo, links, sample messages) and submit them

{% hint style="success" %}
✅ *Once these are complete, your onboarding officially begins!*
{% endhint %}

### Communication & Updates

We believe communication should be simple, clear, and proactive.\
Here’s how we’ll keep in touch during onboarding:

| Channel              | Purpose                  | Frequency |
| -------------------- | ------------------------ | --------- |
| Email                | Updates & Task Followups | Ongoing   |
| Check-In Calls       | Progress Tracking        | Weekly    |
| Slack *(Enterprise)* | Quick Collaboration      | As Needed |

### Helpful Resources

| Resource              | Description   | Link                                        |
| --------------------- | ------------- | ------------------------------------------- |
| nativeMsg Help Center | Guides & FAQs | <https://nativemsg.com/category/resources/> |


# Pre-Flight Checklist

**Goal:** Confirm everything is ready before we submit your brand for approval.\
**Estimated Time:** 10 minutes

***

### What This Covers

* Brand and business readiness
* Message and link requirements
* Opt-in / opt-out and privacy checks
* Submission review tips

***

### 🧭 Why It Matters

Before your campaign can go live, carriers require that all brand, message, and consent details are correct and consistent.

> 💡 *Treat this as your “pre-launch audit.” Complete everything here before submitting your campaign registration.*

***

### ✅ Brand & Business Information

* [ ] Use a **branded business email** (no Gmail, Yahoo, or Outlook).
* [ ] Provide your **tax ID** for brand verification.
* [ ] Confirm **legal business name** matches your tax ID and public brand identity.
* [ ] Verify **business address** matches your website and registration forms.
* [ ] Ensure your **website URL** is live and reflects your brand name and services.

> ⚠️ *If any of these don’t match exactly, your submission may be rejected.*

***

### 💬 Messaging & Content

* [ ] Include **at least two realistic sample messages** with brand name.
* [ ] Confirm your **campaign description** matches your use case (e.g., marketing, alerts, support).
* [ ] Verify **no prohibited (SHAFT)** content — Sex, Hate, Alcohol, Firearms, or Tobacco.

> 💡 *The more complete and realistic your samples are, the faster they get approved.*

***

### 🔗 Links & Privacy

* [ ] Ensure your **Privacy Policy** and **Terms of Service** are live on your website with your verified domain.
* [ ] Avoid **link shorteners** (Bit.ly, TinyURL). Use full URLs from your site.
* [ ] Confirm all **links open properly**.

> 💬 F*ull transparency is required — every link and form must be live, clear, and verifiable.*

***

### 🚀 Final Review Before Submission

* [ ] All brand and business info is consistent across platforms.
* [ ] Privacy and Terms links are active and accurate.
* [ ] Campaign registration form is complete (no placeholders).

Your account will then be reviewed by your CSM.

> ✅ *When all boxes are checked, your campaign is ready for submission. Please use the secured form your Customer Success Manager sent via email.*


# Compliance

## Overview

**Goal:** Ensure your brand and campaigns meet carrier and messaging compliance requirements so your messages send smoothly and without interruption.\
**Estimated Time:** 10–15 minutes

***

### What This Covers

* Why compliance matters
* Brand registration (10DLC overview)
* Campaign registration process
* Message content requirements
* How to avoid rejections

***

### ⚖️ Why Compliance Matters

All mobile operators require registration.\
This verifies who you are and what you’re sending — helping protect customers from spam and keeping your campaigns trusted and deliverable.

> 💡 *Compliance = protection. It ensures your brand’s messages are verified, approved, and reliably delivered.*

***

<figure><img src="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FaSa3ts7DUwcoOoaiBqNV%2FChatGPT%20Image%20Oct%208%2C%202025%20at%2002_29_12%20PM.png?alt=media&amp;token=c63f454f-9d9b-4b7c-aa48-490ad48cb12f" alt=""><figcaption></figcaption></figure>

### 🏢 Step 1: Brand Registration

**Goal:** Verify your business identity with mobile operators.

#### What You’ll Need

* Legal business name and DBA (if applicable)
* Employer Identification Number (EIN or tax ID)
* Full business address and phone number
* Live website (matching your business name and domain)
* Privacy Policy and Terms of Service&#x20;
* An example of how customers will opt-in to your messaging (e.g. QR code, script, text xxxx)

#### How It Works

1. Your Customer Success Manager will share a **secure form** to collect your brand details.
2. nativeMsg submits your brand for registration
3. Once approved, your brand receives a **brand ID** — this is required for all message campaigns.

> 💬 *We’ll notify you when your brand is verified.*

***

### 🧾 Step 2: Message Content Requirements

Carriers require certain message elements to ensure transparency and compliance. These are examples. Removing any of these defaults will result in your messaging capabilities being turned off by mobile operators.&#x20;

| Requirement              | Description                                               | Example                                     |
| ------------------------ | --------------------------------------------------------- | ------------------------------------------- |
| **Brand Identification** | Clearly identify your company name                        | “Thanks for signing up with **nativeMsg**!” |
| **Opt-In Language**      | Make sure recipients explicitly agree to receive messages | “Reply YES to receive updates.”             |
| **Opt-Out Option**       | Must include a way to stop messages                       | “Reply STOP to opt out.”                    |
| **HELP Command**         | Must include an info response                             | “Reply HELP for assistance.”                |
| **Link Transparency**    | Avoid public shorteners (bit.ly, tinyurl)                 | Use your verified domain link instead       |
| **Privacy Policy**       | Include working URL                                       | `www.yourcompany.com/privacy`               |
| **Terms of Service**     | Include working URL                                       | `www.yourcompany.com/tos`                   |

> ✅ *We’ll help you review and approve these before launch.*

***

### 🧠 Common Reasons for Rejection

| Issue                 | Why It Happens                  | How to Fix                          |
| --------------------- | ------------------------------- | ----------------------------------- |
| Missing brand details | Business info incomplete        | Double-check EIN/tax ID and address |
| Mismatched URLs       | Domain doesn’t match brand name | Use company domain (not generic)    |
| No STOP/HELP response | Message flow incomplete         | Contact <support@nativemsg.com>     |
| Shortened links       | Public redirectors discouraged  | Use full URL                        |

***

### 🧩 Step 3: Ongoing Compliance Tips

> 🔄 *Compliance isn’t one-and-done — here’s how to stay in good standing.*

* Always get **explicit opt-in** from users before sending messages
* Include your brand name in every outbound message
* Update campaign details if your use case changes
* Keep Privacy Policy and Terms links live and accurate

***

### ✅ Checklist

* [ ] Message templates reviewed (STOP/HELP included)
* [ ] Privacy and Terms links verified
* [ ] Opt-in flow tested

***

> 💡 *We’re here to make compliance simple and successful — so you can launch fast and stay approved.*


# Avoid Common Rejections

**Goal:** Help your team prevent rejections by catching the most common mistakes before submission.\
**Estimated Time:** 5 minutes

***

### What This Covers

* The most frequent rejection reasons
* How to fix them quickly
* Best practices to stay approved long-term

***

### 🚫 Why Campaigns Get Rejected

Mobile operators are strict about approval criteria — even small inconsistencies can cause delays or denials.\
Below are the most common issues and exactly how to fix them.

***

### ⚠️ Common Rejection Reasons & Fixes

| Rejection Reason                       | Why It Happens                                                       | How to Fix It                                                |
| -------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------ |
| **Incomplete Brand Info**              | Missing or mismatched business name, address, or EIN/tax ID          | Ensure all fields match your official documents and website  |
| **Unverified Domain or Generic Email** | Carriers flag free email domains (Gmail, Yahoo)                      | Use your verified business domain (e.g., `@yourcompany.com`) |
| **No Proof of Consent**                | Missing or unclear opt-in disclosure                                 | Provide an example of clear opt-in where users sign up       |
| **Broken or Shortened Links**          | Bit.ly or broken URLs                                                | Replace with full links from your verified domain            |
| **Vague Campaign Description**         | Description doesn’t explain what you’re sending                      | Clearly state the message purpose and audience               |
| **Missing STOP/HELP Commands**         | Auto-responses not tested or incomplete                              | Test STOP/HELP flow before submission                        |
| **Prohibited Content**                 | Content violates SHAFT rules (Sex, Hate, Alcohol, Firearms, Tobacco) | Remove all restricted references or reframe language         |
| **Mismatched Brand vs. Website**       | Business name and domain don’t align                                 | Ensure your legal name and website branding match exactly    |

***

### 🧠 Pro Tips for Fast Approval

> 💡 *Follow these guidelines to make your review smoother and faster.*

* Keep **brand details consistent** everywhere (registration form, website, and privacy policy).
* Always include your **brand name** in sample messages.
* Add a **realistic campaign description** — one sentence that clearly defines purpose.
* Make sure **Privacy Policy** and **Terms links** load quickly and match your domain.
* Test **STOP/HELP** responses before submitting your form.
* Double-check that your **opt-in examples** include “message & data rates may apply.”
* Avoid **reusing content** from unrelated campaigns.

***


# Onboarding Steps

This page outlines your journey with nativeMsg — from setup to launch.   Each phase includes what we’ll handle, what we’ll need from you, and how we’ll measure success together.

### The Four Onboarding Phases

Your onboarding experience happens in four key phases — designed to make your launch simple, transparent, and efficient.

### Phase 1: Kickoff & Alignment

We’ll align on goals, project scope, success metrics, and confirm your use cases.

{% hint style="info" %}
💡 *This is where we turn your objectives into an actionable success plan.*
{% endhint %}

| What nativeMsg Does      | What You’ll Do                        | Success Looks Like                             |
| ------------------------ | ------------------------------------- | ---------------------------------------------- |
| Schedule kickoff call    | Attend the kickoff meeting            | Alignment on goals, timeline, and deliverables |
| Review your use case     | Share use case and messaging examples | Shared success plan created                    |
| Confirm project timeline | Provide key contacts                  | Project plan approved                          |

### Phase 2: Setup & Configuration

We’ll take care of the initial technical setup and ensure your environment is ready for messaging — securely, compliantly, and fully branded.

| What nativeMsg Does                        | What You’ll Do                   | Success Looks Like           |
| ------------------------------------------ | -------------------------------- | ---------------------------- |
| Provide integrations & channel connections | Request assistance if needed     | Platform ready for testing   |
| Complete registration requirements         | Review business information      | Compliance approved          |
| Brand setup (logos, colors, links)         | Provide brand assets (logo, URL) | Campaign assets ready for QA |

> ⚙️ *We’ll keep you updated throughout setup — no technical background needed.*

### Phase 3: Validation & Testing

Now it’s time to test message flows, opt-in/opt-out responses, and ensure everything functions as intended before we go live.

| What nativeMsg Does              | What You’ll Do                         | Success Looks Like             |
| -------------------------------- | -------------------------------------- | ------------------------------ |
| Run QA and message tests         | Review and approve sample messages     | QA passed and signed off       |
| Verify STOP/HELP and opt-in flow | Confirm privacy policy and terms links | Opt-in/opt-out verified        |
| Test message delivery            | Respond to test runs                   | End-to-end validation complete |

🧠 *Testing ensures every interaction meets compliance and brand standards.*

### Phase 4: Launch & Optimization

Your first campaign is live! Your customer success manager will help monitor performance, track message delivery, and ensure your experience is smooth.

| What nativeMsg Does                 | What You’ll Do              | Success Looks Like          |
| ----------------------------------- | --------------------------- | --------------------------- |
| Help you launch your first campaign | Review launch report        | Successful delivery         |
| Monitor engagement & delivery       | Provide feedback on results | Customer feedback collected |
| Schedule follow-up call             | Attend post-launch review   | Optimization plan created   |

🚀 *Congratulations — you’re live! Your CSM will continue to guide you with insights and recommendations.*

### After Launch

Onboarding doesn’t end at launch — it transitions into partnership.\
You’ll continue working with your Customer Success Manager to grow adoption, and plan future campaigns.

| Topic                   | Description                                     |
| ----------------------- | ----------------------------------------------- |
| Regular check-ins       | Monthly meetings with your CSM for optimization |
| Reporting               | Quarterly reports with insights & trends        |
| Expansion opportunities | Explore new channels, integrations, or features |

### Need Help Along the Way?

| Type of Question | Contact           | Method                  |
| ---------------- | ----------------- | ----------------------- |
| Support          | nativeMsg Support | <support@nativemsg.com> |

💬 *We’re here to help — reach out anytime if you get stuck or want feedback.*


# Support & Resources

This page connects you to support channels, guides, and documentation to help you find answers quickly and stay confident throughout your journey.

### Getting Help

You can reach our team through multiple channels depending on your needs. We aim to respond quickly and make sure you always know who to contact.

| Type of Support       | Best For                          | How to Reach Us         | Typical Response |
| --------------------- | --------------------------------- | ----------------------- | ---------------- |
| **Technical Support** | Integrations, delivery issues, QA | <support@nativemsg.com> | Same day         |

⚙️ *If you’re unsure who to contact, email <support@nativemsg.com> — we’ll route it to the right person.*

### Key Resources

| Resource                        | Description                                          | Link                           |
| ------------------------------- | ---------------------------------------------------- | ------------------------------ |
| **Brand Asset Submission Form** | Upload your logo, links, and message examples        | Check your email for the link. |
| **Help Center**                 | How-to articles, FAQs, and setup guides              | support.nativemsg.com          |
| **Compliance Overview**         | Everything you need for registration and brand setup | Read Guide →                   |
| **Training Videos**             | Short video walkthroughs                             | Watch Videos →                 |

💡 *We recommend starting with the Message Templates and Compliance Overview guides if you’re new to messaging campaigns.*

### Escalation & Priorities

We classify issues by priority to ensure the fastest possible resolution.

| Priority        | Example                                       | Response Time     |
| --------------- | --------------------------------------------- | ----------------- |
| 🔴 **Critical** | Outage, delivery failure, or blocked campaign | < 4 hours         |
| 🟠 **High**     | Setup blockers, compliance rejections         | < 1 business day  |
| 🟡 **Medium**   | Configuration or training questions           | 1–2 business days |
| 🟢 **Low**      | General guidance or feedback                  | 2–3 business days |

***

### ✅ Pre-Submission Quick Review

Before submitting your campaign, confirm:

* [ ] Brand and campaign info match your website and documents
* [ ] Opt-in, STOP, and HELP flows tested
* [ ] All links live and load quickly
* [ ] No shortened or third-party links
* [ ] Sample messages reviewed and approved
* [ ] Campaign description matches use case

> ✅ *If all boxes are checked, your submission is ready for carrier approval.*

***

### Community and Education 🚧

We love sharing what works. Join our community and learn from other nativeMsg users and our Customer Success team. (Currently under construction. Let your account manager know if you're interested in courses and certification to be considered for our beta group.)

### Coming Soon

* Monthly live training webinars
* Quarterly “What’s New at nativeMsg” sessions
* Invite-only customer roundtables
* Newsletter: *Best practices & product updates*

### Our Promise

Your success is our top priority. We’re committed to making onboarding clear, efficient, and transparent.\
If you ever feel lost or need extra help, reach out — we’ll respond and adjust together.


# Campaign Best Practices

**Goal:** Give your team a clear, practical guide to send compliant, effective SMS/MMS/RCS messages.\
**Who this is for:** Marketers, CS teams, and operators preparing to send live messages.

***

### ⚡ Quick Start

1. Use a **branded link** in messages (no public shorteners).
2. Include **brand name**, **STOP**, **HELP**, and **disclosures** in the program.
3. **Segment & schedule**—send relevant content at respectful times.
4. **Test** with internal numbers → **send** → **measure** → **optimize**.

> 💡 *If anything here is unfamiliar, visit: Compliance Overview and Opt-In / Opt-Out Language.*

***

### 1) Message Frequency & Timing

* Be transparent in your opt-in (e.g., **“4 msgs/month”** or **“msg frequency varies”**).
* Favor **quality over quantity**—never overwhelm your audience.
* **Respect timing**: schedule during business hours and consider time zones.
* For recurring programs, clearly state that messages are **recurring**.

***

### 2) Message Content & Tone

* **Identify your brand** in every message.
* Keep it **short and clear** (aim ≤ 160 characters when possible).
* Avoid jargon/abbreviations that confuse.
* Be professional **and** human—friendly language builds trust.

**Good example (concise + clear):**

```
nativeMsg Alerts: Your appointment is tomorrow at 10am. Reply C to confirm or R to reschedule. HELP for help, STOP to cancel.
```

***

### 3) Links & URLs

Use links that build trust and meet carrier expectations.

| Do                                                          | Don’t                                        |
| ----------------------------------------------------------- | -------------------------------------------- |
| ✅ Use **branded domains** (e.g., yourcompany.com/offer)     | 🚫 Avoid public shorteners (bit.ly, tinyurl) |
| ✅ Use **HTTPS** links                                       | 🚫 Don’t use broken or redirect chains       |
| ✅ Keep the **domain consistent** with your registered brand | 🚫 Don’t use off-brand domains               |

***

### 4) Required Disclosures & Compliance

Every messaging **program** must include (or show evidence of) the following:

* **Opt-out:** “Reply STOP to cancel.”
* **Help:** “Reply HELP for help.”
* **Data rates:** “Msg & data rates may apply.”
* **Frequency:** e.g., “4 msgs/month” or “msg frequency varies.”

> ⚠️ *STOP/HELP responses must work automatically and 24/7.*

***

### 5) Opt-In & Consent

* No implied consent, **users must actively opt in**.
* **Store proof of consent** (screenshot, timestamp, or form data).

**Sample web opt-in disclosure:**

```
By subscribing, you agree to receive recurring automated texts from [Brand]. Msg & data rates may apply. Reply STOP to cancel, HELP for help. [Privacy Policy URL] [Terms URL]
```

***

### 6) Support & Customer Care

* **HELP responses** must include a working support contact (email/URL/phone).
* Ensure support details **match** what you filed in registration.
* Respond to customer inquiries **promptly**.

**HELP auto-reply example:**

```
[Brand]: For support, email support@yourcompany.com or visit yourcompany.com/help. Msg & data rates may apply. Reply STOP to cancel.
```

***

### 7) What to Avoid (Red Flags)

* 🚫 Prohibited content: gambling, cannabis, lead-gen/affiliate marketing, hate/sex/violence&#x20;
* 🚫 Promotions without consent&#x20;
* 🚫 Age-restricted content (alcohol, tobacco, firearms)&#x20;
* 🚫 Inconsistent branding across website, campaign, and sample messages

***

### 8) Tips for High Engagement

* **Personalize using stored attributes** (use name or context)
* **Segment** audiences—send relevant content, not blasts
* Ensure **every message provides value** (reminder, alert, offer)
* **Test & optimize**: experiment with wording, CTAs, timing, and measure results

***

### 9) Quick Self-Check Before Sending

* [ ] Does this message include my **brand name**?
* [ ] Does it match my **declared use case**?
* [ ] Is the **link branded** (no public shortener)?

***

### ✍️ Copy-Ready Examples

**Transactional (confirmation):**

```
[Brand]: Your order #4821 has shipped. Track: yourcompany.com/track/4821. HELP for help, STOP to cancel. Msg&data rates may apply.
```

**Promotional (opted-in audience):**

```
[Brand] Exclusive: 20% off ends tonight. Redeem: yourcompany.com/offer. HELP for help, STOP to cancel. 4 msgs/mo. Msg&data rates may apply.
```

> ***
>
> 💬 Need a second set of eyes? Your CSM can review your first message set before you go live.


# 📚 Knowledge Base

Welcome to your self-serve Knowledge Base — short, clear guides to help you get the most from nativeMsg.

<table data-view="cards"><thead><tr><th></th><th data-type="content-ref"></th><th data-hidden data-card-cover data-type="image">Cover image</th></tr></thead><tbody><tr><td><strong>Create Account</strong></td><td><a href="/get-started/knowledge-base/knowledge-base/create-account-+-add-users">Create Account + Add Users</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FKcMIR1rIP3uiEPBOEsNK%2FGemini_Generated_Image_i39qswi39qswi39q.png?alt=media&amp;token=6bf22095-e318-4f64-8d05-0dfda43a0213">Gemini_Generated_Image_i39qswi39qswi39q.png</a></td></tr><tr><td><strong>Templates</strong></td><td><a href="/get-started/knowledge-base/knowledge-base/templates">Templates</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2F7aMTRqH73qvT3EsXhm2k%2FGemini_Generated_Image_25mmy225mmy225mm.png?alt=media&amp;token=43118598-4279-4f3b-8d55-3552741850cf">Gemini_Generated_Image_25mmy225mmy225mm.png</a></td></tr><tr><td><strong>Webhooks</strong></td><td><a href="/get-started/knowledge-base/knowledge-base/webhooks">Webhooks</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FZqMsgNDH5z05pKZBKB0a%2FGemini_Generated_Image_mph8mrmph8mrmph8.png?alt=media&amp;token=31a9c563-3386-42cb-90ed-8e11f2422228">Gemini_Generated_Image_mph8mrmph8mrmph8.png</a></td></tr><tr><td><strong>Plug-Ins</strong></td><td><a href="broken://pages/NYV9WilUN9xrUcpgwwVg">Broken link</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FbtaCF3aZvmvG2Qw0MWXn%2FGemini_Generated_Image_6uh6m56uh6m56uh6.png?alt=media&amp;token=8cb6a050-5936-447d-97c7-be59f9539175">Gemini_Generated_Image_6uh6m56uh6m56uh6.png</a></td></tr><tr><td><strong>Themes</strong></td><td><a href="/get-started/knowledge-base/knowledge-base/themes">Themes</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FPZB53n8EJGFeOPZ542uS%2FGemini_Generated_Image_32x4lm32x4lm32x4.png?alt=media&amp;token=7960dc6c-2227-4666-9742-7bfa40a5ed5d">Gemini_Generated_Image_32x4lm32x4lm32x4.png</a></td></tr><tr><td><strong>Create an Ad</strong></td><td><a href="broken://pages/xt73oorM8aldbzUseIJd">Broken link</a></td><td data-object-fit="contain"><a href="https://761758251-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FiJS5tHgA7j2IaUmgi7uX%2Fuploads%2FJutzyOeUfX2cablqK1Ny%2FGemini_Generated_Image_bkrxy5bkrxy5bkrx.png?alt=media&amp;token=0563a795-99e5-4e89-8310-a374aa1b7cc2">Gemini_Generated_Image_bkrxy5bkrxy5bkrx.png</a></td></tr></tbody></table>


# Create Account + Add Users

## Creating and Managing Accounts

**Goal:** Set up your nativeMsg account, invite your team, and manage sub-accounts with the right permissions.\
**Estimated Time:** 10–15 minutes

***

### What This Covers

* Creating your main account
* Adding and managing sub-accounts (team users)
* Updating or removing users
* Permissions and access control

***

### ✨ Overview

Each organization on nativeMsg starts with a **main account** (the admin) — usually your primary onboarding contact. From there, you can add **sub-accounts** for teammates who need access to Bots, Conversations, Blasts, and other sections of the platform.

> 💡 *Think of sub-accounts as team user profiles — each with their own login and permissions.*

***

### 🆕 Creating Your Main Account

1. **Check your email for an invitation from nativeMsg.**\
   Your Customer Success Manager will send this during onboarding.
2. **Click “Accept Invitation.”**\
   This link will take you to your organization’s nativeMsg login page.
3. **Create your password.**\
   Use a strong, unique password.

   > 💡 *For security, do not reuse credentials from other platforms.*
4. **Confirm your organization details.**\
   Check that your company name and email domain are correct.
5. **Log in to your dashboard.**\
   Once inside, you’ll see your **Settings** menu — this is where you’ll manage users and permissions.

***

### 👥 Adding Sub-Accounts (Team Users)

A **sub-account** is a user you create in nativeMsg with customizable permissions.\
Sub-accounts are ideal for teammates who handle specific areas like campaign management, compliance, or development.

#### Steps to Add a Sub-Account

1. Go to **Settings → Manage Sub-Accounts**.
2. Click **New Sub-Account**.
3. In the **Create Sub Account** window, enter:
   * Full name
   * Email address
   * Role (optional)
4. Click **Save**.

✅ *The new sub-account is now active and, by default, has full access permissions.*

> 💬 *You can customize permissions right after adding the user.*

***

### ✏️ Updating Permissions

Each sub-account can have different access levels for platform features such as Bots, Conversations, and Blasts.

1. Go to **Settings → Manage Sub-Accounts**.
2. In the **Search Sub Account** field, enter the user’s name.
3. Click **Update**.
4. Under **Permissions Settings**, use the **Update Sections** dropdown to:
   * **Select All** → grant full access
   * **Deselect All** → remove all access
   * Or choose specific modules to allow (e.g., Conversations, Blasts)
5. Click **Done** to save.

> ⚙️ *Example: Give your marketing team access to Blasts, while limiting configuration access to admins.*

***

### ❌ Deleting a Sub-Account

If someone leaves your organization or no longer needs access:

1. In **Settings → Manage Sub-Accounts**, search for the user.
2. Click **Delete Sub Account.**
3. Confirm by clicking **Yes** in the pop-up message.

🚨 *Deleting a user immediately removes all permissions and access.*

***

### 🔍 Searching for Users

* Use the **Search Sub Account** field to find users.
* Type at least 3 characters to see matching results.
* Click inside the field to view all existing sub-accounts.

***

### ✅ Checklist

* [ ] Main account created and verified
* [ ] Password set
* [ ] Team members added as sub-accounts
* [ ] Permissions reviewed and customized
* [ ] Unused accounts deleted

***

### 💬 Need Help?

If you run into any issues or have questions about permissions, reach out to your Customer Success Manager or email **<support@nativemsg.com>**. We’re happy to guide you through user setup or access configuration.


# Templates

## Bot Templates: Save and Reuse Conversation Flows

**Goal:** Create reusable templates for your Bots so you can quickly duplicate complex interaction flows.\
**Estimated Time:** 10 minutes

***

### 💡 What Templates Are

Templates in nativeMsg let you **save a bot’s conversation flow** — including its interactions, actions, and logic — so you can reuse it later for another bot.

Instead of rebuilding the same structure every time, you can:

* Save your best-performing bot as a Template
* Apply that Template to a new Bot
* Edit and re-save updated versions

> ⚙️ *Templates are especially powerful for standardizing flows like “Lead Capture,” “Support,” or “Product Tour.”*

***

### 🧭 Why Templates Matter

| Benefit                  | Description                                                        |
| ------------------------ | ------------------------------------------------------------------ |
| **Save Time**            | No need to rebuild complex interactions from scratch.              |
| **Maintain Consistency** | Keep tone, structure, and compliance language uniform across bots. |
| **Accelerate Scaling**   | Clone successful flows across teams, regions, or campaigns.        |
| **Iterate Efficiently**  | Update once, re-save as a new version of the Template.             |

***

### 🧱 How Templates Work

When you create a Bot, you design **Interactions** (conversation steps) and **Actions** (things those steps do).\
Templates capture those Interactions and their flow connections.

Later, you can:

1. Apply the Template to a new or existing Bot
2. Edit or extend that flow as needed
3. Save it again as a new Template version

✅ *Default Templates come built into nativeMsg — you can also create your own.*

***

### 🧩 Creating and Saving a Template

1. Go to **Bots → Design** view.
2. Add multiple **Interactions** and connect them to create your flow.
3. When your flow is complete, click **Templates → Create New.**
4. Enter a **Template Name** and optional **Description.**
5. Click **Save.**

Your Template is now available in the Templates list.

> 💡 *You can continue to edit your bot and re-save the updated flow as a new template later.*

***

### 🧰 Applying a Template to a Bot

To reuse a Template:

1. Go to **Bots → select your Bot.**
2. Click **Templates.**
3. Choose a Template from the list.
4. Click **Apply Template.**

✅ The interactions and logic from that Template are instantly added to your Bot’s canvas.

> 🧪 *You can customize and extend the imported flow — edits won’t affect the original template.*

***

### 🗑️ Deleting a Template

To remove old or unused Templates:

1. Go to **Bots → Templates.**
2. In the Template dialog box, click the **bin 🗑️** icon next to the Template.
3. Confirm **Yes** to delete it.

> ⚠️ You can only delete your own Templates — default nativeMsg templates cannot be removed.

***

### 🎯 Pro Tips for Teams

| Tip                        | Why It Helps                                                            |
| -------------------------- | ----------------------------------------------------------------------- |
| **Name clearly:**          | Use versioning (e.g., “Lead Capture v2 – SMS”).                         |
| **Add descriptions:**      | Helps teammates know what each template does.                           |
| **Test before saving:**    | Validate logic paths and actions to avoid broken flows.                 |
| **Re-save major updates:** | Don’t overwrite; save new versions to track improvements.               |
| **Share defaults:**        | Use consistent templates for onboarding, support, and compliance flows. |

***

### 💬 Example Use Cases

| Template         | Purpose                                                      |
| ---------------- | ------------------------------------------------------------ |
| **Welcome Flow** | Introduces brand and routes users to Sales/Support.          |
| **Lead Capture** | Collects name, email, and preferences before passing to CRM. |
| **Survey Bot**   | Asks multi-question feedback and logs results.               |
| **Product Demo** | Walks users through feature discovery step-by-step.          |

> 🧭 *Start from a default Template, tweak the copy, then save your own branded version.*

***

### 🧠 Quick Troubleshooting

| Issue                          | Solution                                                |
| ------------------------------ | ------------------------------------------------------- |
| Template not appearing         | Refresh Templates list or verify permissions.           |
| Flow looks incomplete          | Make sure all interactions are connected before saving. |
| “Apply Template” grayed out    | Ensure a Bot is selected before applying.               |
| Deleted Template still visible | Refresh the page — caching may delay the update.        |

***

### ✅ Quick Recap

You can:

* Save your current bot as a Template
* Apply Templates to new bots
* Delete or replace outdated Templates
* Build a personal or shared library of reusable flows

> 💡 *Templates help you scale conversational design fast — without rebuilding from scratch.*


# Webhooks

## Managing Webhooks

**Goal:** Set up and manage webhooks to send real-time nativeMsg event data to your own systems.\
**Estimated Time:** 5–10 minutes

***

### What's a webhook?

A **webhook** is a simple, automated message sent from nativeMsg to your system when a specific event occurs.\
It’s a way for your tools (like CRMs, analytics, or databases) to receive instant updates — no manual refresh required.

> ⚙️ When triggered, nativeMsg makes an **HTTP POST** request to the URL(s) you’ve set in your account.

***

### 🧭 Where to Manage Webhooks

1. Go to **Settings → Webhooks** in your nativeMsg dashboard.
2. You’ll see a list of all existing webhooks — inbound and outbound.
3. Each webhook shows:
   * URL endpoint
   * Event type(s)
   * Secret key
   * Edit/Delete options

***

### 🧱 Types of Webhooks

| Type         | Description                                                                   | Example                                          |
| ------------ | ----------------------------------------------------------------------------- | ------------------------------------------------ |
| **Inbound**  | Triggered when data enters nativeMsg (e.g., a message received, user action). | User sends a new message to a bot.               |
| **Outbound** | Triggered when data leaves nativeMsg (e.g., a message sent, a status change). | Bot sends a message or completes an interaction. |

> 💡 *You can add multiple webhooks for different systems or event types — each with its own secret key.*

***

### 🧩 Creating a New Webhook

1. Go to **Settings → Webhooks.**
2. Click **Create New Hook.**
3. In the dialog box, fill in the fields:

| Field      | Description                                                                                                               |
| ---------- | ------------------------------------------------------------------------------------------------------------------------- |
| **URL**    | The endpoint where nativeMsg will POST data. Must include `https://` (e.g., `https://yourdomain.com/webhook`).            |
| **Events** | Select one or more events you want to trigger this webhook. Examples: `message.received`, `message.sent`, `user.created`. |

4. Click **Create.**

✅ The new webhook is created with a **unique secret key** for verification and security.

> 🔒 Keep your secret key private — it’s used to validate that webhook payloads are coming from nativeMsg.

***

### ✏️ Editing a Webhook

1. Go to **Settings → Webhooks.**
2. Find the webhook you want to update.
3. Click **Edit.**
4. Change the **URL** or **Events** as needed.
5. Click **Update.**

✅ The webhook will continue running with the new configuration immediately.

***

### 🗑️ Deleting a Webhook

1. Go to **Settings → Webhooks.**
2. Click **Delete** next to the webhook.
3. Confirm **Yes** to remove it.

⚠️ Once deleted, events will no longer send to that endpoint.

***

### 🧠 Pro Tips for Success

| Tip                             | Why It Matters                                                                   |
| ------------------------------- | -------------------------------------------------------------------------------- |
| **Always use HTTPS**            | Ensures secure communication. Plain HTTP is not supported.                       |
| **Verify your secret key**      | Authenticate incoming requests to prevent spoofing.                              |
| **Log incoming payloads**       | Helps debug or replay webhook data if needed.                                    |
| **Filter events by importance** | Avoid unnecessary traffic — subscribe only to what you need.                     |
| **Test before going live**      | Use a tool like [Webhook.site](https://webhook.site) or Postman to preview data. |

***

### ⚙️ Common Use Cases

| Use Case                | Description                                                                 |
| ----------------------- | --------------------------------------------------------------------------- |
| **CRM Integration**     | Send new lead info or chat interactions to HubSpot, Salesforce, etc.        |
| **Analytics Tracking**  | Log message delivery and engagement events in your BI tool.                 |
| **Automation Triggers** | Launch workflows (e.g., via Zapier or n8n) when users engage with your bot. |
| **Custom Reporting**    | Store conversation data in your internal database.                          |

> 💬 *Webhooks make nativeMsg part of your larger automation ecosystem.*

***

### 🧩 Example JSON Payload

```json
{
  "event": "message.received",
  "timestamp": "2025-10-08T18:15:00Z",
  "bot_id": "12345",
  "user": {
    "id": "67890",
    "phone": "+15555550123"
  },
  "message": {
    "type": "text",
    "content": "Hi, I need help!"
  },
  "signature": "sha256=ab4f123456789cde..."
}
```

> 🧠 *Use the `signature` header to verify authenticity using your secret key.*

***

### ✅ Quick Recap

You can:

* Create secure webhook URLs for inbound/outbound events
* Edit or delete existing hooks anytime
* Use webhooks to integrate nativeMsg data with your own systems

> 🔗 *Webhooks are your real-time bridge between nativeMsg and everything else in your tech stack.*


# Themes

## Designer Themes: Create, Save, and Apply Brand Themes

**Goal:** Quickly brand your Web Chat panel (and supporting previews like Ads and the RCS Emulator) using reusable Themes.\
**Estimated Time:** 10–20 minutes

***

### ⚡ Quick Start (Most Teams Do This)

1. Go to **Designer → Themes**.
2. Adjust **colors, fonts, and sizes** to match your brand.
3. Click **Save**, name your theme (e.g., “Acme – Primary”), **Save** again.
4. Click **Themes → select your theme → Use Theme**.
5. Open the Web Chat preview and confirm everything looks right (text contrast, buttons, quick replies).

> 💡 Save multiple variants (e.g., “Light”, “Dark”, “High Contrast”) and switch per campaign.

***

### 🧭 What Themes Control

Themes are reusable style presets for:

* **Web Chat Panel** (colors, fonts, sizes, UI controls)
* **RCS Emulator preview**
* **Ad previews** (visual consistency during build/review)

Change settings once → reuse as a theme across projects.

***

### 🎛️ Theme Customization (All Options)

Below is a human-friendly reference of what you can configure. You’ll find these on **Designer → Themes**.

#### General UX

| Field                           | What It Does                        | Tip                             |
| ------------------------------- | ----------------------------------- | ------------------------------- |
| **Text Type Delay (ms)**        | Delay between “typing…” and sending | 500–1200ms feels realistic      |
| **Placeholder**                 | Hint text in the input field        | Keep short: “Message here…”     |
| **Avatar Icon**                 | Launcher icon for Web Chat          | Use a simple, on-brand SVG/PNG  |
| **Background Image**            | Chat panel background               | Prefer subtle textures/patterns |
| **Remove Footer Input Form**    | Hide the input box                  | Use for read-only experiences   |
| **Get Started Without History** | Hide previous messages on open      | Useful for fresh starts/demos   |

#### Color Settings

| Area                                    | What It Styles                    |
| --------------------------------------- | --------------------------------- |
| **Status/Background/Hover**             | Global states, canvas, link hover |
| **Right/Left Speech Bubbles – BG/Text** | Customer vs. brand bubble colors  |

#### Size Settings (px)

| Area                                     | What It Styles                      |
| ---------------------------------------- | ----------------------------------- |
| **Width / Height**                       | Chat panel size                     |
| **Message Top Margin / Item Top Margin** | Spacing above the chat and messages |
| **Speech Bubbles Font Size**             | In-bubble text size                 |

#### Video & Image

| Area                  | What It Styles           |
| --------------------- | ------------------------ |
| **Max Width (%)**     | Video or image max width |
| **Auto Play (video)** | Autoplay video on render |

#### Font Settings

| Group             | Fields                                                      |
| ----------------- | ----------------------------------------------------------- |
| **Name/Fallback** | Font for user names/system fallback                         |
| **Message Fonts** | Font for message content                                    |
| **URL**           | Google Fonts URL (e.g., `https://fonts.googleapis.com/...`) |

#### Input Form

| Field                             | What It Styles              |
| --------------------------------- | --------------------------- |
| **BG / Placeholder / Text Color** | Input field colors          |
| **Height / Font Size / Padding**  | Input size and spacing      |
| **Icons Color/Size/Padding**      | Attachment/emoji/etc. icons |

#### Button Settings

| Field                                | What It Styles        |
| ------------------------------------ | --------------------- |
| **Text / Background / Border Color** | Button visuals        |
| **Font Size / Padding / Radius**     | Readability and shape |

#### Quick Replies

| Field                             | What It Styles                                         |
| --------------------------------- | ------------------------------------------------------ |
| **Display Mode**                  | Scrollable bottom / Left stack / Right stack / Cleared |
| **Text/Background/Border/Radius** | Chip style                                             |
| **Height / Font Size / Padding**  | Chip sizing                                            |

**Multi-Select (Quick Replies)**

| Field                               | What It Styles        |
| ----------------------------------- | --------------------- |
| **Submit Width / Radius / Padding** | Submit button         |
| **Submit Background / Text**        | Submit colors         |
| **Selected Reply BG / Text**        | Selected state styles |

#### List & Carousel

| Component    | Fields                                                                                           |
| ------------ | ------------------------------------------------------------------------------------------------ |
| **List**     | Title/Subtitle colors, sizes, heights, BG; Image Max W/H                                         |
| **Carousel** | Title/Subtitle colors, sizes, heights, BG; Button colors/sizes/heights; Max Width; Image Max W/H |

***

### 💾 Save as a New Theme

1. Customize options in **Designer → Themes**.
2. Click **Save**.
3. Enter **Theme Name** (required) and optional **Description**.
4. Click **Save** again.

> ✅ Your new theme appears in the **Themes** list for quick reuse.

***

### 🎯 Apply (Use) a Saved Theme

1. Go to **Designer → Themes**.
2. Click **Themes**.
3. Select the theme → **Use Theme**.
4. The Web Chat panel updates instantly.

> 🧪 Open a chat preview and test contrast, buttons, and quick replies before going live.

***

### 🧹 Clear the Current Theme (Reset to Default)

* Go to **Designer → Themes** → click **Clear** (enabled only if a theme is applied).
* The Web Chat panel returns to default styles.

***

### 🗑️ Delete a Theme

1. Go to **Designer → Themes → Themes**.
2. Click the **bin** icon next to a theme.
3. Confirm **Proceed**.

> ⚠️ Deleting a theme removes it from the list (does not affect already-published pages).

***

### 🎨 Best-Practice Guardrails (Design + Accessibility)

* **Contrast matters:** Ensure body text contrasts at least WCAG AA (\~4.5:1).
* **Consistent bubbles:** Keep left/right bubble colors clearly distinct.
* **Button restraint:** Use 1–2 primary button colors; others as neutral.
* **Quick replies:** Choose a legible chip height (36–44px) and font size (14–16px).
* **Fonts:** Load one primary + one fallback; avoid >2 web font families.
* **Imagery:** Keep chat **Background Image** subtle; content must remain readable.
* **Responsive sizing:** If embedding chat in a narrow column, reduce **Width** and **Speech Bubbles Font Size** slightly.
* **Brand links only:** If buttons/quick replies open URLs, prefer your domain (no public shorteners).

***

### 🔧 Troubleshooting

* **“My buttons look cramped.”** Increase **Button Padding** and check **Font Size**.
* **“Quick replies wrap badly.”** Use **Scrollable at Bottom** display or shorten titles.
* **“Text looks blurry.”** Ensure correct font URL and fallback; avoid scaling via CSS transforms.
* **“Images overflow.”** Reduce **Image Max Width (%)** or **Image Max Height (px)**.
* **“Low contrast warnings.”** Darken text or lighten bubble backgrounds.

***

### 🧩 Sample Theme Presets (Copy These Values)

**Light – Primary**

* Right Bubble: BG `#0B5FFF`, Text `#FFFFFF`
* Left Bubble: BG `#F3F5F8`, Text `#1A1F36`
* Buttons: BG `#0B5FFF`, Text `#FFFFFF`, Radius `8px`
* Quick Replies: BG `#EEF2FF`, Text `#0B5FFF`, Radius `24px`
* Fonts: Inter (URL via Google Fonts), fallback: Arial

**Dark – High Contrast**

* Background: `#0F1115`
* Right Bubble: BG `#2563EB`, Text `#FFFFFF`
* Left Bubble: BG `#1A1F36`, Text `#E5E7EB`
* Buttons: BG `#3B82F6`, Text `#0B0D12`, Radius `8px`
* Quick Replies: BG `#111827`, Text `#E5E7EB`, Border `#374151`

> 💡 Use these as a starting point; tweak to your brand palette.

***

### ✅ Pre-Publish Checklist

* [ ] Text contrast is readable in all states (bubbles, buttons, quick replies).
* [ ] Links and buttons match brand colors and are legible on hover.
* [ ] Quick replies don’t wrap awkwardly on mobile widths.
* [ ] Fonts load correctly (primary + fallback).
* [ ] Carousel/List titles are readable over images.
* [ ] Saved theme is named and described clearly for teammates.


# Functional Overview

**Goal:** Understand how nativeMsg works — the key tools, sections, and workflows you’ll use daily.

***

### 💡 Big Picture

nativeMsg connects your brand to your customers through automated, intelligent conversations.\
\
You can build, send, and monitor messages across multiple channels — all in one place.

> 🧭 *If you’ve ever used an email marketing tool, think of this as your conversational version of it with automation, personalization, and live reporting built in.*

***

### 🧩 Core Components

| Feature          | Description                                                                               | Quick Link                                                                                            |
| ---------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| **Dashboard**    | Your control center — see activity, metrics, trends, and scheduled messages.              | [Dashboard](https://support.nativemsg.com/support/solutions/articles/67000462751-about-dashboard)     |
| **Bots**         | The brains of your automation. They send messages, follow logic, and interact with users. | [Bots](https://support.nativemsg.com/support/solutions/articles/67000462786-about-bots)               |
| **Channels**     | Where your bots live — SMS/RCS, Web Chat, Facebook Messenger, and more.                   | [Channels](https://support.nativemsg.com/support/solutions/articles/67000463842-about-channels)       |
| **Interactions** | Message flows — rules and actions inside each bot that define how conversations work.     | [Interactions](https://support.nativemsg.com/support/solutions/folders/67000410151)                   |
| **Blasts**       | One-to-many messages to your audience (like announcements or promotions).                 | [Blasts](https://support.nativemsg.com/support/solutions/articles/67000465186-about-blasts)           |
| **Humans**       | Your customer list — all users who’ve ever interacted with you.                           | [Humans](https://support.nativemsg.com/support/solutions/articles/67000465196-about-humans)           |
| **Tags**         | Segmenting your list                                                                      | [Tags](https://support.nativemsg.com/support/solutions/articles/67000465198-managing-tags-for-humans) |
| **Intents**      | What's the human asking to do?                                                            | [Intents](https://support.nativemsg.com/support/solutions/articles/67000462866-about-intents)         |

***

### 🧠 How It All Fits Together

1. **Create a Bot** → define your message flow.
2. **Connect a Channel** → choose where your bot runs (SMS, Web Chat, etc.).
3. **Add Interactions** → design the conversation steps.
4. **Test** → make sure it feels natural and compliant.
5. **Publish** → go live.
6. **Monitor via Dashboard** → track performance and iterate.

> 🚀 *Once your bot works well, you can expand it to new channels or send Blasts to larger audiences.*

***

### 🧭 Quick Start Next Steps

Ready to see it in action? Go to "Create Your First Message," a 10-minute guided walkthrough to get your first bot live.


# Create Your First Message

## Quick & Practical Guide

**Goal:** Build and send your first automated message in under 10 minutes.

***

### ⏱️ What You’ll Do

1. **Create a Bot** — name it and pick a template (or start blank). This is the beginning of the experience.
2. **Connect a Channel** — SMS/RCS, Web Chat, or Facebook Messenger.
3. **Add Interactions** — the messages and rules your bot will follow.
4. **Test It** — run the in-app tester until it feels right.
5. **Publish** — go live.
6. *(Optional)* **Send a Blast** — message multiple users at once.

***

### 🤖 Step 1: Create a Bot

* Go to **Bots → Create New Bot**.
* Give it a **name** (e.g., “Welcome Flow”).
* Choose a **template** or start blank.
* Click **Save**.

> 💡 *Pro Tip:* Start small — one welcome message is enough to get live fast.

***

### 🔗 Step 2: Connect a Channel

* Open your new bot → click **Add Channel**.
* Choose where you’ll send from:
  * **SMS/RCS** for phone messaging
  * **Web Chat** for your site
  * **Facebook Messenger** for social
* Enter the required credentials → **Connect.**

✅ You’ll see your channel listed with an icon when connected.

***

### 💬 Step 3: Add Interactions

* On your bot canvas, click **+ Add Interaction.**
* Name it (e.g., “Welcome Message”).
* Inside that interaction, add **Actions**:
  * Message: text to send (“Hi {first\_name}, welcome to nativeMsg!”)
  * Wait for: pauses until user responds
  * Assign Tag / Update Attribute: save user data for personalization

> ⚙️ *Start simple: one message + one reply path.*

***

### 🧪 Step 4: Test Your Bot

* Click **Test Bot** in the editor.
* Type like a user — check flow, replies, and links.
* Make sure STOP and HELP work properly.
* Adjust interactions if needed.

✅ Repeat until it feels natural and correct.

***

### 🚀 Step 5: Publish

* Click **Publish.**
* Your bot is now live on the selected channel.
* Watch early results on your **Dashboard** → Bots panel.

> 📊 *Metrics like New Humans, Conversations, and Messages will update in real time.*

***

### 📢 Optional: Send a Blast

* Go to **Blasts → Create New Blast.**
* Choose your **bot**, **channel**, and **audience (tags)**.
* Write a short message (under 160 chars).
* Add a branded link (no bit.ly).
* Schedule or send immediately.

> 💡 *Always target Blasts to specific Tags — never send to everyone.*

***

### ✅ You’re Live!

You’ve just:

* Created a bot
* Connected a channel
* Built your first flow
* Tested and published
* (Optionally) sent your first broadcast

You’re now running your first automated nativeMsg experience!


# Create Your First Carousel

## End-to-End Guide

**Goal:** Build and send your first interactive Carousel message — both inside a bot flow (1:1) and as a broadcast (1-to-many).\
**Estimated Time:** 15–20 minutes

***

### 🧭 Overview

Carousels let you show multiple cards in a single message — each with images, titles, buttons, and quick replies.\
They’re perfect for:

* Product menus
* Multi-option surveys
* Support or service choices
* Content showcases

You can use Carousels:

* **Inside a Bot** (for ongoing 1:1 conversations), or
* **As a Blast** (to broadcast to many users at once).

***

### Part A — Build It in a Bot (for 1:1 and ongoing flows)

#### 1️⃣ Create or Open a Bot

* Go to **Bots → Create New Bot**, name it (e.g., “Product Carousel”).
* Or open an **existing bot** to add the carousel to an existing flow.

***

#### 2️⃣ Connect a Channel

* Click **Add Channel** → choose your target channel:
  * Web Chat
  * Facebook Messenger
  * SMS/RCS
* Enter required credentials → click **Connect.**

✅ Once connected, you’ll see your channel icon listed under that bot.

***

#### 3️⃣ Add an Interaction

* On your bot canvas, click **+ Add Interaction**.
* Name it something clear like **“Product Carousel.”**

> 💡 *Each interaction can contain one or more actions. The Carousel lives inside one of these.*

***

#### 4️⃣ Add a Rich Card (Carousel) Action

* Open your new interaction.
* Click **Add Action → Rich Card.**
* In the action panel, choose **Carousel.**

***

#### 5️⃣ Configure the Carousel

**Overall Title (optional):**\
Used on some channels to introduce the Carousel.

**Bubbles / Cards:**\
For each card you add:

* **Title (required)** — short, clear text (e.g., “New Arrivals”).
* **Subtitle (optional)** — add context (e.g., “Explore our newest products”).
* **Media File:** click *Media file* to upload or link an image/video.
* **Media Height:** pick what fits your design (auto, short, tall).
* **Card Width (if available):** choose how wide each card should appear.
* **Default Action (optional):** choose what happens when the card is tapped.

Click **+ Add Bubble/Card** to include more cards.

> 🖼️ *Pro Tip:* Use consistent image sizes across cards for clean scrolling.

***

#### 6️⃣ Add Buttons & Quick Replies (Optional but Recommended)

**Buttons on each card:**\
You can add one or more of these:

* URL — open a webpage
* Postback — trigger another interaction
* Phone — initiate a call
* View Location — show a map
* Calendar — open scheduling

**Quick Replies (under the carousel):**\
Add shortcuts for users, like:

* Postback
* Location
* View Location
* Calendar

You can also enable:

* **Multi-select** — allow multiple choices
* **Submit title** + **Execute target** — define what happens after submission.

***

#### 7️⃣ Save → Test → Publish

* Click **Test Bot** to preview the Carousel flow.
* Review layout, buttons, and media behavior across devices.
* Adjust copy, image size, or actions if needed.
* Click **Publish** when ready.

✅ Your Carousel is now live in your bot’s 1:1 flow.

***

### Part B — Send a Carousel as a Blast (One-to-Many)

#### 1️⃣ Create a New Blast

* Go to **Blasts → Create New Blast.**
* Enter a **title**, select a **Bot**, and choose:
  * **Channel(s)** (e.g., SMS, Web Chat)
  * **Tags** (audience segment)

***

#### 2️⃣ Build the Carousel

* In the **Create Message** screen → click the **Carousel tab.**
* For each card:
  * Title, Subtitle
  * Media file (upload or link)
  * Default Action (optional: URL, Postback, or advanced Web default)

**Buttons on each card:**

* URL, Postback, Phone, Location, Calendar

**Quick Replies (below the carousel):**

* Postback
* Location
* View Location
* Calendar

***

#### 3️⃣ (Optional) Add RCS Fallback

If sending via RCS:

* Add a **Fallback Message** — combine an MMS image + SMS text.
* Ensures users without RCS still receive your message.

***

#### 4️⃣ Send or Schedule

* Click **Send Blast** to send now, or **Schedule** for later.
* View upcoming sends under **Dashboard → Scheduled Messages.**

✅ Once sent, performance metrics appear in the **Dashboard → Blasts** section.

***

### ⚙️ Smart Defaults & Tips

| Best Practice           | Why It Matters                                  |
| ----------------------- | ----------------------------------------------- |
| **Image Aspect Ratio**  | Keep all cards consistent for smooth scrolling. |
| **Buttons (1–2 max)**   | Too many options cause decision fatigue.        |
| **Branded Links Only**  | Use your domain — avoid public shorteners.      |
| **Short Copy**          | Put key info in the first 60–90 characters.     |
| **Preview on Channels** | Rendering varies by channel — test each.        |

> 💡 *Previewing on Web and SMS ensures consistent look before launch.*

***

### ✅ Mini Compliance Check (Before Go-Live)

* [ ] Brand is clear on the carousel and first message.
* [ ] Required disclosures (STOP/HELP/frequency/rates) appear in your flow or confirmation.
* [ ] Links use branded domains and are live.
* [ ] Policy links (Privacy + Terms) are accessible.
* [ ] Opt-in documentation is stored.

***

### 🧭 Related Articles

* Create Your First Message
* Sending Your First Blast
* Launching Your 10DLC Campaign with Confidence
* Opt-In / Opt-Out Language


# Sending Your First Blast

**Goal:** Send your first one-to-many message (Blast) using nativeMsg.\
Blasts let you reach multiple subscribers at once — for announcements, promotions, or important updates.\
**Estimated Time:** 10–15 minutes

***

### 💡 What Is a Blast?

A **Blast** is a broadcast message you send to a group of users who are subscribed to a channel in nativeMsg.\
It’s similar to sending an email campaign — but across SMS, RCS, Web Chat, or Facebook Messenger.

You can send immediately or schedule delivery for later.

Each Blast can include:

* Text, images, audio, or video
* Action buttons (URL, Postback, Phone, Location, Calendar)
* Quick Replies (to collect info or guide next steps)
* Fallback messaging for non-RCS devices

***

### 🧭 Overview of the Blast Workflow

1. Create a new Blast
2. Choose Bot, Channel(s), and Tags (audience)
3. Create the message (text, media, buttons, quick replies)
4. (Optional) Add a Carousel
5. (Optional) Add Fallback Message (for RCS users without rich media support)
6. Send immediately or schedule for later

***

### 🧱 Step 1: Create a New Blast

1. Go to **Blasts → Create New Blast**.
2. Enter a **Title** — for your internal reference.
3. In **Choose Bot**, select the bot you want to use.

   > This determines which channels are available.
4. In **Choose Channels**, select one or more destinations (SMS/RCS, Web Chat, Facebook, etc.).
5. In **Choose Tags**, select the audience segment(s) you want to reach.

✅ *Pro Tip:* Tags let you target exactly who should receive the message — such as “Leads,” “VIPs,” or “New Signups.”

***

### 💬 Step 2: Create the Message

The message is what your users see.\
You can add **text**, **media**, **buttons**, and **quick replies** to make it interactive.

1. In the **Media** panel, click **Media file**.
   * Upload an image, audio, or video file from your system, *or* paste a link.
2. In **Blast Message**, write the message text you want to send.

#### 🖼️ Optional: Add a Carousel

For a more visual layout:

* Click the **Carousel** tab in the message builder.
* Add a **Title** and **Subtitle**.
* Upload **media** (images or videos).
* Add **Buttons** or **Default Actions** (URLs or quick replies).
* Click **+ Add** to include more cards.

> 💡 *Each card acts like a mini-message — perfect for showcasing products or multiple options.*

***

### 🔘 Step 3: Add Buttons

Buttons let users take instant action. You can add one or more of these to each message or carousel card:

| Button Type       | Description                          |
| ----------------- | ------------------------------------ |
| **URL**           | Opens a webpage or link              |
| **Postback**      | Triggers a bot interaction or event  |
| **Phone**         | Starts a phone call                  |
| **View Location** | Shows a map or coordinates           |
| **Calendar**      | Opens an event invite with date/time |

> 🧩 *Always name your button before closing the panel to save it.*

***

### 💬 Step 4: Add Quick Replies

Quick Replies give users fast, one-tap responses.\
You can add up to **20 quick replies** per Blast.

1. Below the message panel, click **Quick Reply**.
2. Add a **Reply Title** (the button text).
3. Choose a Quick Reply Type:
   * **Postback** — triggers a bot action or saves data
   * **Location** — requests user location
   * **View Location** — displays an address or map
   * **Calendar** — adds an event to the user’s calendar

✅ *When tapped, the user’s response is automatically posted to their conversation thread.*

***

### 📱 Step 5: Add a Fallback Message (for RCS)

If you’re sending via RCS (Rich Communication Services), some users may not have RCS-enabled devices.\
Use **Fallback Messaging** to ensure everyone still receives the content.

1. Click **Attach File** to upload an image, audio, or video.
   * This will send via MMS.
2. In **Fallback Message**, type your backup text.
   * This sends via SMS.

> 💬 *Fallback is optional but highly recommended — it guarantees full reach.*

***

### ⏱️ Step 6: Send or Schedule

* Click **Send Blast** to send immediately, or
* Click **Schedule** to deliver later.

#### Status Indicators

| Status        | Meaning                        |
| ------------- | ------------------------------ |
| **Draft**     | Blast created but not sent yet |
| **Scheduled** | Blast set for future date/time |
| **Sent**      | Blast successfully delivered   |

You can view all upcoming or completed blasts in **Dashboard → Scheduled Messages.**

***

### 📊 Step 7: Monitor Performance

After your blast is live, review engagement in the Dashboard:

* Total Messages Sent / Received
* Channel Activity
* Top Bots and Popular Campaigns

> 📈 *Click into any metric to see where changes are happening.*

***

### ⚙️ Smart Tips for Better Blasts

| Tip                        | Why It Matters                                                       |
| -------------------------- | -------------------------------------------------------------------- |
| **Segment with Tags**      | Improves relevance and reduces opt-outs.                             |
| **Limit Copy Length**      | SMS truncates long text; keep it under 160 characters when possible. |
| **Use Branded Links**      | Builds trust and reduces carrier filtering.                          |
| **Preview Before Sending** | Check formatting across channels.                                    |
| **Include STOP/HELP**      | Required by carriers for recurring programs.                         |
| **Schedule for Best Time** | Match your audience’s local business hours.                          |

***

### 🧩 Mini Compliance Checklist (Before Send)

* [ ] Brand name is clear and matches registered campaign.
* [ ] STOP / HELP and frequency disclosures are covered in the program.
* [ ] Links use your verified domain (no shorteners).
* [ ] Privacy & Terms links are live.
* [ ] Message tested successfully on all channels.

***

### ✅ You Did It!

You just sent your first blast!\
You can now:

* Duplicate successful blasts for future campaigns
* Schedule recurring messages
* Track engagement in your Dashboard

> 💬 *Pro Tip:* Use results to refine audience Tags and bot interactions for even better performance.


# Analytics

## nativeMsg Analytics: Quick Start Guide

**Goal:** Understand how to measure performance across your bots, messages, and audiences — and use analytics to improve engagement.\
**Estimated Time:** 10 minutes

***

### 🧭 Overview

The **Analytics** section in nativeMsg helps you track performance across all your bots, messages, and users.\
You can view trends at a high level (all bots) or drill down into one bot’s activity.

> 💡 *Think of Analytics as your mission control — where you see what’s working, what’s not, and when your audience is most active.*

***

### 1️⃣ Where to Find It

* In the left navigation, click **Analytics.**
* Choose **All Bots** to view total activity, or pick a specific bot to zoom in.

You’ll see multiple panels — Ads, Bot Activity, Humans, Messaging, Subscribers, and Custom Reports.

***

### 2️⃣ What You Can See

#### 🔹 **Ads**

Track how your ad-driven campaigns perform.

* Impressions, Conversations, Engagements, Clicks
* Active Hours chart (see when engagement peaks)
* Identify which ads drive real conversation starts

***

#### 🔹 **Bot Activity**

Everything happening inside your bots.

* **Overview:** Conversations and engagement stats
* **Interactions Report:** Which flows are performing best
* **Common Phrases:** What users are typing most often
* **Active Hours:** When your users chat the most
* **Conversation Length:** Average duration of chat sessions

> 💡 *Use Common Phrases to refine your bot’s wording with real customer language.*

***

#### 🔹 **Humans**

Your audience metrics — new vs. returning users.

* Track audience growth over time
* Identify your most active customer segments
* See how engagement evolves after campaigns

***

#### 🔹 **Messaging**

Message-level performance across all bots and channels.

* Sent, Received, and Total message counts
* Compare message volume to user engagement
* Spot spikes to understand campaign or event impact

> 📈 *This is your quick pulse check for engagement health.*

***

#### 🔹 **Subscribers**

Understand your subscribed audience.

* Total **Subscribed** vs. **Unsubscribed** users
* View by **Tag** (audience segment) or **Channel** (SMS, Web, Facebook, etc.)

**Breakdowns available:**

| View                       | What It Shows                         |
| -------------------------- | ------------------------------------- |
| **Subscriptions by Tag**   | How many users belong to each segment |
| **Subscribers by Channel** | Where users are most active           |

> 💡 *Helps you focus future Blasts on the best-performing audiences.*

***

#### 🔹 **Custom Reports**

Create your own dashboards with only the data you care about.

1. Click **Custom Reports → + Add.**
2. Name your report.
3. Select the analytics modules to include (Ads, Bot Activity, Messaging, etc.).
4. Rearrange or remove panels using drag-and-drop.
5. Click **Create** to save.

✅ *Perfect for weekly KPI snapshots or monthly performance reviews.*

***

### 3️⃣ Filtering Data

Filter any analytics view to focus on the right bot, channel, or time period.

**At the top of the page:**

1. Choose a **Bot** (All Bots or specific).
2. Select a **Date Range**:
   * Today
   * This Week
   * 15 Days
   * 90 Days
   * Custom range (set your own start/end dates)
3. *(Optional)* Check **Exclude Deleted Channels** for cleaner reports.
4. Click **Apply Filters.**

> 💡 *The dashboard refreshes instantly with your filtered data.*

***

### 4️⃣ Exporting & Sharing

Need to share performance data with your team? You can export any view.

1. Click **Download** (top-right corner).
2. Choose your file format:
   * **CSV** (for spreadsheets)
   * **XLSX** (Excel)
   * **ODS** (OpenDocument)
   * **PDF** (for reports & presentations)
3. nativeMsg will refresh the latest data before exporting.
4. Share the file via email or include it in your internal reporting.

> 📤 *Pro Tip:* Filter your data first — exports match your active filters and date range.

***

### ✅ Pro Tips for New Users

| Tip                                        | Why It Matters                                            |
| ------------------------------------------ | --------------------------------------------------------- |
| **Start with “Humans + Messaging” panels** | Get a quick snapshot of overall engagement health.        |
| **Use “Common Phrases”**                   | Improve flows based on real customer input.               |
| **Create Custom Reports**                  | Save time on weekly or monthly reporting.                 |
| **Apply date filters before exporting**    | Keeps data aligned with your reporting period.            |
| **Check “Active Hours”**                   | Identify the best times to send Blasts or respond faster. |

***

### 📊 Example Use Cases

* **Marketing Teams:** Track campaign engagement, subscriber growth, and message conversions.
* **Customer Success Teams:** Monitor customer interactions and FAQs from Common Phrases.
* **Operations:** Evaluate support load via average conversation length and daily message volume.

***

### ✅ Quick Recap

You can use Analytics to:

* Measure campaign and bot performance
* Discover peak user activity hours
* Understand audience growth and retention
* Build and export your own reports

> 🧭 *Data is your feedback loop — check Analytics weekly to keep improving your messaging experience.*


# Webinar Recordings

Explore our library of previously recorded webinars on-demand.

<figure><img src="https://3444176771-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyULvCYv6DeAGlhe0nj63%2Fuploads%2FNnYWrCto3IFAQYZi5zVs%2FBeyond%20apps%20and%20chatbots.png?alt=media&amp;token=70ba85e4-0d3d-4b08-b789-946e92230566" alt=""><figcaption></figcaption></figure>

[Beyond Apps & Chatbots: The Customer Channel Most Brands Are Missing](https://www.nativemsg.com/webinar-beyond-apps)

<figure><img src="https://3444176771-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyULvCYv6DeAGlhe0nj63%2Fuploads%2FmF10ZIt4GzNrfECrZmzD%2FRCS%20102.png?alt=media&amp;token=cb8ff258-3640-4e62-a547-40d0f037fafc" alt=""><figcaption></figcaption></figure>

[RCS 102: Automating Inbound Engagement with RCS + CRMs](https://www.nativemsg.com/webinar-rcs-102)

<figure><img src="https://3444176771-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FyULvCYv6DeAGlhe0nj63%2Fuploads%2Fc9xzDnMT2lUWBmbbIcq8%2FRCS%20101.jpeg?alt=media&amp;token=b0e2bc91-7edb-4da2-b678-95ad15858a58" alt=""><figcaption></figcaption></figure>

[RCS 101: What It Is and Why It Matters](https://nativemsg.com/webinar-rcs-101)


# User Guide

The nativeMsg User Guide provides step-by-step instructions to help you design, launch, and optimize RCS for Business experiences. From building interactive journeys to managing verified senders and analyzing campaign performance, this guide walks you through every feature so you can confidently turn messaging into measurable outcomes.

<a href="https://support.nativemsg.com/support/solutions/67000278766" class="button primary" data-icon="up-right-from-square">User Guide</a>


# nativeMsg API Reference

A programmatic interface for creating and scaling RCS for Business experiences.

The nativeMsg API enables businesses and partners to programmatically build, trigger, and scale interactive RCS for Business experiences—powered by secure cloud hosting, verified sender identities, global carrier connectivity, and automatic SMS/MMS fallback.

<a href="https://docs.nativemsg.com/" class="button primary" data-icon="arrow-up-right-from-square">API Reference</a>


# Overview

The RCS Experience Schema is a JSON Schema (draft-07) specification that defines the complete structure of an RCS conversational experience. An experience describes how a messaging agent responds to user input: which workflows are triggered by which intents, what messages are sent, how user data is captured, and how the conversation branches based on conditions.

## Scope

**This specification covers:**

* The root Experience object and its required properties
* Workflow definition, intent matching, keyword recognition, and expression configuration
* All Action types: message sending, HTTP requests, user-input capture, flow control, attribute assignment, and settings updates
* Message variants: text, media, buttons, quick replies, carousels, lists, and rich cards
* Condition and Operator structures for conditional action execution
* Channel-specific constraints and compatibility rules

**This specification does not cover:**

* Authentication or authorization for the nativeMsg API
* How experiences are deployed or published to a channel
* Webhook configuration or inbound event formats
* AI model configuration or NLU training pipelines
* Analytics, reporting, or conversation history APIs

## Normative Language

{% hint style="info" %}
This documentation uses RFC 2119 terminology throughout all normative sections.
{% endhint %}

| Keyword        | Meaning                                                       |
| -------------- | ------------------------------------------------------------- |
| **MUST**       | Absolute requirement. Violations produce invalid experiences. |
| **MUST NOT**   | Absolute prohibition. Violations produce invalid experiences. |
| **SHOULD**     | Recommended. Non-conformance requires justification.          |
| **SHOULD NOT** | Not recommended. Non-conformance requires justification.      |
| **MAY**        | Optional. Permitted but not required.                         |

Normative requirements derive directly from the JSON Schema definition. Informative notes, tips, and implementation guidance are marked with

blocks.

## What You Can Build

An experience built with this schema can:

* **Route conversations** — Match user messages to workflows by intent name, keyword, or natural language expression
* **Send rich messages** — Plain text, images, videos, interactive buttons, quick replies, carousels, lists, and RCS rich cards
* **Capture user input** — Collect text, numbers, dates, locations, and files and store them as named attributes
* **Call external APIs** — Make HTTP requests during a conversation and map response data into attributes
* **Branch conditionally** — Execute different actions based on attribute values, channel type, tags, and device platform
* **Assign metadata** — Tag conversations, set attributes, and update experience settings at runtime
* **Send notifications** — Dispatch internal notes or emails as part of a workflow

## Documentation Organization

| Section                                                                                    | Contents                                                           |
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
| [Quick Start](/rcs-experience-schema/rcs-experience-schema/quick-start)                    | Minimal working example, field annotations, and schema validation  |
| [Core Concepts](/rcs-experience-schema/rcs-experience-schema/core-concepts)                | Normative definitions of all core terms and a message-flow diagram |
| [Reference: Experience](/rcs-experience-schema/reference/experience)                       | Root object specification                                          |
| [Reference: Workflow](/rcs-experience-schema/reference/workflow)                           | Workflow, Intent, Keyword, Expression specifications               |
| [Reference: Actions](/rcs-experience-schema/reference/actions)                             | All action types with complete property tables                     |
| [Reference: Messages](/rcs-experience-schema/reference/messages)                           | Message variants, buttons, quick replies, carousels, rich cards    |
| [Reference: Conditions](/rcs-experience-schema/reference/conditions)                       | Condition, Operator, and comparison semantics                      |
| [Guides: Coding Language Examples](/rcs-experience-schema/guides/coding-language-examples) | Validate and deploy an experience in 7  programming languages      |
| [Guides: Common Patterns](/rcs-experience-schema/guides/common-patterns)                   | Implementation cookbook: 8 complete recipes                        |
| [Troubleshooting](/rcs-experience-schema/guides/troubleshooting)                           | Validation error reference and debugging checklist                 |

## Schema Identity

| Field                    | Value                                                                 |
| ------------------------ | --------------------------------------------------------------------- |
| `$schema`                | `http://json-schema.org/draft-07/schema#`                             |
| `$id`                    | `https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json` |
| Root type                | `object`                                                              |
| Required root properties | `name`, `workflows`                                                   |

## Minimal Valid Experience

The following is the smallest syntactically valid experience. It contains one workflow, one intent, and one action that sends a text message.

{% code title="minimal-experience.json" %}

```json
{
  "name": "Hello World Experience",
  "workflows": [
    {
      "intents": ["greeting"],
      "actions": [
        {
          "send": {
            "message": {
              "text": "Hello! How can I help you today?"
            }
          }
        }
      ]
    }
  ]
}
```

{% endcode %}

For a fully annotated version of this example, see [Quick Start](/rcs-experience-schema/rcs-experience-schema/quick-start).


# Quick Start

This page walks you through the smallest complete nativeMsg experience, explains every field, and shows how to validate an experience file against the schema before deploying it.

## Prerequisites

* A text editor or IDE with JSON support
* Node.js ≥ 16, Python ≥ 3.8, or a shell with `curl` available (for validation)
* A nativeMsg account and API credentials (for deployment)

## Minimal Working Example

The following experience defines a single workflow that responds to a "greeting" intent with a text message and two quick replies.

```json
{
  "name": "Support Bot v1",
  "welcomeMessageExecute": "greeting",
  "workflows": [
    {
      "name": "greeting",
      "intents": ["greeting"],
      "expressions": [
        "hello",
        "hi",
        "hey there",
        "good morning"
      ],
      "actions": [
        {
          "name": "send-greeting",
          "send": {
            "message": {
              "text": "Welcome to Acme Support. What can I help you with today?",
              "quickReplies": [
                {
                  "type": "text",
                  "title": "Track an order",
                  "payload": "track_order"
                },
                {
                  "type": "text",
                  "title": "Return an item",
                  "payload": "return_item"
                }
              ]
            }
          }
        }
      ]
    }
  ]
}
```

### Field-by-Field Annotation

| Field                     | Path                                     | Type   | Required    | Description                                                                                                                              |
| ------------------------- | ---------------------------------------- | ------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `name`                    | `$.name`                                 | string | **Yes**     | Human-readable name for this experience. 1–100 characters.                                                                               |
| `welcomeMessageExecute`   | `$.welcomeMessageExecute`                | string | No          | Name of the workflow to run when the channel invitation is displayed. Must match a workflow `name`.                                      |
| `workflows`               | `$.workflows`                            | array  | **Yes**     | Ordered list of [Workflow](broken://pages/4f3a4c9c0cdd8e437977c1f62747be13864b7417) objects. Must contain at least one item.             |
| `workflows[].name`        | `$.workflows[0].name`                    | string | No          | Unique identifier for this workflow. Must not start with `nm:`. Used by `execute`, `goto`, and `welcomeMessageExecute`.                  |
| `workflows[].intents`     | `$.workflows[0].intents`                 | array  | **Yes**     | Intent names or [IntentObject](broken://pages/4f3a4c9c0cdd8e437977c1f62747be13864b7417#intentobject) entries that trigger this workflow. |
| `workflows[].expressions` | `$.workflows[0].expressions`             | array  | No          | Natural language training phrases. Strings or [Expression](broken://pages/4f3a4c9c0cdd8e437977c1f62747be13864b7417#expression) objects.  |
| `workflows[].actions`     | `$.workflows[0].actions`                 | array  | **Yes**     | Sequence of [Action](broken://pages/8f4ec348362b58255a5a67bf2e1a474327e98a84) objects to execute.                                        |
| `actions[].name`          | `$.workflows[0].actions[0].name`         | string | No          | Unique identifier for this action within its workflow. Must not start with `nm:`.                                                        |
| `actions[].send.message`  | `$.workflows[0].actions[0].send.message` | object | No          | [Message](broken://pages/401e9d0c103180d8f0bde62f36890063993ffb24) to deliver to the user.                                               |
| `message.text`            | `...message.text`                        | string | Conditional | Message body. Maximum 3072 characters. Required unless another content field is present.                                                 |
| `message.quickReplies`    | `...message.quickReplies`                | array  | No          | Up to 11 [QuickReply](broken://pages/401e9d0c103180d8f0bde62f36890063993ffb24#quickreply) objects.                                       |
| `quickReplies[].type`     | `...quickReplies[0].type`                | string | **Yes**     | Reply type. `"text"` renders as a tappable chip.                                                                                         |
| `quickReplies[].title`    | `...quickReplies[0].title`               | string | Conditional | Display label. Required when type is `text` or `postback`. Maximum 25 characters.                                                        |
| `quickReplies[].payload`  | `...quickReplies[0].payload`             | string | No          | Value sent back to the platform when selected. Maximum 1000 characters.                                                                  |

{% hint style="info" %}
The `expressions` array teaches the NLU engine which phrases should match the `greeting` intent. You do not need expressions for intents that are matched by exact name or keyword.
{% endhint %}

{% hint style="danger" %}
Both `name` and `workflows` are required at the root level. An experience missing either property will fail schema validation and be rejected by the API.
{% endhint %}

## Schema Validation

Validate your experience file locally before deploying. The schema is published at:

```
https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json
```

{% tabs %}
{% tab title="Node.js (ajv)" %}
{% code title="validate.js" %}

```javascript
const Ajv = require('ajv');
const addFormats = require('ajv-formats');
const fs = require('fs');
const https = require('https');

async function fetchSchema(url) {
  return new Promise((resolve, reject) => {
    https.get(url, (res) => {
      let data = '';
      res.on('data', (chunk) => { data += chunk; });
      res.on('end', () => resolve(JSON.parse(data)));
      res.on('error', reject);
    });
  });
}

async function validate(experienceFile) {
  const ajv = new Ajv({ allErrors: true });
  addFormats(ajv);

  const schema = await fetchSchema(
    'https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json'
  );
  const validate = ajv.compile(schema);

  const experience = JSON.parse(fs.readFileSync(experienceFile, 'utf8'));
  const valid = validate(experience);

  if (valid) {
    console.log('✓ Experience is valid');
  } else {
    console.error('✗ Validation errors:');
    validate.errors.forEach((err) => {
      console.error(`  ${err.instancePath} ${err.message}`);
    });
    process.exit(1);
  }
}

validate(process.argv[2] || 'experience.json');
```

{% endcode %}

Install dependencies and run:

```bash
npm install ajv ajv-formats
node validate.js experience.json
```

{% endtab %}

{% tab title="Python (jsonschema)" %}
{% code title="validate.py" %}

```python
import json
import sys
import urllib.request
from jsonschema import validate, ValidationError, Draft7Validator

SCHEMA_URL = 'https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json'

def load_schema(url):
    with urllib.request.urlopen(url) as response:
        return json.loads(response.read())

def validate_experience(experience_file):
    schema = load_schema(SCHEMA_URL)
    with open(experience_file, 'r') as f:
        experience = json.load(f)

    validator = Draft7Validator(schema)
    errors = list(validator.iter_errors(experience))

    if not errors:
        print('✓ Experience is valid')
    else:
        print('✗ Validation errors:')
        for error in errors:
            path = ' > '.join(str(p) for p in error.absolute_path) or '(root)'
            print(f'  [{path}] {error.message}')
        sys.exit(1)

if __name__ == '__main__':
    experience_file = sys.argv[1] if len(sys.argv) > 1 else 'experience.json'
    validate_experience(experience_file)
```

{% endcode %}

Install dependencies and run:

```bash
pip install jsonschema
python validate.py experience.json
```

{% endtab %}

{% tab title="CLI (ajv-cli)" %}

```bash
# Install ajv-cli globally
npm install -g ajv-cli ajv-formats

# Download the schema locally (optional, can also reference by URL)
curl -o nativemsg-schema.json \
  https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json

# Validate your experience file
ajv validate \
  -s nativemsg-schema.json \
  -d experience.json \
  --spec=draft7 \
  -c ajv-formats
```

Expected output for a valid file:

```
experience.json valid
```

Expected output for an invalid file:

```
experience.json invalid
[
  {
    "instancePath": "",
    "schemaPath": "#/required",
    "keyword": "required",
    "params": { "missingProperty": "workflows" },
    "message": "must have required property 'workflows'"
  }
]
```

{% endtab %}
{% endtabs %}

## Next Steps

* Read [Concepts](broken://pages/0ca76fdd2a70308106e531cd07b5462fdc2f0f80) for normative definitions of all schema terms
* See [Reference: Workflow](broken://pages/4f3a4c9c0cdd8e437977c1f62747be13864b7417) for the complete workflow specification
* See [Reference: Actions](broken://pages/8f4ec348362b58255a5a67bf2e1a474327e98a84) for all available action types
* Browse [Common Patterns](broken://pages/b9b7512e1a0e907c28f17e0b6500b522e672b8e3) for ready-to-use experience recipes


# Core Concepts

This section provides normative definitions for every core term used throughout this documentation. Terms defined here are used consistently without re-explanation in subsequent sections.

## Definitions

### Experience

An **Experience** is the root configuration object that defines a complete nativeMsg conversational agent. An experience MUST have a unique `name` and MUST contain at least one [Workflow](#workflow). It is the unit of deployment: the entire object is submitted to the nativeMsg platform as a single JSON document.

### Workflow

A **Workflow** is a named unit of conversational logic that is activated when the platform determines that an incoming user message matches one of its [Intents](#intent), a button is pressed or a quick reply response. A workflow contains an ordered list of [Actions](#action) that are executed sequentially when the workflow is triggered. A workflow MAY also define [Keywords](#keyword), [Expressions](#expression), and a [Validation](#validation) constraint.

### Intent

An **Intent** represents the meaning or purpose of a user's message. An intent is identified by name. When a user sends a message, the platform evaluates all workflows and selects the one whose intent name, keyword, or expression best matches the message content. An intent MAY be specified as a bare string (the intent name) or as an [IntentObject](#intentobject) with additional configuration.

### IntentObject

An **IntentObject** is the structured form of an intent declaration within a workflow. It extends a bare string intent with optional [Expressions](#expression).&#x20;

### Keyword

A **Keyword** is a named set of literal string values that trigger a workflow when the user's message matches any of the listed values. Keywords are evaluated before NLU expression matching. A keyword MAY include alternate expression phrasings for each value to improve recognition.

### Expression

An **Expression** is a natural language training phrase that teaches the NLU engine which utterances should match a given intent. An expression MAY be specified as a bare string or as a structured object with `text`, an optional `generate` flag, and an optional list of [Slots](#slot).

### Slot

A **Slot** is a named entity extracted from an expression. It identifies a typed sub-phrase within an expression text that the NLU engine should recognize and extract as a variable. Slots are used to capture entities such as product names, dates, or locations from free-text input.

### Action

An **Action** is a single executable step within a workflow. Actions are evaluated in order. An action MAY include a `conditions` array; if conditions are present and not satisfied, the action is skipped. An action executes exactly one primary operation, determined by which action-type property is present: `send`, `waitFor`, `delay`, `execute`, `goto`, `assignTags`, `assignAttributes`, `updateAttribute`, `subscribe`, `updateSettings`, or `pause`.

### Attribute

An **Attribute** is a named variable associated with a conversation or contact. Attributes are set by `assignAttributes`, `updateAttribute`, or `waitFor` actions, and read back using `{attributeName}` placeholder syntax in message text, request URLs, and other string fields. Attribute names MUST match the pattern `^[a-zA-Z][a-zA-Z0-9_]*$`. Nested attribute paths use dot notation and MUST match `^[a-zA-Z][a-zA-Z0-9_.]*$`.

### Condition

A **Condition** is a predicate evaluated at runtime to determine whether an action should execute. Conditions MAY test channel type, channel ID, assigned tags, device type, device platform, or attribute values via `comparisons`. Multiple conditions in an action's `conditions` array are combined using [Operator](#operator) objects.

### Operator

An **Operator** is a logical combinator (`AND`, `OR`, `NOT`) inserted into a `conditions` array to compose multiple [Condition](#condition) objects. Operators apply to the conditions that follow them according to standard boolean semantics.

### Validation

A **Validation** is a constraint attached to a workflow or action that tests a named attribute value against a regular expression pattern. If the pattern does not match, execution is redirected to the workflow or action named in `executeOnError`.

### Channel

A **Channel** is the messaging medium through which the user communicates (for example: RCS, DSC, DLC). Channel type is available as a condition filter and as a channel restriction on individual actions via the `channel` property.

### SendAction

A **SendAction** is the value of an action's `send` property. It specifies what to deliver to the user or to an external system. Exactly one of its sub-properties SHOULD be present: `message`, `email`, `request`, `json`, `note`, `rss`, or `populate`.

## Message Flow

The following diagram shows how an incoming user message is processed from receipt through workflow execution.

```mermaid
flowchart TD
    A([User sends message]) --> B{Keyword match?}
    B -- Yes --> E[Select matching Workflow]
    B -- No --> C{Intent / NLU match?}
    C -- Yes --> E
    C -- No --> D[Default / fallback Workflow]
    D --> E
    E --> F[Evaluate Workflow Validation]
    F -- Fails --> G[Execute executeOnError target]
    F -- Passes --> H[Begin Action sequence]
    H --> I{Action has conditions?}
    I -- No --> J[Execute Action]
    I -- Yes --> K{Conditions satisfied?}
    K -- No --> L[Skip Action]
    K -- Yes --> J
    J --> M{More actions?}
    L --> M
    M -- Yes --> I
    M -- No --> N([Workflow complete])
    G --> N
```

{% hint style="info" %}
Keyword matching is evaluated before NLU intent matching. If a message matches a keyword exactly, the NLU step is bypassed for that turn.
{% endhint %}

## Placeholder Syntax

Many string fields in the schema support attribute placeholder substitution. The platform replaces `{attributeName}` tokens at runtime with the current value of the named attribute. Nested paths use dot notation: `{order.trackingNumber}`.

Placeholders are supported in: message `text`, request `url`, request `content`, note `text`, email `subject`, email `text`, and `recipientPhone`.

## Reserved Prefix

Workflow names and action names MUST NOT begin with the prefix `nm:`. This prefix is reserved for internal nativeMsg system workflows and actions.


# Experience

The **Experience** object is the root of every nativeMsg experience document. It MUST be a valid JSON object conforming to JSON Schema draft-07.

## Properties

| Property                | Type                                                                         | Required | Default | Constraints                                | Description                                                                       |
| ----------------------- | ---------------------------------------------------------------------------- | -------- | ------- | ------------------------------------------ | --------------------------------------------------------------------------------- |
| `name`                  | string                                                                       | **Yes**  | —       | minLength: 1, maxLength: 100               | Human-readable name for this experience.                                          |
| `workflows`             | array of [Workflow](broken://pages/d1da14ba55d42ff713017b758d2ac856535a6698) | **Yes**  | —       | minItems: 1 (implied)                      | Ordered list of workflows defining the conversational logic.                      |
| `welcomeMessageExecute` | string                                                                       | No       | —       | Must reference an existing workflow `name` | Name of the workflow to execute when the channel invitation message is displayed. |

{% hint style="danger" %}
`name` and `workflows` are required. Any experience document missing either property is invalid and will be rejected.
{% endhint %}

## Property Specifications

### `name`

**Type:** string\
**Required:** Yes\
**Constraints:** minLength: 1, maxLength: 100

The `name` property provides a human-readable identifier for the experience. It is used for display in the nativeMsg platform console and for logging. It does not need to be globally unique in the schema, but platform operators SHOULD treat it as a unique label within a project.

{% code title="Example" %}

```json
{
  "name": "Acme Retail Support — Spring 2025"
}
```

{% endcode %}

{% hint style="danger" %}
An empty string (`""`) is not a valid value for `name`. The minimum length is 1 character.
{% endhint %}

### `workflows`

**Type:** array of [Workflow](broken://pages/d1da14ba55d42ff713017b758d2ac856535a6698) objects\
**Required:** Yes

The `workflows` array defines every conversational flow available in this experience. Workflows are evaluated in document order for intent matching. The platform selects the first matching workflow.

{% code title="Example" %}

```json
{
  "name": "Order Status Bot",
  "workflows": [
    {
      "name": "check-order",
      "intents": ["check_order_status"],
      "actions": [
        {
          "send": {
            "message": {
              "text": "Please provide your order number."
            }
          }
        }
      ]
    }
  ]
}
```

{% endcode %}

{% hint style="info" %}
Workflow names within a single experience SHOULD be unique. The platform uses workflow names as targets for `execute`, `goto`, and `welcomeMessageExecute`. Duplicate names produce undefined resolution behavior.
{% endhint %}

### `welcomeMessageExecute`

**Type:** string\
**Required:** No

When set, `welcomeMessageExecute` names the workflow that the platform executes when it displays the channel invitation or welcome card to the user, before any user message is received. The value MUST match the `name` property of a workflow in the same experience.

{% code title="Example" %}

```json
{
  "name": "Customer Onboarding",
  "welcomeMessageExecute": "welcome",
  "workflows": [
    {
      "name": "welcome",
      "intents": ["nm:start"],
      "actions": [
        {
          "send": {
            "message": {
              "text": "Hi! I am your Acme assistant. Tap below to get started.",
              "quickReplies": [
                {
                  "type": "text",
                  "title": "Get started",
                  "payload": "get_started"
                }
              ]
            }
          }
        }
      ]
    }
  ]
}
```

{% endcode %}

{% hint style="warning" %}
If `welcomeMessageExecute` references a workflow name that does not exist in the `workflows` array, the welcome message step will fail silently or produce a platform error. Always verify the referenced name matches exactly (case-sensitive).
{% endhint %}

## Complete Example

The following is a complete, valid Experience document with two workflows.

{% code title="Complete Experience Example" %}

```json
{
  "name": "Acme Support Bot",
  "welcomeMessageExecute": "greeting",
  "workflows": [
    {
      "name": "greeting",
      "intents": ["greeting"],
      "expressions": [
        "hello",
        "hi",
        "hey",
        "good morning",
        "good afternoon"
      ],
      "actions": [
        {
          "name": "send-welcome",
          "send": {
            "message": {
              "text": "Welcome to Acme Support! What can I help you with?",
              "quickReplies": [
                {
                  "type": "text",
                  "title": "Order status",
                  "payload": "order_status"
                },
                {
                  "type": "text",
                  "title": "Returns",
                  "payload": "returns"
                },
                {
                  "type": "text",
                  "title": "Talk to an agent",
                  "payload": "human_agent"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "order-status",
      "intents": ["order_status", "check_order"],
      "expressions": [
        "where is my order",
        "track my package",
        "order status",
        "when will my order arrive"
      ],
      "actions": [
        {
          "name": "ask-order-number",
          "send": {
            "message": {
              "text": "Please enter your order number (e.g. ACM-12345)."
            }
          }
        },
        {
          "name": "capture-order-number",
          "waitFor": {
            "data": "text",
            "content": "orderNumber"
          }
        }
      ]
    }
  ]
}
```

{% endcode %}

## Validation Rules

The schema enforces the following validation rules at the root level:

| Rule                 | Constraint                     | Effect of violation     |
| -------------------- | ------------------------------ | ----------------------- |
| `name` present       | `required`                     | Schema validation error |
| `name` non-empty     | `minLength: 1`                 | Schema validation error |
| `name` within limit  | `maxLength: 100`               | Schema validation error |
| `workflows` present  | `required`                     | Schema validation error |
| `workflows` is array | `type: array`                  | Schema validation error |
| Each workflow item   | `$ref: #/definitions/Workflow` | Schema validation error |


# Workflow

A **Workflow** defines how the experience responds to a matched user intent. It contains the triggers (intents, keywords, expressions) and the ordered sequence of actions to execute.

## Workflow Properties

| Property      | Type                                                                       | Required | Default | Constraints               | Description                                                                                  |
| ------------- | -------------------------------------------------------------------------- | -------- | ------- | ------------------------- | -------------------------------------------------------------------------------------------- |
| `name`        | string                                                                     | No       | —       | Must not start with `nm:` | Unique identifier for this workflow. Used by `execute`, `goto`, and `welcomeMessageExecute`. |
| `intents`     | array of string \| [IntentObject](#intentobject)                           | **Yes**  | —       | —                         | Intent triggers for this workflow.                                                           |
| `keywords`    | array of [Keyword](#keyword)                                               | No       | —       | —                         | Exact-match keyword triggers.                                                                |
| `expressions` | array of string \| [Expression](#expression)                               | No       | —       | —                         | NLU training phrases for intent matching.                                                    |
| `actions`     | array of [Action](broken://pages/5b89afd4cd5115b5bcefb6933f09e2528ee35422) | **Yes**  | —       | —                         | Ordered sequence of actions to execute.                                                      |
| `validation`  | [Validation](#validation)                                                  | No       | —       | —                         | Attribute validation applied before action execution.                                        |

{% hint style="danger" %}
Both `intents` and `actions` are required on every workflow. A workflow missing either property is invalid.
{% endhint %}

{% hint style="danger" %}
Workflow `name` values MUST NOT begin with the prefix `nm:`. This prefix is reserved for internal nativeMsg system identifiers.
{% endhint %}

## Sub-Definitions

### IntentObject

An **IntentObject** is the structured form of an intent declaration. It is used when an intent requires additional configuration beyond a bare string name.

| Property         | Type                               | Required | Default | Constraints | Description                                              |
| ---------------- | ---------------------------------- | -------- | ------- | ----------- | -------------------------------------------------------- |
| `name`           | string                             | **Yes**  | —       | —           | Intent identifier.                                       |
| `expressions`    | array of [Expression](#expression) | No       | —       | —           | NLU training phrases scoped to this intent.              |
| `excludeContext` | string                             | No       | —       | —           | Suppresses this intent when the named context is active. |

```json
{
  "intents": [
    {
      "name": "cancel_order",
      "excludeContext": "payment_in_progress",
      "expressions": [
        { "text": "I want to cancel my order" },
        { "text": "cancel order" }
      ]
    }
  ]
}
```

### Keyword

A **Keyword** defines an exact-match trigger. The platform matches keyword values before evaluating NLU intents.

| Property | Type                  | Required | Default | Constraints | Description               |
| -------- | --------------------- | -------- | ------- | ----------- | ------------------------- |
| `name`   | string                | **Yes**  | —       | —           | Keyword identifier.       |
| `values` | array of KeywordValue | No       | —       | —           | List of matchable values. |

KeywordValue sub-object:

| Property      | Type            | Required | Default | Constraints | Description                               |
| ------------- | --------------- | -------- | ------- | ----------- | ----------------------------------------- |
| `value`       | string          | **Yes**  | —       | —           | Exact string to match against user input. |
| `expressions` | array of string | No       | —       | —           | Alternate phrasings for this value.       |

```json
{
  "keywords": [
    {
      "name": "stop_keyword",
      "values": [
        { "value": "STOP" },
        { "value": "UNSUBSCRIBE", "expressions": ["opt out", "remove me"] }
      ]
    }
  ]
}
```

### Expression

An **Expression** is a natural language training phrase. It MAY be a bare string or a structured object.

| Property   | Type                   | Required | Default | Constraints | Description                                                       |
| ---------- | ---------------------- | -------- | ------- | ----------- | ----------------------------------------------------------------- |
| `text`     | string                 | **Yes**  | —       | —           | The training phrase text.                                         |
| `generate` | boolean                | No       | `true`  | —           | When `true`, the platform generates additional phrase variations. |
| `slots`    | array of [Slot](#slot) | No       | —       | —           | Entity annotations within the expression text.                    |

Bare string form:

```json
{
  "expressions": ["track my order", "where is my package"]
}
```

Structured object form:

```json
{
  "expressions": [
    {
      "text": "I want to track order number ACM-12345",
      "generate": true,
      "slots": [
        {
          "type": "order_number",
          "value": "ACM-12345",
          "name": "orderNumber"
        }
      ]
    }
  ]
}
```

### Slot

A **Slot** annotates an entity within an expression text.

| Property | Type   | Required | Default | Constraints | Description                                                             |
| -------- | ------ | -------- | ------- | ----------- | ----------------------------------------------------------------------- |
| `type`   | string | **Yes**  | —       | —           | Entity type recognized by the NLU engine.                               |
| `value`  | string | **Yes**  | —       | —           | The literal substring in the expression text that represents this slot. |
| `name`   | string | **Yes**  | —       | —           | Name under which the extracted value is stored.                         |

{% hint style="info" %}
The `value` in a Slot definition is the training example value — the literal text as it appears in the expression. At runtime, the NLU engine recognizes any matching entity of the given `type`, not just the training value.
{% endhint %}

### Validation

A **Validation** tests an attribute value before executing the workflow's actions.

| Property         | Type                      | Required | Default | Constraints           | Description                                                  |
| ---------------- | ------------------------- | -------- | ------- | --------------------- | ------------------------------------------------------------ |
| `content`        | string                    | **Yes**  | —       | —                     | Name of the attribute to validate.                           |
| `pattern`        | string                    | **Yes**  | —       | Must be a valid regex | Regular expression the attribute value must match.           |
| `executeOnError` | string \| array of string | No       | —       | —                     | Workflow or action name(s) to execute when validation fails. |

```json
{
  "validation": {
    "content": "orderNumber",
    "pattern": "^ACM-\\d{5}$",
    "executeOnError": "invalid-order-number"
  }
}
```

{% hint style="danger" %}
Both `content` and `pattern` are required in a Validation object. Omitting either property produces a schema validation error.
{% endhint %}

## Complete Example

The following example shows a realistic workflow with multiple intent forms, keyword triggers, NLU expressions, attribute validation, and several action types.

```json
{
  "name": "order-tracking",
  "intents": [
    "check_order_status",
    {
      "name": "track_package",
      "excludeContext": "order_dispute_open",
      "expressions": [
        { "text": "track my package" },
        { "text": "where is my shipment", "generate": true }
      ]
    }
  ],
  "keywords": [
    {
      "name": "track_keyword",
      "values": [
        { "value": "TRACK" },
        { "value": "TRACKORDER" }
      ]
    }
  ],
  "expressions": [
    "where is my order",
    "has my order shipped",
    {
      "text": "track order ACM-12345",
      "generate": false,
      "slots": [
        {
          "type": "order_id",
          "value": "ACM-12345",
          "name": "orderNumber"
        }
      ]
    }
  ],
  "validation": {
    "content": "sessionActive",
    "pattern": "^true$",
    "executeOnError": "session-expired"
  },
  "actions": [
    {
      "name": "ask-order-id",
      "send": {
        "message": {
          "text": "Please enter your order number (format: ACM-XXXXX)."
        }
      }
    },
    {
      "name": "capture-order-id",
      "waitFor": {
        "data": "text",
        "content": "orderNumber",
        "timeout": "2m",
        "executeOnTimeout": "order-id-timeout"
      }
    },
    {
      "name": "validate-order-id",
      "validation": {
        "content": "orderNumber",
        "pattern": "^ACM-\\d{5}$",
        "executeOnError": "invalid-order-id"
      }
    },
    {
      "name": "fetch-order-status",
      "send": {
        "request": {
          "url": "https://api.acme.com/orders/{orderNumber}/status",
          "method": "GET",
          "headers": {
            "Authorization": "Bearer {apiToken}",
            "Accept": "application/json"
          },
          "dataFormat": "json",
          "response": {
            "status": "orderStatus",
            "estimatedDelivery": "deliveryDate",
            "carrier": "carrierName"
          },
          "retries": 2,
          "fallback": "order-lookup-failed"
        }
      }
    },
    {
      "name": "send-order-status",
      "send": {
        "message": {
          "text": "Your order {orderNumber} is currently {orderStatus}. Estimated delivery: {deliveryDate} via {carrierName}.",
          "quickReplies": [
            {
              "type": "text",
              "title": "Track shipment",
              "payload": "track_shipment"
            },
            {
              "type": "text",
              "title": "Contact support",
              "payload": "contact_support"
            }
          ]
        }
      }
    }
  ]
}
```


# Actions

An **Action** is a single executable step within a workflow. Actions are processed sequentially. If an action includes a `conditions` array, the platform evaluates those conditions first; if they are not satisfied, the action is skipped and execution continues with the next action.

## Action Properties

| Property           | Type                                                                                                                                                          | Required | Default | Constraints                    | Description                                                                  |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | ------------------------------ | ---------------------------------------------------------------------------- |
| `name`             | string                                                                                                                                                        | No       | —       | Must not start with `nm:`      | Unique identifier for this action. Used as a target by `execute` and `goto`. |
| `conditions`       | array of [Condition](broken://pages/5ec5e72928181d14c580c94fff1c5c7de67c8f86) \| [Operator](broken://pages/5ec5e72928181d14c580c94fff1c5c7de67c8f86#operator) | No       | —       | —                              | Predicates controlling whether this action executes.                         |
| `validation`       | [Validation](broken://pages/d1da14ba55d42ff713017b758d2ac856535a6698#validation)                                                                              | No       | —       | —                              | Attribute validation applied before this action executes.                    |
| `channel`          | string                                                                                                                                                        | No       | —       | e.g. `"rcs"`, `"dsc"`, `"dlc"` | Restricts this action to a specific channel type.                            |
| `send`             | [SendAction](#sendaction)                                                                                                                                     | No       | —       | —                              | Delivers a message, email, HTTP request, note, RSS feed, or data population. |
| `waitFor`          | [WaitForAction](#waitforaction)                                                                                                                               | No       | —       | —                              | Pauses execution to collect user input.                                      |
| `delay`            | [DelayAction](#delayaction)                                                                                                                                   | No       | —       | —                              | Pauses execution for a fixed duration.                                       |
| `execute`          | string \| array of string                                                                                                                                     | No       | —       | —                              | Executes one or more named workflows or actions.                             |
| `goto`             | string                                                                                                                                                        | No       | —       | —                              | Transfers execution to a named workflow or action.                           |
| `assignTags`       | string \| array of string                                                                                                                                     | No       | —       | —                              | Assigns one or more tags to the conversation.                                |
| `assignAttributes` | [AssignAttributesAction](#assignattributesaction)                                                                                                             | No       | —       | —                              | Assigns or updates named attributes.                                         |
| `updateAttribute`  | [UpdateAttributeAction](#updateattributeaction)                                                                                                               | No       | —       | —                              | Updates a single attribute with transformation options.                      |
| `subscribe`        | boolean                                                                                                                                                       | No       | —       | —                              | Subscribes the contact to the current channel when `true`.                   |
| `updateSettings`   | [UpdateSettingsAction](#updatesettingsaction)                                                                                                                 | No       | —       | —                              | Updates experience or conversation settings at runtime.                      |
| `pause`            | [PauseAction](#pauseaction)                                                                                                                                   | No       | —       | —                              | Inserts a short pause (typing indicator) before the next action.             |

{% hint style="info" %}
An action with no primary operation property (no `send`, `waitFor`, `delay`, etc.) is valid — it can exist solely for its `conditions`, `assignTags`, or `assignAttributes` side effects.
{% endhint %}

## SendAction

The `send` property delivers content to the user or an external system. At most one sub-property SHOULD be populated per `send` object.

| Property   | Type                                                               | Required | Description                              |
| ---------- | ------------------------------------------------------------------ | -------- | ---------------------------------------- |
| `message`  | [Message](broken://pages/a96939910c71ba9feedfb956bba3dc3d8c39ad65) | No       | Sends a message to the user.             |
| `email`    | [EmailAction](#emailaction)                                        | No       | Sends an email.                          |
| `request`  | [RequestAction](#requestaction)                                    | No       | Sends an HTTP request.                   |
| `json`     | object \| array                                                    | No       | Sends a raw JSON payload.                |
| `note`     | [NoteAction](#noteaction)                                          | No       | Sends an internal note.                  |
| `rss`      | [RssAction](#rssaction)                                            | No       | Fetches and renders an RSS feed.         |
| `populate` | [PopulateData](#populatedata)                                      | No       | Populates attributes from a data source. |

### EmailAction

Sends an email from within a workflow.

| Property  | Type                      | Required | Default | Constraints             | Description                                          |
| --------- | ------------------------- | -------- | ------- | ----------------------- | ---------------------------------------------------- |
| `to`      | string \| array of string | **Yes**  | —       | Valid email address(es) | Recipient address(es).                               |
| `cc`      | string \| array of string | No       | —       | Valid email address(es) | CC recipient(s).                                     |
| `bcc`     | string \| array of string | No       | —       | Valid email address(es) | BCC recipient(s).                                    |
| `subject` | string                    | No       | —       | —                       | Email subject line. Supports attribute placeholders. |
| `text`    | string                    | No       | —       | —                       | Email body. Supports attribute placeholders.         |
| `isHtml`  | boolean                   | No       | `false` | —                       | When `true`, the `text` body is rendered as HTML.    |

{% code title="email-action.json" %}

```json
{
  "send": {
    "email": {
      "to": "support-team@acme.com",
      "cc": "manager@acme.com",
      "subject": "New support request from {contactName}",
      "text": "Contact: {contactName}\nPhone: {contactPhone}\nIssue: {issueDescription}",
      "isHtml": false
    }
  }
}
```

{% endcode %}

### RequestAction

Sends an HTTP request and optionally maps the response into attributes.

| Property          | Type             | Required    | Default  | Constraints                                       | Description                                                                              |
| ----------------- | ---------------- | ----------- | -------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `url`             | string           | **Yes**     | —        | Must be a valid URL or `{attribute}` placeholder  | Request URL. Supports attribute placeholders.                                            |
| `method`          | string           | No          | `"GET"`  | `GET`, `POST`, `PUT`, `PATCH`, `DELETE`           | HTTP method.                                                                             |
| `dataFormat`      | string           | No          | `"text"` | `text`, `json`, `form`                            | Request body format.                                                                     |
| `headers`         | object           | No          | —        | Map of string → string                            | HTTP request headers.                                                                    |
| `content`         | string \| object | Conditional | —        | Required when method is `POST`, `PUT`, or `PATCH` | Request body.                                                                            |
| `response`        | string \| object | No          | —        | —                                                 | Attribute name or mapping object for storing the response body.                          |
| `responseHeaders` | string \| object | No          | —        | —                                                 | Attribute name or mapping for storing response headers.                                  |
| `retries`         | integer          | No          | `0`      | minimum: 0                                        | Number of retry attempts on failure.                                                     |
| `fallback`        | string           | No          | —        | —                                                 | Workflow or action to execute on request failure.                                        |
| `process`         | boolean          | No          | `true`   | —                                                 | When `true`, attribute placeholders in `content` are substituted before sending.         |
| `async`           | boolean          | No          | `false`  | —                                                 | When `true`, the request is sent asynchronously and execution continues without waiting. |
| `allowHtml`       | boolean          | No          | `false`  | —                                                 | When `true`, HTML in the response is not sanitized.                                      |

{% hint style="danger" %}
`content` is required when `method` is `POST`, `PUT`, or `PATCH`. Omitting it when using these methods produces a validation error.
{% endhint %}

{% hint style="danger" %}
When `dataFormat` is `"json"` and `method` is `POST`, `PUT`, or `PATCH`, the `content` field MUST be a JSON object or a string beginning with `{`.
{% endhint %}

{% code title="request-action-get.json" %}

```json
{
  "send": {
    "request": {
      "url": "https://api.acme.com/v1/orders/{orderNumber}",
      "method": "GET",
      "headers": {
        "Authorization": "Bearer {apiToken}",
        "Accept": "application/json"
      },
      "dataFormat": "json",
      "response": {
        "status": "orderStatus",
        "estimatedDelivery": "deliveryDate"
      },
      "retries": 2,
      "fallback": "api-error-handler"
    }
  }
}
```

{% endcode %}

POST example:

{% code title="request-action-post.json" %}

```json
{
  "send": {
    "request": {
      "url": "https://api.acme.com/v1/leads",
      "method": "POST",
      "dataFormat": "json",
      "headers": {
        "Content-Type": "application/json",
        "Authorization": "Bearer {apiToken}"
      },
      "content": {
        "name": "{contactName}",
        "phone": "{contactPhone}",
        "source": "rcs_chat"
      },
      "response": "leadId",
      "retries": 1,
      "fallback": "lead-creation-failed"
    }
  }
}
```

{% endcode %}

### NoteAction

Sends an internal note (not visible to the end user).

| Property         | Type                      | Required | Default | Constraints | Description                                                     |
| ---------------- | ------------------------- | -------- | ------- | ----------- | --------------------------------------------------------------- |
| `text`           | string                    | **Yes**  | —       | —           | Note content. Supports attribute placeholders.                  |
| `channelId`      | integer \| null           | No       | —       | —           | Target channel ID for the note.                                 |
| `recipientPhone` | string \| null            | No       | —       | —           | Phone number of the recipient. Supports attribute placeholders. |
| `executeOnError` | string \| array of string | No       | —       | —           | Workflow or action to execute on error.                         |

{% code title="note-action.json" %}

```json
{
  "send": {
    "note": {
      "text": "Customer {contactName} requested a refund for order {orderNumber}. Escalation required.",
      "channelId": 42
    }
  }
}
```

{% endcode %}

### RSSAction

Fetches an RSS feed and renders it as a carousel or list.

| Property         | Type   | Required | Default      | Constraints            | Description                 |
| ---------------- | ------ | -------- | ------------ | ---------------------- | --------------------------- |
| `url`            | string | **Yes**  | —            | format: uri            | URL of the RSS feed.        |
| `preferedLayout` | string | No       | `"carousel"` | `"list"`, `"carousel"` | Preferred rendering layout. |

{% code title="rss-action.json" %}

```json
{
  "send": {
    "rss": {
      "url": "https://blog.acme.com/feed.xml",
      "preferedLayout": "carousel"
    }
  }
}
```

{% endcode %}

### PopulateData

Populates attributes from a data source.

| Property               | Type            | Required | Default | Constraints | Description                                   |
| ---------------------- | --------------- | -------- | ------- | ----------- | --------------------------------------------- |
| `from`                 | string \| null  | No       | —       | —           | Source attribute name.                        |
| `attribute`            | string \| null  | No       | —       | —           | Target attribute name.                        |
| `allowInnerAttributes` | boolean \| null | No       | `true`  | —           | Whether to allow nested attribute resolution. |

{% code title="populate-data.json" %}

```json
{
  "send": {
    "populate": {
      "from": "apiResponseData",
      "attribute": "productList",
      "allowInnerAttributes": true
    }
  }
}
```

{% endcode %}

## WaitForAction

Pauses workflow execution until the user provides input of the specified type.

| Property           | Type                      | Required | Default | Constraints                        | Description                                               |
| ------------------ | ------------------------- | -------- | ------- | ---------------------------------- | --------------------------------------------------------- |
| `data`             | string \| array of string | **Yes**  | —       | See enum values                    | Type(s) of data to accept from the user.                  |
| `content`          | string                    | **Yes**  | —       | Pattern: `^[a-zA-Z][a-zA-Z0-9_]*$` | Attribute name in which to store the received input.      |
| `keywords`         | array of KeywordCapture   | No       | —       | —                                  | Keyword-to-attribute mappings recognized during the wait. |
| `executeOnError`   | string \| array of string | No       | —       | —                                  | Target(s) to execute when input fails validation.         |
| `executeOnTimeout` | string \| array of string | No       | —       | —                                  | Target(s) to execute when the wait times out.             |
| `timeout`          | string                    | No       | —       | e.g. `"30s"`, `"5m"`, `"1h"`       | Duration after which the timeout handler fires.           |

**Accepted `data` values:**

| Value            | Description                     |
| ---------------- | ------------------------------- |
| `"message"`      | Any message, including media    |
| `"text"`         | Plain text input                |
| `"quick reply"`  | Quick reply selection           |
| `"multi select"` | Multiple quick reply selections |
| `"number"`       | Numeric input                   |
| `"money"`        | Monetary amount                 |
| `"distance"`     | Distance measurement            |
| `"coordinates"`  | Geographic coordinates          |
| `"datetime"`     | Date and/or time                |
| `"file"`         | File attachment                 |

{% hint style="danger" %}
Both `data` and `content` are required in a `waitFor` object. The `content` value MUST match the pattern `^[a-zA-Z][a-zA-Z0-9_]*$`.
{% endhint %}

KeywordCapture sub-object:

| Property    | Type   | Required | Description                                            |
| ----------- | ------ | -------- | ------------------------------------------------------ |
| `name`      | string | **Yes**  | Keyword name to watch for.                             |
| `attribute` | string | **Yes**  | Attribute in which to store the matched keyword value. |

{% code title="waitfor-action.json" %}

```json
{
  "waitFor": {
    "data": "text",
    "content": "userEmail",
    "timeout": "5m",
    "executeOnTimeout": "email-capture-timeout",
    "executeOnError": "invalid-email-format"
  }
}
```

{% endcode %}

## DelayAction

Pauses execution for a fixed amount of time, with optional timeout routing.

| Property           | Type                      | Required | Default | Constraints | Description                           |
| ------------------ | ------------------------- | -------- | ------- | ----------- | ------------------------------------- |
| `seconds`          | integer                   | No       | —       | minimum: 0  | Delay duration in seconds.            |
| `milliseconds`     | integer                   | No       | —       | minimum: 0  | Delay duration in milliseconds.       |
| `executeOnTimeout` | string \| array of string | No       | —       | —           | Target(s) to execute after the delay. |
| `timeout`          | string                    | No       | —       | —           | Alternative timeout duration string.  |

{% code title="delay-action.json" %}

```json
{
  "delay": {
    "seconds": 3
  }
}
```

{% endcode %}

## Execute

Executes one or more named workflows or actions by name. Use `execute` when you want to run a sub-workflow and then continue in the current workflow. Contrast with `goto`, which does not return.

| Form             | Type            | Description                            |
| ---------------- | --------------- | -------------------------------------- |
| Single target    | string          | Executes the named workflow or action. |
| Multiple targets | array of string | Executes each named target in order.   |

{% code title="execute-single.json" %}

```json
{
  "execute": "send-confirmation-message"
}
```

{% endcode %}

{% code title="execute-multiple.json" %}

```json
{
  "execute": ["log-event", "send-confirmation-message", "notify-agent"]
}
```

{% endcode %}

## Goto

Transfers execution to a named workflow or action. Unlike `execute`, `goto` does not return to the current workflow after the target completes.

{% code title="goto.json" %}

```json
{
  "goto": "main-menu"
}
```

{% endcode %}

## AssignTags

Assigns one or more tags to the conversation.

| Form          | Type            | Description             |
| ------------- | --------------- | ----------------------- |
| Single tag    | string          | Assigns the named tag.  |
| Multiple tags | array of string | Assigns each named tag. |

{% code title="assign-tags.json" %}

```json
{
  "assignTags": ["vip-customer", "order-inquiry"]
}
```

{% endcode %}

## AssignAttributesAction

Assigns or updates multiple attributes in a single action.

| Property                 | Type                                              | Required | Default | Constraints | Description                                                           |
| ------------------------ | ------------------------------------------------- | -------- | ------- | ----------- | --------------------------------------------------------------------- |
| `attributes`             | array of [AttributeValue](#attributevalue)        | **Yes**  | —       | —           | List of attribute assignments.                                        |
| `value`                  | string \| object \| array \| null                 | No       | —       | —           | Default value applied when an `AttributeValue` has no explicit value. |
| `replace`                | string \| null                                    | No       | —       | —           | Regex pattern for string replacement.                                 |
| `process`                | boolean \| null                                   | No       | `true`  | —           | When `true`, attribute placeholders in values are substituted.        |
| `evaluate`               | boolean \| null                                   | No       | `false` | —           | When `true`, values are evaluated as expressions.                     |
| `embeddingMode`          | boolean \| null                                   | No       | `false` | —           | Enables embedding mode for AI vector operations.                      |
| `takeNext`               | boolean \| null                                   | No       | `false` | —           | Extracts the next value in a sequence.                                |
| `takeNextPath`           | string \| null                                    | No       | —       | —           | JSON path for `takeNext` extraction.                                  |
| `multipleValues`         | `"array"` \| `"object"` \| null                   | No       | —       | —           | How to store multiple values.                                         |
| `multipleValuesSettings` | [MultipleValuesSettings](#multiplevaluessettings) | No       | —       | —           | Configuration for multi-value storage.                                |
| `format`                 | string \| null                                    | No       | —       | —           | Format string applied to values.                                      |
| `remove`                 | boolean \| null                                   | No       | `false` | —           | When `true`, removes the attribute instead of setting it.             |

### AttributeValue

| Property                 | Type                                              | Required | Default | Constraints                         | Description                              |
| ------------------------ | ------------------------------------------------- | -------- | ------- | ----------------------------------- | ---------------------------------------- |
| `attributePath`          | string                                            | **Yes**  | —       | Pattern: `^[a-zA-Z][a-zA-Z0-9_.]*$` | Dot-notation path to the attribute.      |
| `value`                  | string \| object \| array \| null                 | No       | —       | —                                   | Value to assign.                         |
| `replace`                | string \| null                                    | No       | —       | —                                   | Regex pattern for string replacement.    |
| `process`                | boolean \| null                                   | No       | —       | —                                   | Attribute placeholder substitution flag. |
| `evaluate`               | boolean \| null                                   | No       | —       | —                                   | Expression evaluation flag.              |
| `embeddingMode`          | boolean \| null                                   | No       | —       | —                                   | Embedding mode flag.                     |
| `takeNext`               | boolean \| null                                   | No       | —       | —                                   | Extract next value in sequence.          |
| `takeNextPath`           | string \| null                                    | No       | —       | —                                   | JSON path for `takeNext`.                |
| `format`                 | string \| null                                    | No       | —       | —                                   | Format string.                           |
| `multipleValues`         | `"array"` \| `"object"` \| null                   | No       | —       | —                                   | Multi-value storage mode.                |
| `multipleValuesSettings` | [MultipleValuesSettings](#multiplevaluessettings) | No       | —       | —                                   | Multi-value configuration.               |
| `remove`                 | boolean \| null                                   | No       | —       | —                                   | Remove flag.                             |

{% hint style="danger" %}
`attributePath` MUST match the pattern `^[a-zA-Z][a-zA-Z0-9_.]*$`. Paths starting with a digit or containing special characters (other than `.` and `_`) are invalid.
{% endhint %}

{% code title="assign-attributes.json" %}

```json
{
  "assignAttributes": {
    "attributes": [
      {
        "attributePath": "contactName",
        "value": "{firstName} {lastName}",
        "process": true
      },
      {
        "attributePath": "order.status",
        "value": "pending"
      },
      {
        "attributePath": "sessionTimestamp",
        "value": "{currentTimestamp}",
        "process": true
      }
    ]
  }
}
```

{% endcode %}

### MultipleValuesSettings

| Property                | Type    | Required | Default | Constraints            | Description                                                   |
| ----------------------- | ------- | -------- | ------- | ---------------------- | ------------------------------------------------------------- |
| `singleValueStandalone` | boolean | No       | `true`  | —                      | When `true`, a single value is stored directly (not wrapped). |
| `delimiter`             | string  | No       | —       | —                      | Delimiter for joining multiple values.                        |
| `prefix`                | string  | No       | —       | —                      | Prefix prepended to each value.                               |
| `suffix`                | string  | No       | —       | —                      | Suffix appended to each value.                                |
| `rootElement`           | string  | No       | —       | —                      | XML/JSON root element name.                                   |
| `valueElement`          | string  | No       | —       | —                      | XML/JSON value element name.                                  |
| `xmlOptions`            | object  | No       | —       | Map of string → string | Additional XML serialization options.                         |

## UpdateAttributeAction

Updates a single attribute. Similar to `AssignAttributesAction` but scoped to one attribute.

| Property    | Type                      | Required | Default | Constraints | Description                           |
| ----------- | ------------------------- | -------- | ------- | ----------- | ------------------------------------- |
| `attribute` | string \| array of string | **Yes**  | —       | —           | Attribute name(s) to update.          |
| `value`     | string \| object \| array | No       | —       | —           | Value to assign.                      |
| `replace`   | string                    | No       | —       | —           | Regex pattern for string replacement. |
| `process`   | boolean                   | No       | `true`  | —           | Attribute placeholder substitution.   |
| `evaluate`  | boolean                   | No       | `false` | —           | Expression evaluation.                |
| `takeNext`  | boolean                   | No       | `false` | —           | Extract next value in sequence.       |

{% hint style="danger" %}
`attribute` is required. An `updateAttribute` object with no `attribute` property is invalid.
{% endhint %}

{% code title="update-attribute.json" %}

```json
{
  "updateAttribute": {
    "attribute": "orderStatus",
    "value": "confirmed",
    "process": false
  }
}
```

{% endcode %}

## Subscribe

When set to `true`, subscribes the current contact to the channel.

{% code title="subscribe.json" %}

```json
{
  "subscribe": true
}
```

{% endcode %}

## UpdateSettingsAction

Updates experience or conversation settings at runtime.

| Property                | Type                   | Required | Description                             |
| ----------------------- | ---------------------- | -------- | --------------------------------------- |
| `rcsExperience`         | object                 | No       | RCS experience settings.                |
| `rcsExperience.enabled` | boolean                | No       | Enables or disables the RCS experience. |
| `conversation`          | object                 | No       | Conversation-level settings.            |
| `conversation.priority` | `"normal"` \| `"high"` | No       | Sets the conversation priority.         |

{% code title="update-settings-priority.json" %}

```json
{
  "updateSettings": {
    "conversation": {
      "priority": "high"
    }
  }
}
```

{% endcode %}

{% code title="update-settings-rcs.json" %}

```json
{
  "updateSettings": {
    "rcsExperience": {
      "enabled": false
    }
  }
}
```

{% endcode %}

## PauseAction

Inserts a brief pause before the next action, typically displayed as a typing indicator.

| Property       | Type    | Required | Default | Constraints | Description                     |
| -------------- | ------- | -------- | ------- | ----------- | ------------------------------- |
| `seconds`      | integer | No       | —       | minimum: 0  | Pause duration in seconds.      |
| `milliseconds` | integer | No       | —       | minimum: 0  | Pause duration in milliseconds. |

{% code title="pause-action.json" %}

```json
{
  "pause": {
    "milliseconds": 800
  }
}
```

{% endcode %}

## Complete Action Sequence Example

The following shows a realistic action sequence that collects user input, calls an API, and branches based on the response.

{% code title="complete-action-sequence.json" %}

```json
[
  {
    "name": "prompt-for-email",
    "send": {
      "message": {
        "text": "Please enter your email address to look up your account."
      }
    }
  },
  {
    "name": "capture-email",
    "waitFor": {
      "data": "text",
      "content": "userEmail",
      "timeout": "3m",
      "executeOnTimeout": "email-timeout-handler"
    }
  },
  {
    "name": "pause-before-lookup",
    "pause": {
      "milliseconds": 500
    }
  },
  {
    "name": "lookup-account",
    "send": {
      "request": {
        "url": "https://api.acme.com/v1/accounts/lookup",
        "method": "POST",
        "dataFormat": "json",
        "headers": {
          "Authorization": "Bearer {apiToken}",
          "Content-Type": "application/json"
        },
        "content": {
          "email": "{userEmail}"
        },
        "response": {
          "accountId": "accountId",
          "tier": "accountTier",
          "found": "accountFound"
        },
        "retries": 1,
        "fallback": "api-error"
      }
    }
  },
  {
    "name": "check-account-found",
    "conditions": [
      {
        "comparisons": [
          ["accountFound", "==", "false"]
        ]
      }
    ],
    "goto": "account-not-found"
  },
  {
    "name": "send-account-summary",
    "send": {
      "message": {
        "text": "Found your account! You are on the {accountTier} plan. Account ID: {accountId}.",
        "quickReplies": [
          {
            "type": "text",
            "title": "View billing",
            "payload": "view_billing"
          },
          {
            "type": "text",
            "title": "Upgrade plan",
            "payload": "upgrade_plan"
          }
        ]
      }
    }
  },
  {
    "name": "tag-account-tier",
    "assignTags": "{accountTier}"
  }
]
```

{% endcode %}


# Messages

The **Message** object defines the content delivered to the user during a `send` action. A message MUST contain at least one of the following content fields: `text`, `media`, `mediaType`+`media`, `buttons`, `quickReplies`, `carousel`, `richCard`, or `title`.

## Message Properties

| Property       | Type                                       | Required    | Default | Constraints                       | Description                                                                |
| -------------- | ------------------------------------------ | ----------- | ------- | --------------------------------- | -------------------------------------------------------------------------- |
| `text`         | string                                     | Conditional | —       | maxLength: 3072                   | Message body text. At least one content field must be present.             |
| `title`        | string                                     | Conditional | —       | maxLength: 200                    | Message title. Satisfies the content requirement alone.                    |
| `mediaType`    | string                                     | Conditional | —       | `image`, `video`, `audio`, `file` | Type of media attachment. Required when `media` is also intended as typed. |
| `media`        | string                                     | Conditional | —       | format: uri                       | URL of the media file. Can be used alone or with `mediaType`.              |
| `buttons`      | array of [Button](#button)                 | Conditional | —       | maxItems: 4 (for RCS rich cards)  | Interactive buttons.                                                       |
| `quickReplies` | array of [QuickReply](#quickreply) \| null | Conditional | —       | maxItems: 11                      | Suggested reply chips.                                                     |
| `carousel`     | array of [RichCard](#richcard)             | Conditional | —       | minItems: 2, maxItems: 10         | Horizontally scrollable rich card sequence.                                |
| `richCard`     | [RichCardConfig](#richcardconfig)          | Conditional | —       | —                                 | Layout configuration for a standalone rich card.                           |

{% hint style="danger" %}
A Message object MUST satisfy at least one of these `anyOf` requirements: `text` present, `mediaType`+`media` present, `media` present alone, `carousel` present, `buttons` present, `quickReplies` present, `richCard` present, or `title` present. An empty message object is invalid.
{% endhint %}

## Channel-Specific Limits

| Feature                   | RCS                       | DSC                                                 | DLC                                                 |
| ------------------------- | ------------------------- | --------------------------------------------------- | --------------------------------------------------- |
| Text max length           | 3072 chars                | <p>1,600 chars MMS,</p><p>160 chars SMS segment</p> | <p>1,600 chars MMS,</p><p>160 chars SMS segment</p> |
| Buttons per message       | 4                         | N/A                                                 | N/A                                                 |
| Quick replies             | Up to 11                  | N/A                                                 | N/A                                                 |
| Carousel cards            | 2–10                      | N/A                                                 | N/A                                                 |
| List items                | 2–4                       | N/A                                                 | N/A                                                 |
| Media types               | image, video, audio, file | image, video, file                                  | image, video, file                                  |
| Rich cards                | Yes                       | No                                                  | No                                                  |
| Button title max          | 25 chars                  | N/A                                                 | N/A                                                 |
| Quick reply title max     | 25 chars                  | N/A                                                 | N/A                                                 |
| Rich card title max       | 200 chars                 | N/A                                                 | N/A                                                 |
| Rich card description max | 2000 chars                | N/A                                                 | N/A                                                 |

{% hint style="warning" %}
Features listed as "N/A" for DLC are silently degraded or omitted when the experience runs on a DLC channel. Design cross-channel experiences with a `channel` action restriction or `channelTypes` condition to serve appropriate content per channel.
{% endhint %}

## Text Message

The simplest message form. The `text` field is the only required content.

{% code title="text-message.json" %}

```json
{
  "send": {
    "message": {
      "text": "Your order ACM-12345 has been shipped. Estimated arrival: March 3, 2025."
    }
  }
}
```

{% endcode %}

## Media Message

A message with a media attachment. `media` is the attachment URL. `mediaType` hints at the type for rendering.

{% code title="media-message.json" %}

```json
{
  "send": {
    "message": {
      "text": "Here is your receipt:",
      "mediaType": "file",
      "media": "https://cdn.acme.com/receipts/ACM-12345.pdf"
    }
  }
}
```

{% endcode %}

Accepted `mediaType` values:

| Value     | Description                         |
| --------- | ----------------------------------- |
| `"image"` | JPEG/JPG, PNG, GIF                  |
| `"video"` | H.263, M4V, MP4, MPEG, MPEG-4, WebM |
| `"audio"` | MP3, OGG, WAV, ACC, MPEG, MP4, 3GPP |
| `"file"`  | PDF                                 |

## Button

A **Button** is an interactive element attached to a message. The required fields depend on the button type.

### Button Properties

| Property          | Type           | Required    | Default | Constraints            | Description                                                                                                |
| ----------------- | -------------- | ----------- | ------- | ---------------------- | ---------------------------------------------------------------------------------------------------------- |
| `type`            | string         | **Yes**     | —       | See enum               | Button type.                                                                                               |
| `title`           | string         | Conditional | —       | maxLength: 25          | Display label. Required for `weburl`, `postback`, `call`.                                                  |
| `payload`         | string         | Conditional | —       | maxLength: 1000        | URL for `weburl`, phone for `call`, custom data for `postback`. Required for `weburl`, `postback`, `call`. |
| `execute`         | string \| null | No          | —       | —                      | Workflow or action to execute on click (postback buttons).                                                 |
| `postbackData`    | string \| null | No          | —       | —                      | Postback data sent on click.                                                                               |
| `description`     | string \| null | No          | —       | —                      | Descriptive text for the button.                                                                           |
| `application`     | string \| null | No          | —       | —                      | Application identifier.                                                                                    |
| `webviewViewMode` | string         | No          | —       | `FULL`, `HALF`, `TALL` | Webview display mode.                                                                                      |
| `eventTitle`      | string         | No          | —       | —                      | Calendar event title (calendar type).                                                                      |
| `startTime`       | string         | No          | —       | ISO 8601               | Calendar event start (calendar type).                                                                      |
| `endTime`         | string         | No          | —       | ISO 8601               | Calendar event end (calendar type).                                                                        |
| `query`           | string         | No          | —       | —                      | Location search query (viewLocation type).                                                                 |
| `latLong`         | object         | No          | —       | —                      | `{ latitude, longitude }` (viewLocation type).                                                             |
| `label`           | string         | No          | —       | —                      | Location label (viewLocation type).                                                                        |
| `theme`           | object         | No          | —       | —                      | Button theme configuration.                                                                                |

### Button Types and Required Fields

| Type           | `title`          | `payload`               | Additional Required Fields                       |
| -------------- | ---------------- | ----------------------- | ------------------------------------------------ |
| `weburl`       | **Yes** (max 25) | **Yes** (URL, max 1000) | —                                                |
| `postback`     | **Yes** (max 25) | **Yes** (max 1000)      | —                                                |
| `call`         | **Yes** (max 25) | **Yes** (phone number)  | —                                                |
| `text`         | No               | No                      | —                                                |
| `location`     | No               | No                      | —                                                |
| `viewLocation` | No               | No                      | `query` or `latLong` recommended                 |
| `calendar`     | No               | No                      | `eventTitle`, `startTime`, `endTime` recommended |
| `datetime`     | No               | No                      | —                                                |

{% code title="buttons.json" %}

```json
{
  "buttons": [
    {
      "type": "weburl",
      "title": "Track shipment",
      "payload": "https://tracking.acme.com/ACM-12345"
    },
    {
      "type": "postback",
      "title": "Cancel order",
      "payload": "cancel_ACM-12345",
      "execute": "cancel-order-workflow"
    },
    {
      "type": "call",
      "title": "Call support",
      "payload": "+18005551234"
    },
    {
      "type": "viewLocation",
      "title": "Find nearest store",
      "query": "Acme Store New York"
    }
  ]
}
```

{% endcode %}

## QuickReply

**Quick replies** are suggested response chips displayed below a message. Up to 11 MAY be included.

### QuickReply Properties

| Property          | Type           | Required    | Default | Constraints            | Description                                              |
| ----------------- | -------------- | ----------- | ------- | ---------------------- | -------------------------------------------------------- |
| `type`            | string         | **Yes**     | —       | See enum               | Quick reply type.                                        |
| `title`           | string         | Conditional | —       | maxLength: 25          | Display label. Required for `text` and `postback` types. |
| `payload`         | string         | No          | —       | maxLength: 1000        | Data sent when selected.                                 |
| `execute`         | string \| null | No          | —       | —                      | Workflow or action to execute on selection.              |
| `postbackData`    | string \| null | No          | —       | —                      | Postback data.                                           |
| `webviewViewMode` | string         | No          | —       | `FULL`, `HALF`, `TALL` | Webview mode.                                            |
| `application`     | string \| null | No          | —       | —                      | Application identifier.                                  |

### Quick Reply Types and Required Fields

| Type           | `title` Required | Description                                      |
| -------------- | ---------------- | ------------------------------------------------ |
| `text`         | **Yes**          | Text chip that sends its title as user input     |
| `postback`     | **Yes**          | Sends payload without displaying as user message |
| `location`     | No               | Prompts user to share location                   |
| `viewLocation` | No               | Opens map view                                   |
| `calendar`     | No               | Opens calendar picker                            |
| `weburl`       | No               | Opens a URL                                      |
| `call`         | No               | Initiates a phone call                           |
| `datetime`     | No               | Opens datetime picker                            |

{% code title="quick-replies.json" %}

```json
{
  "quickReplies": [
    {
      "type": "text",
      "title": "Yes, confirm",
      "payload": "confirm_yes",
      "execute": "confirm-order"
    },
    {
      "type": "text",
      "title": "No, cancel",
      "payload": "confirm_no",
      "execute": "cancel-order"
    },
    {
      "type": "location",
      "title": "Share my location"
    }
  ]
}
```

{% endcode %}

## Carousel

A **carousel** is a horizontally scrollable sequence of 2–10 [RichCard](#richcard) objects.

{% hint style="danger" %}
A `carousel` array MUST contain a minimum of 2 items and a maximum of 10 items. Arrays outside this range are invalid.
{% endhint %}

{% code title="carousel.json" %}

```json
{
  "send": {
    "message": {
      "carousel": [
        {
          "title": "Acme Pro Running Shoe",
          "description": "Lightweight carbon-fiber sole. Available in 8 colors.",
          "media": "https://cdn.acme.com/products/pro-runner.jpg",
          "mediaType": "image",
          "mediaHeight": "MEDIUM",
          "buttons": [
            {
              "type": "weburl",
              "title": "Shop now",
              "payload": "https://shop.acme.com/pro-runner"
            },
            {
              "type": "postback",
              "title": "Add to cart",
              "payload": "cart_add_pro-runner",
              "execute": "add-to-cart"
            }
          ]
        },
        {
          "title": "Acme Trail Blazer",
          "description": "All-terrain grip. Waterproof membrane. 6mm lug depth.",
          "media": "https://cdn.acme.com/products/trail-blazer.jpg",
          "mediaType": "image",
          "mediaHeight": "MEDIUM",
          "buttons": [
            {
              "type": "weburl",
              "title": "Shop now",
              "payload": "https://shop.acme.com/trail-blazer"
            },
            {
              "type": "postback",
              "title": "Add to cart",
              "payload": "cart_add_trail-blazer",
              "execute": "add-to-cart"
            }
          ]
        }
      ]
    }
  }
}
```

{% endcode %}

## RichCard

A **RichCard** is a single card in a carousel. At least one of `title`, `description`, or `media` MUST be present.

### RichCard Properties

| Property          | Type                               | Required    | Default | Constraints                       | Description                           |
| ----------------- | ---------------------------------- | ----------- | ------- | --------------------------------- | ------------------------------------- |
| `title`           | string \| null                     | Conditional | —       | maxLength: 200                    | Card title.                           |
| `description`     | string \| null                     | Conditional | —       | maxLength: 2000                   | Card body text.                       |
| `media`           | string \| null                     | Conditional | —       | —                                 | Media URL (image, GIF, video, PDF).   |
| `mediaType`       | string                             | No          | —       | `image`, `video`, `audio`, `file` | Media type hint.                      |
| `buttons`         | array of [Button](#button) \| null | No          | —       | maxItems: 4                       | Action buttons on the card.           |
| `cardOrientation` | string \| null                     | No          | —       | `VERTICAL`, `HORIZONTAL`          | Card layout orientation.              |
| `mediaHeight`     | string \| null                     | No          | —       | `SHORT`, `MEDIUM`, `TALL`         | Media area height.                    |
| `imageAlignment`  | string \| null                     | No          | —       | `LEFT`, `RIGHT`                   | Image alignment for horizontal cards. |
| `thumbnailUrl`    | string \| null                     | No          | —       | —                                 | Thumbnail URL for video content.      |

{% hint style="danger" %}
Each RichCard MUST satisfy at least one of: `title` present, `description` present, or `media` present. A card with none of these fields is invalid.
{% endhint %}

## RichCardConfig

The `richCard` property on a [Message](#message-properties) provides layout configuration for a standalone rich card message.

### RichCardConfig Properties

| Property          | Type           | Required | Default | Constraints               | Description                              |
| ----------------- | -------------- | -------- | ------- | ------------------------- | ---------------------------------------- |
| `cardOrientation` | string \| null | No       | —       | `VERTICAL`, `HORIZONTAL`  | Card layout orientation.                 |
| `mediaHeight`     | string \| null | No       | —       | `SHORT`, `MEDIUM`, `TALL` | Media area height.                       |
| `imageAlignment`  | string \| null | No       | —       | `LEFT`, `RIGHT`           | Image alignment (horizontal cards only). |
| `thumbnailUrl`    | string \| null | No       | —       | —                         | Thumbnail URL for media.                 |

{% code title="richcard-config.json" %}

```json
{
  "send": {
    "message": {
      "title": "Acme Spring Sale",
      "text": "Up to 40% off select items this weekend only.",
      "media": "https://cdn.acme.com/banners/spring-sale-2025.jpg",
      "mediaType": "image",
      "richCard": {
        "cardOrientation": "VERTICAL",
        "mediaHeight": "TALL"
      },
      "buttons": [
        {
          "type": "weburl",
          "title": "Shop the sale",
          "payload": "https://shop.acme.com/sale"
        }
      ]
    }
  }
}
```

{% endcode %}


# Conditions

**Conditions** control whether an individual [Actions](/rcs-experience-schema/reference/actions) executes at runtime. An action's `conditions` array is evaluated before the action runs; if the conditions are not satisfied, the action is skipped.

A `conditions` array MAY contain any mix of [Condition](#condition) objects and [Operator](#operator) objects. Operators are used to compose multiple conditions with boolean logic.

## Condition Properties

| Property          | Type                       | Required | Default | Constraints                         | Description                                                                |
| ----------------- | -------------------------- | -------- | ------- | ----------------------------------- | -------------------------------------------------------------------------- |
| `channelTypes`    | string \| array of string  | No       | —       | —                                   | Restricts to one or more channel type identifiers (e.g. `"rcs"`, `"dsc"`). |
| `channelIds`      | integer \| string \| array | No       | —       | —                                   | Restricts to specific channel IDs.                                         |
| `tags`            | string \| array of string  | No       | —       | —                                   | Requires the conversation to have these tag(s) assigned.                   |
| `deviceTypes`     | string \| array of string  | No       | —       | If available                        | Restricts to device type(s).                                               |
| `devicePlatforms` | string \| array of string  | No       | —       | If available                        | Restricts to device platform(s).                                           |
| `comparisons`     | array of Comparison        | No       | —       | Each item: array of exactly 3 items | Attribute value comparisons.                                               |
| `unit`            | string                     | No       | —       | `"km"`, `"m"`, `"mi"`, `"ft"`       | Unit for distance comparisons.                                             |
| `precision`       | number                     | No       | —       | —                                   | Decimal precision for numeric comparisons.                                 |

## Comparison Operators

Each entry in `comparisons` is a three-element array: `[attributeName, operator, value]`.

| Operator     | Applies to              | Semantics                                                     |
| ------------ | ----------------------- | ------------------------------------------------------------- |
| `==`         | string, number, boolean | Attribute value equals comparison value (strict equality).    |
| `!=`         | string, number, boolean | Attribute value does not equal comparison value.              |
| `<`          | number                  | Attribute value is less than comparison value.                |
| `>`          | number                  | Attribute value is greater than comparison value.             |
| `<=`         | number                  | Attribute value is less than or equal to comparison value.    |
| `>=`         | number                  | Attribute value is greater than or equal to comparison value. |
| `contains`   | string                  | Attribute value contains comparison value as a substring.     |
| `startsWith` | string                  | Attribute value starts with comparison value.                 |
| `endsWith`   | string                  | Attribute value ends with comparison value.                   |

The third element (comparison value) MUST be a string, number, or boolean.

## Operator

An **Operator** object is inserted into the `conditions` array to combine adjacent Condition objects with boolean logic.

| Property   | Type   | Required | Constraints              | Description                |
| ---------- | ------ | -------- | ------------------------ | -------------------------- |
| `operator` | string | **Yes**  | `"AND"`, `"OR"`, `"NOT"` | Logical operator to apply. |

### AND / OR / NOT Semantics

* **AND** — All conditions on both sides MUST be satisfied.
* **OR** — At least one condition on either side MUST be satisfied.
* **NOT** — The following condition MUST NOT be satisfied.

{% hint style="info" %}
When no Operator is specified between multiple Condition objects in a `conditions` array, the platform's default combining behavior is AND. For explicit control, always include Operator objects.
{% endhint %}

## Composition Rules

{% stepper %}
{% step %}

#### Condition and Operator evaluation order

Condition objects and Operator objects are evaluated in array order.
{% endstep %}

{% step %}

#### Operator scope

An Operator applies to the Conditions immediately surrounding it.
{% endstep %}

{% step %}

#### NOT scope

NOT applies only to the Condition immediately following it.
{% endstep %}

{% step %}

#### Nesting

Nesting is achieved by ordering Conditions and Operators carefully; the schema does not support a parenthetical grouping syntax.
{% endstep %}
{% endstepper %}

## Examples

### Single Condition — Channel Type

Execute this action only on RCS channels:

```json
{
  "conditions": [
    {
      "channelTypes": "rcs"
    }
  ],
  "send": {
    "message": {
      "text": "Here is your RCS-enhanced order summary.",
      "carousel": [
        {
          "title": "Order ACM-12345",
          "description": "2x Acme Pro Runner — Size 10",
          "media": "https://cdn.acme.com/products/pro-runner.jpg"
        },
        {
          "title": "Order total",
          "description": "$149.99 — Paid"
        }
      ]
    }
  }
}
```

### Single Condition — Attribute Comparison

Execute this action only when the attribute `accountTier` equals `"premium"`:

```json
{
  "conditions": [
    {
      "comparisons": [
        ["accountTier", "==", "premium"]
      ]
    }
  ],
  "send": {
    "message": {
      "text": "As a Premium member, you qualify for free expedited shipping on this return."
    }
  }
}
```

### Multiple Comparisons Within One Condition

Multiple entries in the `comparisons` array within a single Condition are combined with AND:

```json
{
  "conditions": [
    {
      "comparisons": [
        ["accountTier", "==", "premium"],
        ["orderTotal", ">=", 100]
      ]
    }
  ],
  "assignTags": "eligible-for-gift"
}
```

### AND Operator — Two Conditions

Both conditions must be satisfied:

```json
{
  "conditions": [
    {
      "channelTypes": "rcs"
    },
    {
      "operator": "AND"
    },
    {
      "comparisons": [
        ["hasOptedIn", "==", true]
      ]
    }
  ],
  "send": {
    "message": {
      "text": "You are subscribed to RCS promotional messages."
    }
  }
}
```

### OR Operator — Two Conditions

Either condition satisfies the check:

```json
{
  "conditions": [
    {
      "tags": "vip-customer"
    },
    {
      "operator": "OR"
    },
    {
      "comparisons": [
        ["lifetimeSpend", ">=", 1000]
      ]
    }
  ],
  "execute": "vip-offer-workflow"
}
```

### NOT Operator — Negated Condition

Execute this action only when the user does NOT have the `opted-out` tag:

```json
{
  "conditions": [
    {
      "operator": "NOT"
    },
    {
      "tags": "opted-out"
    }
  ],
  "send": {
    "message": {
      "text": "We have a special offer just for you this week!"
    }
  }
}
```

### Complex Composition — Tags AND (Channel OR Attribute)

```json
{
  "conditions": [
    {
      "tags": "active-subscriber"
    },
    {
      "operator": "AND"
    },
    {
      "channelTypes": ["rcs", "dsc"]
    },
    {
      "operator": "OR"
    },
    {
      "comparisons": [
        ["forceRichContent", "==", true]
      ]
    }
  ],
  "send": {
    "message": {
      "text": "Here is your personalized weekly summary.",
      "mediaType": "image",
      "media": "https://cdn.acme.com/summaries/weekly-2025-03.png"
    }
  }
}
```

### Channel ID Restriction

Restrict an action to specific channel IDs:

```json
{
  "conditions": [
    {
      "channelIds": [101, 204]
    }
  ],
  "send": {
    "message": {
      "text": "This message targets channels 101 and 204 only."
    }
  }
}
```

### Distance-Based Condition

Check proximity using coordinates with unit specification:

```json
{
  "conditions": [
    {
      "comparisons": [
        ["distanceToStore", "<=", 5]
      ],
      "unit": "km",
      "precision": 1
    }
  ],
  "send": {
    "message": {
      "text": "You are within 5 km of an Acme store. Visit us today!"
    }
  }
}
```


# Common Patterns

This guide provides implementation recipes for common nativeMsg experience patterns. Each recipe follows the format: **Problem → Normative approach → Complete JSON example → Notes**.

***

## 1. Greeting Workflow with Quick Replies

**Problem:** Display a welcome message with tappable reply options when a user initiates conversation.

**Normative approach:** Define a workflow with the `greeting` intent and a `send` action containing a `message` with a `quickReplies` array. The `welcomeMessageExecute` property at the root SHOULD reference this workflow so it also fires on channel invitation.

```json
{
  "name": "Greeting Pattern",
  "welcomeMessageExecute": "greeting",
  "workflows": [
    {
      "name": "greeting",
      "intents": ["greeting"],
      "expressions": ["hello", "hi", "hey", "start", "help", "menu"],
      "actions": [
        {
          "name": "send-greeting",
          "send": {
            "message": {
              "text": "Hi! I am the Acme assistant. What would you like to do?",
              "quickReplies": [
                {
                  "type": "text",
                  "title": "Track an order",
                  "payload": "track_order",
                  "execute": "order-tracking"
                },
                {
                  "type": "text",
                  "title": "Start a return",
                  "payload": "start_return",
                  "execute": "returns"
                },
                {
                  "type": "text",
                  "title": "Store locations",
                  "payload": "store_locations",
                  "execute": "find-stores"
                }
              ]
            }
          }
        }
      ]
    }
  ]
}
```

**Notes:** Quick reply chips disappear after the user selects one. Limit titles to 25 characters or fewer. Up to 11 quick replies are allowed per message.

***

## 2. Collecting User Input with `waitFor` and Attribute Storage

**Problem:** Prompt the user for a specific piece of information and store it for use in subsequent steps.

**Normative approach:** Use a `send` action to prompt, then a `waitFor` action to pause execution until the user responds. Set `content` to the attribute name. Add a `validation` action after capture to verify the format.

```json
{
  "name": "Input Capture Pattern",
  "workflows": [
    {
      "name": "collect-email",
      "intents": ["collect_email"],
      "actions": [
        {
          "name": "prompt-email",
          "send": {
            "message": {
              "text": "Please enter your email address so we can send you a confirmation."
            }
          }
        },
        {
          "name": "capture-email",
          "waitFor": {
            "data": "text",
            "content": "userEmail",
            "timeout": "5m",
            "executeOnTimeout": "email-timeout",
            "executeOnError": "email-error"
          }
        },
        {
          "name": "validate-email-format",
          "validation": {
            "content": "userEmail",
            "pattern": "^[a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,}$",
            "executeOnError": "invalid-email"
          }
        },
        {
          "name": "confirm-email-captured",
          "send": {
            "message": {
              "text": "Got it! We will send your confirmation to {userEmail}.",
              "quickReplies": [
                {
                  "type": "text",
                  "title": "That is correct",
                  "payload": "email_confirmed",
                  "execute": "finalize-registration"
                },
                {
                  "type": "text",
                  "title": "Change email",
                  "payload": "change_email",
                  "execute": "collect-email"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "invalid-email",
      "intents": ["invalid_email_error"],
      "actions": [
        {
          "name": "send-invalid-email-message",
          "send": {
            "message": {
              "text": "That does not look like a valid email address. Please try again (e.g. name@example.com)."
            }
          }
        },
        {
          "name": "re-capture-email",
          "waitFor": {
            "data": "text",
            "content": "userEmail",
            "timeout": "5m",
            "executeOnTimeout": "email-timeout"
          }
        }
      ]
    }
  ]
}
```

**Notes:** The `content` attribute name MUST match `^[a-zA-Z][a-zA-Z0-9_]*$`. Values captured by `waitFor` are immediately available as `{attributeName}` placeholders in subsequent actions.

***

## 3. Conditional Branching with `conditions` and `goto`

**Problem:** Execute different actions depending on the value of an attribute or the channel type.

**Normative approach:** Add `conditions` to each guarded action. Use `goto` to transfer control to a named action or workflow when a condition is met. Actions without a matching condition are skipped; execution continues with the next action.

```json
{
  "name": "Conditional Branching Pattern",
  "workflows": [
    {
      "name": "tier-offer",
      "intents": ["view_offer"],
      "actions": [
        {
          "name": "fetch-account-tier",
          "send": {
            "request": {
              "url": "https://api.acme.com/v1/accounts/{contactPhone}/tier",
              "method": "GET",
              "headers": {
                "Authorization": "Bearer {apiToken}"
              },
              "response": "accountTier",
              "retries": 1,
              "fallback": "tier-fetch-error"
            }
          }
        },
        {
          "name": "route-premium",
          "conditions": [
            { "comparisons": [["accountTier", "==", "premium"]] }
          ],
          "goto": "premium-offer"
        },
        {
          "name": "route-standard",
          "conditions": [
            { "comparisons": [["accountTier", "==", "standard"]] }
          ],
          "goto": "standard-offer"
        },
        {
          "name": "send-default-offer",
          "send": {
            "message": {
              "text": "Check out our current promotions at acme.com/offers."
            }
          }
        }
      ]
    },
    {
      "name": "premium-offer",
      "intents": ["premium_offer_internal"],
      "actions": [
        {
          "name": "send-premium-offer",
          "send": {
            "message": {
              "text": "As a Premium member, you have exclusive access to our VIP sale — 30% off everything this weekend!",
              "buttons": [
                {
                  "type": "weburl",
                  "title": "Shop VIP sale",
                  "payload": "https://shop.acme.com/vip-sale"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "standard-offer",
      "intents": ["standard_offer_internal"],
      "actions": [
        {
          "name": "send-standard-offer",
          "send": {
            "message": {
              "text": "Here is a 15% discount code just for you: ACME15. Valid through Sunday.",
              "buttons": [
                {
                  "type": "weburl",
                  "title": "Shop now",
                  "payload": "https://shop.acme.com/offers"
                }
              ]
            }
          }
        }
      ]
    }
  ]
}
```

**Notes:** Once a `goto` fires, remaining actions in the current workflow are not evaluated. Use `execute` when you need to invoke a sub-workflow and then continue in the caller.

***

## 4. Calling an External API with `send.request` and Mapping the Response

**Problem:** Retrieve data from an external service and surface the response values in subsequent messages.

**Normative approach:** Use a `send.request` action with a `response` mapping object. Keys in the mapping are JSON response field names; values are the attribute names in which to store them. Specify a `fallback` workflow for error handling.

```json
{
  "name": "API Integration Pattern",
  "workflows": [
    {
      "name": "product-lookup",
      "intents": ["lookup_product"],
      "actions": [
        {
          "name": "prompt-product-id",
          "send": {
            "message": {
              "text": "Enter a product ID to look up (e.g. PRD-001)."
            }
          }
        },
        {
          "name": "capture-product-id",
          "waitFor": {
            "data": "text",
            "content": "productId",
            "timeout": "3m",
            "executeOnTimeout": "lookup-timeout"
          }
        },
        {
          "name": "fetch-product",
          "send": {
            "request": {
              "url": "https://api.acme.com/v1/products/{productId}",
              "method": "GET",
              "headers": {
                "Authorization": "Bearer {apiToken}",
                "Accept": "application/json"
              },
              "dataFormat": "json",
              "response": {
                "name": "productName",
                "price": "productPrice",
                "inStock": "productInStock",
                "imageUrl": "productImageUrl",
                "description": "productDescription"
              },
              "retries": 2,
              "fallback": "product-not-found"
            }
          }
        },
        {
          "name": "send-product-details",
          "send": {
            "message": {
              "title": "{productName}",
              "text": "{productDescription}\n\nPrice: ${productPrice}",
              "mediaType": "image",
              "media": "{productImageUrl}",
              "buttons": [
                {
                  "type": "postback",
                  "title": "Add to cart",
                  "payload": "cart_add_{productId}",
                  "execute": "add-to-cart"
                },
                {
                  "type": "weburl",
                  "title": "View full details",
                  "payload": "https://shop.acme.com/products/{productId}"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "product-not-found",
      "intents": ["product_not_found_error"],
      "actions": [
        {
          "name": "send-not-found-message",
          "send": {
            "message": {
              "text": "I could not find a product with ID {productId}. Please check the ID and try again.",
              "quickReplies": [
                {
                  "type": "text",
                  "title": "Try again",
                  "payload": "retry_lookup",
                  "execute": "product-lookup"
                }
              ]
            }
          }
        }
      ]
    }
  ]
}
```

**Notes:** The `response` mapping object maps top-level JSON response keys to attribute names. For nested structures, use `send.populate` after storing the raw response to an attribute.

***

## 5. Multi-Step Form Using Sequential `waitFor` Actions

**Problem:** Collect multiple pieces of information from the user across several turns.

**Normative approach:** Chain `send` + `waitFor` pairs sequentially within one workflow. Each `waitFor` stores its result in a uniquely named attribute. After all captures, submit or confirm.

```json
{
  "name": "Multi-Step Form Pattern",
  "workflows": [
    {
      "name": "return-request",
      "intents": ["start_return", "return_item"],
      "expressions": [
        "I want to return something",
        "start a return",
        "return an item",
        "get a refund"
      ],
      "actions": [
        {
          "name": "intro-return",
          "send": {
            "message": {
              "text": "I can help you start a return. I need a few details — it will only take a moment."
            }
          }
        },
        {
          "name": "ask-order-number",
          "send": {
            "message": {
              "text": "Step 1 of 3: What is your order number? (e.g. ACM-12345)"
            }
          }
        },
        {
          "name": "capture-order-number",
          "waitFor": {
            "data": "text",
            "content": "returnOrderNumber",
            "timeout": "5m",
            "executeOnTimeout": "form-timeout"
          }
        },
        {
          "name": "ask-return-reason",
          "send": {
            "message": {
              "text": "Step 2 of 3: What is the reason for your return?",
              "quickReplies": [
                { "type": "text", "title": "Wrong size", "payload": "wrong_size" },
                { "type": "text", "title": "Defective item", "payload": "defective" },
                { "type": "text", "title": "Changed my mind", "payload": "changed_mind" },
                { "type": "text", "title": "Wrong item received", "payload": "wrong_item" }
              ]
            }
          }
        },
        {
          "name": "capture-return-reason",
          "waitFor": {
            "data": ["text", "quick reply"],
            "content": "returnReason",
            "timeout": "5m",
            "executeOnTimeout": "form-timeout"
          }
        },
        {
          "name": "ask-preferred-resolution",
          "send": {
            "message": {
              "text": "Step 3 of 3: How would you like to resolve this?",
              "quickReplies": [
                { "type": "text", "title": "Full refund", "payload": "refund" },
                { "type": "text", "title": "Exchange", "payload": "exchange" },
                { "type": "text", "title": "Store credit", "payload": "store_credit" }
              ]
            }
          }
        },
        {
          "name": "capture-resolution",
          "waitFor": {
            "data": ["text", "quick reply"],
            "content": "returnResolution",
            "timeout": "5m",
            "executeOnTimeout": "form-timeout"
          }
        },
        {
          "name": "submit-return-request",
          "send": {
            "request": {
              "url": "https://api.acme.com/v1/returns",
              "method": "POST",
              "dataFormat": "json",
              "headers": {
                "Authorization": "Bearer {apiToken}",
                "Content-Type": "application/json"
              },
              "content": {
                "orderNumber": "{returnOrderNumber}",
                "reason": "{returnReason}",
                "resolution": "{returnResolution}",
                "contactPhone": "{contactPhone}"
              },
              "response": "returnCaseId",
              "retries": 2,
              "fallback": "return-submission-failed"
            }
          }
        },
        {
          "name": "confirm-return",
          "send": {
            "message": {
              "text": "Your return request has been submitted!\n\nCase ID: {returnCaseId}\nOrder: {returnOrderNumber}\nReason: {returnReason}\nResolution: {returnResolution}\n\nYou will receive an email confirmation shortly."
            }
          }
        }
      ]
    }
  ]
}
```

**Notes:** Accepting both `"text"` and `"quick reply"` in the `data` array allows users to either tap a chip or type a free-text response. Each `waitFor` blocks execution until the user responds or the timeout fires.

***

## 6. Transferring to a Human Agent Using `updateSettings`

**Problem:** Escalate a conversation to a live agent when the user requests it or when automated handling fails.

**Normative approach:** Set conversation priority to `"high"`, assign routing tags, send an internal note for context, and confirm to the user. Disabling the experience via `updateSettings.rcsExperience.enabled: false` stops further automated responses.

```json
{
  "name": "Human Escalation Pattern",
  "workflows": [
    {
      "name": "escalate-to-human",
      "intents": ["human_agent", "speak_to_agent", "escalate"],
      "expressions": [
        "I want to speak to a person",
        "connect me to an agent",
        "human please",
        "let me talk to someone",
        "real person"
      ],
      "actions": [
        {
          "name": "acknowledge-escalation",
          "send": {
            "message": {
              "text": "I am connecting you with a support agent now. Average wait time is under 2 minutes."
            }
          }
        },
        {
          "name": "set-high-priority",
          "updateSettings": {
            "conversation": {
              "priority": "high"
            }
          }
        },
        {
          "name": "tag-for-routing",
          "assignTags": ["escalated", "needs-agent", "human-requested"]
        },
        {
          "name": "store-escalation-context",
          "assignAttributes": {
            "attributes": [
              {
                "attributePath": "escalationTimestamp",
                "value": "{currentTimestamp}",
                "process": true
              },
              {
                "attributePath": "escalationTrigger",
                "value": "user_requested"
              }
            ]
          }
        },
        {
          "name": "notify-agent-queue",
          "send": {
            "note": {
              "text": "Escalation requested by {contactPhone} at {escalationTimestamp}. Reason: {escalationTrigger}.",
              "channelId": 15
            }
          }
        },
        {
          "name": "disable-bot",
          "updateSettings": {
            "rcsExperience": {
              "enabled": false
            }
          }
        }
      ]
    }
  ]
}
```

**Notes:** Setting `rcsExperience.enabled` to `false` prevents further automated responses, leaving the conversation open for a human agent. Re-enable it programmatically via the nativeMsg API when the agent session ends.

***

## 7. Carousel Product Listing

**Problem:** Display a horizontally scrollable set of products, each with an image, description, and action buttons.

**Normative approach:** Use a `send.message` with a `carousel` array. Each entry is a [RichCard](/rcs-experience-schema/reference/messages) with `title`, `description`, `media`, and up to 4 `buttons`. A carousel MUST contain 2–10 cards.

```json
{
  "name": "Carousel Pattern",
  "workflows": [
    {
      "name": "show-featured-products",
      "intents": ["browse_products", "featured_items"],
      "expressions": [
        "show me products",
        "what do you have",
        "browse catalogue",
        "featured items"
      ],
      "actions": [
        {
          "name": "send-product-carousel",
          "send": {
            "message": {
              "text": "Here are our featured products this week:",
              "carousel": [
                {
                  "title": "Acme Pro Runner",
                  "description": "Lightweight carbon-fiber sole. Responsive cushioning. Available in 8 colors.",
                  "media": "https://cdn.acme.com/products/pro-runner-hero.jpg",
                  "mediaType": "image",
                  "mediaHeight": "MEDIUM",
                  "buttons": [
                    {
                      "type": "weburl",
                      "title": "View details",
                      "payload": "https://shop.acme.com/products/pro-runner"
                    },
                    {
                      "type": "postback",
                      "title": "Add to cart",
                      "payload": "cart_add_pro-runner",
                      "execute": "add-to-cart"
                    }
                  ]
                },
                {
                  "title": "Acme Trail Blazer",
                  "description": "All-terrain grip with waterproof membrane. 6 mm lug depth. Sizes 6–15.",
                  "media": "https://cdn.acme.com/products/trail-blazer-hero.jpg",
                  "mediaType": "image",
                  "mediaHeight": "MEDIUM",
                  "buttons": [
                    {
                      "type": "weburl",
                      "title": "View details",
                      "payload": "https://shop.acme.com/products/trail-blazer"
                    },
                    {
                      "type": "postback",
                      "title": "Add to cart",
                      "payload": "cart_add_trail-blazer",
                      "execute": "add-to-cart"
                    }
                  ]
                },
                {
                  "title": "Acme Urban Walker",
                  "description": "Memory foam insole. Slip-resistant outsole. Designed for all-day wear.",
                  "media": "https://cdn.acme.com/products/urban-walker-hero.jpg",
                  "mediaType": "image",
                  "mediaHeight": "MEDIUM",
                  "buttons": [
                    {
                      "type": "weburl",
                      "title": "View details",
                      "payload": "https://shop.acme.com/products/urban-walker"
                    },
                    {
                      "type": "postback",
                      "title": "Add to cart",
                      "payload": "cart_add_urban-walker",
                      "execute": "add-to-cart"
                    }
                  ]
                }
              ]
            }
          }
        }
      ]
    }
  ]
}
```

**Notes:** Each card supports up to 4 buttons. `mediaHeight` controls the image area: `SHORT` (\~112 dp), `MEDIUM` (\~168 dp), `TALL` (\~264 dp). Carousels are RCS-specific; use a list or sequential text messages as fallback on non-RCS channels.

***

## 8. RCS Rich Card with Channel Fallback

**Problem:** Send a visually rich message on RCS channels while delivering a plain-text fallback on channels that do not support rich cards.

**Normative approach:** Use the action `channel` property to restrict rich-content actions to RCS, and define parallel actions restricted to other channels for graceful degradation.

```json
{
  "name": "Channel Fallback Pattern",
  "workflows": [
    {
      "name": "send-promo-announcement",
      "intents": ["promo_announcement"],
      "actions": [
        {
          "name": "rich-card-rcs",
          "channel": "rcs",
          "send": {
            "message": {
              "title": "Spring Sale — Up to 40% Off",
              "text": "Shop our biggest sale of the year. This weekend only.",
              "mediaType": "image",
              "media": "https://cdn.acme.com/banners/spring-sale-2025.jpg",
              "richCard": {
                "cardOrientation": "VERTICAL",
                "mediaHeight": "TALL"
              },
              "buttons": [
                {
                  "type": "weburl",
                  "title": "Shop now",
                  "payload": "https://shop.acme.com/spring-sale"
                },
                {
                  "type": "postback",
                  "title": "Save for later",
                  "payload": "save_promo_spring2025",
                  "execute": "save-promo"
                }
              ]
            }
          }
        },
        {
          "name": "text-fallback-dsc",
          "channel": "dsc",
          "send": {
            "message": {
              "text": "Spring Sale — Up to 40% Off! Shop our biggest sale of the year this weekend only: https://shop.acme.com/spring-sale"
            }
          }
        },
        {
          "name": "text-fallback-dlc",
          "channel": "dlc",
          "send": {
            "message": {
              "text": "Acme Spring Sale: Up to 40% off this weekend. Shop at acme.com/spring-sale. Reply STOP to opt out."
            }
          }
        }
      ]
    }
  ]
}
```

**Notes:** The `channel` property on an action restricts execution to that channel type string exactly. Only one of the three actions above will fire per conversation turn, depending on the active channel. This pattern avoids duplicating entire workflows for channel-specific content.


# Channel Compatibility

This guide defines the normative capability matrix across nativeMsg-supported channel types, provides channel identifier reference strings, and specifies the rules for designing experiences that degrade gracefully across channels.

## Channel Identifier Strings

The following strings are used in the action `channel` property and the condition `channelTypes` field.

| Identifier | Channel Type                | Description                                                  |
| ---------- | --------------------------- | ------------------------------------------------------------ |
| `rcs`      | Rich Communication Services | RCS Business Messaging over the native Android messaging app |
| `dsc`      | DSC                         | Direct-to-consumer messaging channel with rich card support  |
| `dlc`      | DLC                         | 10DLC SMS/MMS messaging (limited formatting)                 |

{% hint style="info" %}
Channel identifier strings are case-sensitive. `"RCS"` and `"rcs"` are not equivalent in condition or action channel fields.
{% endhint %}

## Feature × Channel Capability Matrix

The table below lists every schema feature and its support status per channel. Where a feature is supported, the exact maximum or constraint is stated. Where a feature is unsupported, the degradation behavior is noted.

| Feature                                  | RCS                             | DSC                   | DLC                                                                           |
| ---------------------------------------- | ------------------------------- | --------------------- | ----------------------------------------------------------------------------- |
| **Text message**                         | ✓ Max 3072 chars                | ✓ Max 3072 chars      | ✓ Max 160 chars per SMS segment; longer messages split into multiple segments |
| **Message title**                        | ✓ Max 200 chars                 | ✓ Max 200 chars       | ✗ Ignored                                                                     |
| **Media — image**                        | ✓ JPEG, PNG, GIF, WebP          | ✓ JPEG, PNG, GIF      | ✓ MMS only; file size limits apply per carrier                                |
| **Media — video**                        | ✓ MP4, WebM                     | ✓ MP4                 | ✗ Not supported                                                               |
| **Media — audio**                        | ✓ MP3, OGG, WAV                 | ✗ Not supported       | ✗ Not supported                                                               |
| **Media — file**                         | ✓ PDF and common document types | ✓ PDF                 | ✗ Not supported                                                               |
| **Buttons — total per message**          | Max 4                           | Max 4                 | ✗ Not supported                                                               |
| **Button type: weburl**                  | ✓                               | ✓                     | ✗                                                                             |
| **Button type: postback**                | ✓                               | ✓                     | ✗                                                                             |
| **Button type: call**                    | ✓                               | ✓                     | ✗                                                                             |
| **Button type: text**                    | ✓                               | ✓                     | ✗                                                                             |
| **Button type: location**                | ✓                               | ✗                     | ✗                                                                             |
| **Button type: viewLocation**            | ✓                               | ✗                     | ✗                                                                             |
| **Button type: calendar**                | ✓                               | ✗                     | ✗                                                                             |
| **Button type: datetime**                | ✓                               | ✗                     | ✗                                                                             |
| **Button title max**                     | 25 chars                        | 25 chars              | N/A                                                                           |
| **Button payload max**                   | 1000 chars                      | 1000 chars            | N/A                                                                           |
| **Quick replies**                        | ✓ Max 13                        | ✓ Max 13              | ✗ Not supported                                                               |
| **Quick reply title max**                | 25 chars                        | 25 chars              | N/A                                                                           |
| **Quick reply — image**                  | ✓ (`imageUrl`)                  | ✓ (`imageUrl`)        | ✗                                                                             |
| **Quick reply type: location**           | ✓                               | ✗                     | ✗                                                                             |
| **Quick reply type: datetime**           | ✓                               | ✗                     | ✗                                                                             |
| **Quick reply type: calendar**           | ✓                               | ✗                     | ✗                                                                             |
| **Carousel**                             | ✓ Min 2, Max 10 cards           | ✓ Min 2, Max 10 cards | ✗ Not supported                                                               |
| **List**                                 | ✓ Min 2, Max 4 items            | ✓ Min 2, Max 4 items  | ✗ Not supported                                                               |
| **Rich card (standalone)**               | ✓                               | ✓                     | ✗ Not supported                                                               |
| **Rich card — VERTICAL orientation**     | ✓                               | ✓                     | ✗                                                                             |
| **Rich card — HORIZONTAL orientation**   | ✓                               | ✓                     | ✗                                                                             |
| **Rich card title max**                  | 200 chars                       | 200 chars             | N/A                                                                           |
| **Rich card description max**            | 2000 chars                      | 2000 chars            | N/A                                                                           |
| **Rich card buttons max**                | 4                               | 4                     | N/A                                                                           |
| **mediaHeight: SHORT**                   | ✓                               | ✓                     | ✗                                                                             |
| **mediaHeight: MEDIUM**                  | ✓                               | ✓                     | ✗                                                                             |
| **mediaHeight: TALL**                    | ✓                               | ✓                     | ✗                                                                             |
| **imageAlignment: LEFT / RIGHT**         | ✓ (HORIZONTAL only)             | ✓ (HORIZONTAL only)   | ✗                                                                             |
| **waitFor: text**                        | ✓                               | ✓                     | ✓                                                                             |
| **waitFor: number**                      | ✓                               | ✓                     | ✓                                                                             |
| **waitFor: datetime**                    | ✓                               | ✓                     | ✓ (typed by user)                                                             |
| **waitFor: coordinates**                 | ✓                               | ✓                     | ✗                                                                             |
| **waitFor: file**                        | ✓                               | ✓                     | ✗                                                                             |
| **waitFor: quick reply**                 | ✓                               | ✓                     | ✗                                                                             |
| **waitFor: multi select**                | ✓                               | ✓                     | ✗                                                                             |
| **send.email**                           | ✓                               | ✓                     | ✓                                                                             |
| **send.request**                         | ✓                               | ✓                     | ✓                                                                             |
| **send.note**                            | ✓                               | ✓                     | ✓                                                                             |
| **send.rss**                             | ✓                               | ✓                     | ✗                                                                             |
| **assignTags**                           | ✓                               | ✓                     | ✓                                                                             |
| **assignAttributes**                     | ✓                               | ✓                     | ✓                                                                             |
| **updateSettings.conversation.priority** | ✓                               | ✓                     | ✓                                                                             |
| **updateSettings.rcsExperience.enabled** | ✓                               | ✗                     | ✗                                                                             |
| **subscribe**                            | ✓                               | ✓                     | ✓                                                                             |
| **Webview (webviewViewMode)**            | ✓ full, half, tall              | ✓ full, half, tall    | ✗                                                                             |

{% hint style="warning" %}
Features listed as ✗ for a channel are silently ignored or cause a degraded experience — they do not produce a runtime error. Always test your experience on each target channel before deploying.
{% endhint %}

## Designing for Cross-Channel Compatibility

### Normative Rules

1. **Channel-restrict rich actions.** Any action that uses a feature not supported on all target channels MUST include either a `channel` property (restricting execution to the capable channel) or a `conditions` array with a `channelTypes` filter.
2. **Always provide a text fallback.** For every rich-content action (`carousel`, `list`, `richCard`, `buttons`), a parallel action restricted to non-rich channels MUST be defined, sending equivalent information as plain text.
3. **Do not rely on button-triggered `execute` on DLC.** On DLC channels, buttons are not rendered and their `execute` callbacks never fire. All workflow branching on DLC MUST be driven by user-typed text or keyword matching.
4. **Quick replies MUST NOT be the only navigation mechanism.** Any workflow that relies on quick reply selection for progression MUST include a `waitFor` step accepting `"text"` in addition to `"quick reply"`, so text-only channel users can respond.
5. **Test DLC text length.** Messages intended for DLC MUST be designed to fit within 160 characters where possible, or explicitly planned for multi-segment delivery. Attribute placeholder substitution happens before length evaluation; account for the expanded text length.

### Channel-Restriction Pattern

Use the `channel` property on actions to serve channel-appropriate content from a single workflow:

```json
{
  "name": "Cross-Channel Announcement",
  "workflows": [
    {
      "name": "send-announcement",
      "intents": ["announcement"],
      "actions": [
        {
          "name": "rich-announcement-rcs",
          "channel": "rcs",
          "send": {
            "message": {
              "title": "New Feature: Live Order Tracking",
              "text": "Track your order in real time with our new interactive map.",
              "mediaType": "image",
              "media": "https://cdn.acme.com/features/live-tracking-preview.jpg",
              "richCard": {
                "cardOrientation": "VERTICAL",
                "mediaHeight": "MEDIUM"
              },
              "buttons": [
                {
                  "type": "weburl",
                  "title": "Try it now",
                  "payload": "https://shop.acme.com/track"
                }
              ]
            }
          }
        },
        {
          "name": "rich-announcement-dsc",
          "channel": "dsc",
          "send": {
            "message": {
              "title": "New Feature: Live Order Tracking",
              "text": "Track your order in real time with our new interactive map.",
              "buttons": [
                {
                  "type": "weburl",
                  "title": "Try it now",
                  "payload": "https://shop.acme.com/track"
                }
              ]
            }
          }
        },
        {
          "name": "text-announcement-dlc",
          "channel": "dlc",
          "send": {
            "message": {
              "text": "New: Live order tracking is here! Visit acme.com/track to try it. Reply STOP to opt out."
            }
          }
        }
      ]
    }
  ]
}
```

### Condition-Based Channel Routing

Use `conditions` with `channelTypes` when you need to branch execution logic (not just content) by channel:

```json
{
  "name": "check-rcs-capable",
  "conditions": [
    {
      "channelTypes": ["rcs", "dsc"]
    }
  ],
  "send": {
    "message": {
      "text": "Choose an option below:",
      "quickReplies": [
        { "type": "text", "title": "Option A", "payload": "opt_a" },
        { "type": "text", "title": "Option B", "payload": "opt_b" }
      ]
    }
  }
}
```

```json
{
  "name": "dlc-text-prompt",
  "conditions": [
    {
      "channelTypes": "dlc"
    }
  ],
  "send": {
    "message": {
      "text": "Reply A for Option A or B for Option B."
    }
  }
}
```

### RSS Feed Channel Restriction

RSS feed rendering (`send.rss`) is only supported on RCS and DSC. Always restrict this action and provide a fallback:

```json
{
  "name": "send-news-rcs",
  "channel": "rcs",
  "send": {
    "rss": {
      "url": "https://blog.acme.com/feed.xml",
      "preferedLayout": "carousel"
    }
  }
}
```

```json
{
  "name": "send-news-dlc",
  "channel": "dlc",
  "send": {
    "message": {
      "text": "Read our latest articles at blog.acme.com"
    }
  }
}
```


# Coding Language Examples

This guide demonstrates how to validate and deploy a nativeMsg experience using seven different languages and tools. All examples use the same customer support experience definition.

## The Example Experience

The following experience defines three workflows for a customer support bot: a greeting, an order tracking flow, and a human escalation path.

```json
{
  "name": "Acme Customer Support",
  "welcomeMessageExecute": "greeting",
  "workflows": [
    {
      "name": "greeting",
      "intents": ["greeting"],
      "expressions": [
        "hello",
        "hi",
        "hey",
        "good morning",
        "good afternoon",
        "start"
      ],
      "actions": [
        {
          "name": "send-welcome",
          "send": {
            "message": {
              "text": "Welcome to Acme Support! How can I help you today?",
              "quickReplies": [
                {
                  "type": "text",
                  "title": "Track an order",
                  "payload": "track_order",
                  "execute": "order-tracking"
                },
                {
                  "type": "text",
                  "title": "Start a return",
                  "payload": "start_return",
                  "execute": "returns"
                },
                {
                  "type": "text",
                  "title": "Talk to an agent",
                  "payload": "human_agent",
                  "execute": "escalate-to-human"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "order-tracking",
      "intents": ["track_order", "order_status"],
      "expressions": [
        "where is my order",
        "track my package",
        "order status",
        "when will my order arrive",
        "has my order shipped"
      ],
      "actions": [
        {
          "name": "ask-order-number",
          "send": {
            "message": {
              "text": "Please enter your order number (format: ACM-XXXXX)."
            }
          }
        },
        {
          "name": "capture-order-number",
          "waitFor": {
            "data": "text",
            "content": "orderNumber",
            "timeout": "3m",
            "executeOnTimeout": "order-number-timeout"
          }
        },
        {
          "name": "validate-order-number",
          "validation": {
            "content": "orderNumber",
            "pattern": "^ACM-\\d{5}$",
            "executeOnError": "invalid-order-number"
          }
        },
        {
          "name": "fetch-order-status",
          "send": {
            "request": {
              "url": "https://api.acme.com/v1/orders/{orderNumber}",
              "method": "GET",
              "headers": {
                "Authorization": "Bearer {apiToken}",
                "Accept": "application/json"
              },
              "response": {
                "status": "orderStatus",
                "carrier": "carrierName",
                "estimatedDelivery": "deliveryDate"
              },
              "retries": 2,
              "fallback": "order-lookup-failed"
            }
          }
        },
        {
          "name": "send-order-status",
          "send": {
            "message": {
              "text": "Order {orderNumber}: {orderStatus}\nCarrier: {carrierName}\nEstimated delivery: {deliveryDate}",
              "quickReplies": [
                {
                  "type": "text",
                  "title": "Track shipment",
                  "payload": "track_shipment"
                },
                {
                  "type": "text",
                  "title": "Something else",
                  "payload": "main_menu",
                  "execute": "greeting"
                }
              ]
            }
          }
        }
      ]
    },
    {
      "name": "escalate-to-human",
      "intents": ["human_agent", "speak_to_agent"],
      "expressions": [
        "talk to a person",
        "speak to an agent",
        "human please",
        "I need help from a person"
      ],
      "actions": [
        {
          "name": "send-escalation-message",
          "send": {
            "message": {
              "text": "Connecting you to a support agent now. Average wait time is 2 minutes."
            }
          }
        },
        {
          "name": "set-priority-high",
          "updateSettings": {
            "conversation": {
              "priority": "high"
            }
          }
        },
        {
          "name": "tag-for-agent",
          "assignTags": ["needs-human", "escalated"]
        },
        {
          "name": "notify-support-team",
          "send": {
            "email": {
              "to": "support@acme.com",
              "subject": "Escalation request from {contactPhone}",
              "text": "A customer has requested to speak with an agent.\nPhone: {contactPhone}\nTime: {currentTimestamp}"
            }
          }
        }
      ]
    }
  ]
}
```

## Deploying the Experience

The examples below validate the experience locally then POST it to the nativeMsg API. Replace `YOUR_API_KEY` and `YOUR_ACCOUNT_ID` with your actual credentials.

{% tabs %}
{% tab title="JSON" %}
The raw experience definition above is the complete deployable artifact. Save it as `experience.json` and use any of the code examples below to deploy it.
{% endtab %}

{% tab title="JavaScript / Node.js" %}

```javascript
const fs = require('fs');
const https = require('https');
const Ajv = require('ajv');
const addFormats = require('ajv-formats');
const axios = require('axios');

const API_KEY = process.env.NATIVEMSG_API_KEY;
const ACCOUNT_ID = process.env.NATIVEMSG_ACCOUNT_ID;
const SCHEMA_URL = 'https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json';
const API_URL = `https://api.nativemsg.com/v1/bots/`;

async function fetchSchema(url) {
  return new Promise((resolve, reject) => {
    https.get(url, (res) => {
      let data = '';
      res.on('data', (chunk) => { data += chunk; });
      res.on('end', () => resolve(JSON.parse(data)));
      res.on('error', reject);
    });
  });
}

async function deployExperience(experienceFile) {
  const experience = JSON.parse(fs.readFileSync(experienceFile, 'utf8'));

  // Validate
  const ajv = new Ajv({ allErrors: true });
  addFormats(ajv);
  const schema = await fetchSchema(SCHEMA_URL);
  const validate = ajv.compile(schema);

  if (!validate(experience)) {
    console.error('Validation failed:');
    validate.errors.forEach((err) => {
      console.error(`  ${err.instancePath} ${err.message}`);
    });
    process.exit(1);
  }
  console.log('✓ Validation passed');

  // Deploy
  const response = await axios.post(API_URL, experience, {
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    }
  });

  console.log(`✓ Experience deployed. ID: ${response.data.id}`);
  console.log(`  Status: ${response.status}`);
  return response.data;
}

deployExperience('experience.json').catch((err) => {
  console.error('Deployment failed:', err.response?.data || err.message);
  process.exit(1);
});
```

```bash
npm install ajv ajv-formats axios
NATIVEMSG_API_KEY=your_key NATIVEMSG_ACCOUNT_ID=your_id node deploy.js
```

{% endtab %}

{% tab title="Python" %}

```python
import json
import os
import sys
import urllib.request
import requests
from jsonschema import Draft7Validator

API_KEY = os.environ['NATIVEMSG_API_KEY']
ACCOUNT_ID = os.environ['NATIVEMSG_ACCOUNT_ID']
SCHEMA_URL = 'https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json'
API_URL = f'https://api.nativemsg.com/v1/bots'

def load_schema(url):
    with urllib.request.urlopen(url) as response:
        return json.loads(response.read())

def validate_experience(experience, schema):
    validator = Draft7Validator(schema)
    errors = list(validator.iter_errors(experience))
    if errors:
        print('Validation failed:')
        for error in errors:
            path = ' > '.join(str(p) for p in error.absolute_path) or '(root)'
            print(f'  [{path}] {error.message}')
        sys.exit(1)
    print('✓ Validation passed')

def deploy_experience(experience_file):
    with open(experience_file, 'r') as f:
        experience = json.load(f)

    schema = load_schema(SCHEMA_URL)
    validate_experience(experience, schema)

    response = requests.post(
        API_URL,
        json=experience,
        headers={
            'Authorization': f'Bearer {API_KEY}',
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        }
    )
    response.raise_for_status()

    data = response.json()
    print(f'✓ Experience deployed. ID: {data["id"]}')
    print(f'  Status: {response.status_code}')
    return data

if __name__ == '__main__':
    experience_file = sys.argv[1] if len(sys.argv) > 1 else 'experience.json'
    deploy_experience(experience_file)
```

```bash
pip install jsonschema requests
NATIVEMSG_API_KEY=your_key NATIVEMSG_ACCOUNT_ID=your_id python deploy.py experience.json
```

{% endtab %}

{% tab title="TypeScript" %}

```typescript
import * as fs from 'fs';
import * as https from 'https';
import Ajv, { ValidateFunction } from 'ajv';
import addFormats from 'ajv-formats';
import axios, { AxiosResponse } from 'axios';

const API_KEY: string = process.env.NATIVEMSG_API_KEY!;
const ACCOUNT_ID: string = process.env.NATIVEMSG_ACCOUNT_ID!;
const SCHEMA_URL = 'https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json';
const API_URL = `https://api.nativemsg.com/v1/bots`;

interface DeployedExperience {
  id: string;
  name: string;
  status: string;
}

function fetchSchema(url: string): Promise<object> {
  return new Promise((resolve, reject) => {
    https.get(url, (res) => {
      let data = '';
      res.on('data', (chunk: string) => { data += chunk; });
      res.on('end', () => resolve(JSON.parse(data)));
      res.on('error', reject);
    });
  });
}

async function deployExperience(experienceFile: string): Promise<DeployedExperience> {
  const experience = JSON.parse(fs.readFileSync(experienceFile, 'utf8'));

  const ajv = new Ajv({ allErrors: true });
  addFormats(ajv);

  const schema = await fetchSchema(SCHEMA_URL);
  const validate: ValidateFunction = ajv.compile(schema);

  if (!validate(experience)) {
    console.error('Validation failed:');
    (validate.errors || []).forEach((err) => {
      console.error(`  ${err.instancePath} ${err.message}`);
    });
    process.exit(1);
  }
  console.log('✓ Validation passed');

  const response: AxiosResponse<DeployedExperience> = await axios.post<DeployedExperience>(
    API_URL,
    experience,
    {
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        'Content-Type': 'application/json',
        Accept: 'application/json'
      }
    }
  );

  console.log(`✓ Experience deployed. ID: ${response.data.id}`);
  return response.data;
}

deployExperience('experience.json').catch((err: Error) => {
  console.error('Deployment failed:', err.message);
  process.exit(1);
});
```

```bash
npm install ajv ajv-formats axios
npx ts-node deploy.ts
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'json'
require 'net/http'
require 'uri'

API_KEY    = ENV.fetch('NATIVEMSG_API_KEY')
ACCOUNT_ID = ENV.fetch('NATIVEMSG_ACCOUNT_ID')
SCHEMA_URL = URI('https://docs.nativemsg.com/schemas/rcs-experience-schema-1-0-0.json')
API_URL    = URI("https://api.nativemsg.com/v1/bots")

def fetch_json(url)
  response = Net::HTTP.get_response(url)
  raise "Failed to fetch #{url}: #{response.code}" unless response.is_a?(Net::HTTPSuccess)
  JSON.parse(response.body)
end

def deploy_experience(experience_file)
  experience = JSON.parse(File.read(experience_file))

  raise 'Missing required field: name' unless experience.key?('name')
  raise 'Missing required field: workflows' unless experience.key?('workflows')
  raise 'workflows must be an array' unless experience['workflows'].is_a?(Array)
  puts '✓ Basic validation passed'

  request = Net::HTTP::Post.new(API_URL)
  request['Authorization'] = "Bearer #{API_KEY}"
  request['Content-Type']  = 'application/json'
  request['Accept']        = 'application/json'
  request.body = JSON.generate(experience)

  response = Net::HTTP.start(API_URL.host, API_URL.port, use_ssl: true) do |http|
    http.request(request)
  end

  unless response.is_a?(Net::HTTPSuccess)
    raise "Deployment failed: #{response.code} #{response.body}"
  end

  data = JSON.parse(response.body)
  puts "✓ Experience deployed. ID: #{data['id']}"
  data
rescue StandardError => e
  warn "Error: #{e.message}"
  exit 1
end

experience_file = ARGV[0] || 'experience.json'
deploy_experience(experience_file)
```

```bash
# For full Draft-07 validation, add the json-schema gem:
# gem install json-schema
NATIVEMSG_API_KEY=your_key NATIVEMSG_ACCOUNT_ID=your_id ruby deploy.rb experience.json
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	apiKey := os.Getenv("NATIVEMSG_API_KEY")
	accountID := os.Getenv("NATIVEMSG_ACCOUNT_ID")

	if apiKey == "" || accountID == "" {
		fmt.Fprintln(os.Stderr, "NATIVEMSG_API_KEY and NATIVEMSG_ACCOUNT_ID must be set")
		os.Exit(1)
	}

	experienceFile := "experience.json"
	if len(os.Args) > 1 {
		experienceFile = os.Args[1]
	}

	data, err := os.ReadFile(experienceFile)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read file: %v\n", err)
		os.Exit(1)
	}

	var experience map[string]interface{}
	if err := json.Unmarshal(data, &experience); err != nil {
		fmt.Fprintf(os.Stderr, "Invalid JSON: %v\n", err)
		os.Exit(1)
	}
	if _, ok := experience["name"]; !ok {
		fmt.Fprintln(os.Stderr, "Missing required field: name")
		os.Exit(1)
	}
	if _, ok := experience["workflows"]; !ok {
		fmt.Fprintln(os.Stderr, "Missing required field: workflows")
		os.Exit(1)
	}
	fmt.Println("✓ Basic validation passed")

	apiURL := fmt.Sprintf("https://api.nativemsg.com/v1/bots", accountID)
	req, err := http.NewRequest(http.MethodPost, apiURL, bytes.NewReader(data))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create request: %v\n", err)
		os.Exit(1)
	}
	req.Header.Set("Authorization", "Bearer "+apiKey)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Request failed: %v\n", err)
		os.Exit(1)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		fmt.Fprintf(os.Stderr, "API error %d: %s\n", resp.StatusCode, body)
		os.Exit(1)
	}

	var result map[string]interface{}
	json.Unmarshal(body, &result)
	fmt.Printf("✓ Experience deployed. ID: %v\n", result["id"])
}
```

```bash
go run deploy.go experience.json
```

{% hint style="info" %}
For full Draft-07 JSON Schema validation in Go, use a library such as `github.com/qri-io/jsonschema` or `github.com/xeipuuv/gojsonschema`.
{% endhint %}
{% endtab %}

{% tab title="cURL" %}

```bash
#!/usr/bin/env bash
set -euo pipefail

API_KEY="${NATIVEMSG_API_KEY}"
ACCOUNT_ID="${NATIVEMSG_ACCOUNT_ID}"
EXPERIENCE_FILE="${1:-experience.json}"
API_URL="https://api.nativemsg.com/v1/bots"

if ! jq empty "${EXPERIENCE_FILE}" 2>/dev/null; then
  echo "ERROR: ${EXPERIENCE_FILE} is not valid JSON" >&2
  exit 1
fi

NAME=$(jq -r '.name // empty' "${EXPERIENCE_FILE}")
WORKFLOWS=$(jq -r '.workflows // empty' "${EXPERIENCE_FILE}")

if [ -z "${NAME}" ]; then
  echo "ERROR: Missing required field: name" >&2
  exit 1
fi
if [ -z "${WORKFLOWS}" ]; then
  echo "ERROR: Missing required field: workflows" >&2
  exit 1
fi

echo "✓ Basic validation passed (name: ${NAME})"

RESPONSE=$(curl --silent --show-error --fail \
  --request POST \
  --url "${API_URL}" \
  --header "Authorization: Bearer ${API_KEY}" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data-binary "@${EXPERIENCE_FILE}" \
  --write-out "\nHTTP_STATUS:%{http_code}")

HTTP_STATUS=$(echo "${RESPONSE}" | grep "HTTP_STATUS:" | cut -d: -f2)
BODY=$(echo "${RESPONSE}" | grep -v "HTTP_STATUS:")

EXPERIENCE_ID=$(echo "${BODY}" | jq -r '.id')
echo "✓ Experience deployed. ID: ${EXPERIENCE_ID} (HTTP ${HTTP_STATUS})"
```

```bash
chmod +x deploy.sh
NATIVEMSG_API_KEY=your_key NATIVEMSG_ACCOUNT_ID=your_id ./deploy.sh experience.json
```

{% hint style="info" %}
The cURL example requires `jq` for JSON parsing. Install it with `brew install jq` (macOS) or `apt-get install jq` (Debian/Ubuntu).
{% endhint %}
{% endtab %}
{% endtabs %}


# Troubleshooting

This page documents common validation errors, their root causes, and corrected examples. It also provides a normative debugging checklist.

***

## Common Validation Errors

### Missing Required Root Property

**Error message:**

```
must have required property 'workflows'
must have required property 'name'
```

**Root cause:** The root object is missing one or both required properties: `name` or `workflows`.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "workflows": [
    {
      "intents": ["greeting"],
      "actions": [
        {
          "send": {
            "message": { "text": "Hello!" }
          }
        }
      ]
    }
  ]
}
```

Missing: `name`
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "name": "Greeting Bot",
  "workflows": [
    {
      "intents": ["greeting"],
      "actions": [
        {
          "send": {
            "message": { "text": "Hello!" }
          }
        }
      ]
    }
  ]
}
```

{% endtab %}
{% endtabs %}

***

### Missing Required Workflow Properties

**Error message:**

```
/workflows/0 must have required property 'intents'
/workflows/0 must have required property 'actions'
```

**Root cause:** Every workflow object MUST contain both `intents` and `actions`.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "name": "My Bot",
  "workflows": [
    {
      "name": "greeting",
      "actions": [
        {
          "send": {
            "message": { "text": "Hello!" }
          }
        }
      ]
    }
  ]
}
```

Missing: `intents`
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "name": "My Bot",
  "workflows": [
    {
      "name": "greeting",
      "intents": ["greeting"],
      "actions": [
        {
          "send": {
            "message": { "text": "Hello!" }
          }
        }
      ]
    }
  ]
}
```

{% endtab %}
{% endtabs %}

***

### Empty Message Object

**Error message:**

```
/workflows/0/actions/0/send/message must match at least one schema in anyOf
```

**Root cause:** The `message` object does not contain any of the required content fields (`text`, `media`, `carousel`, `list`, `buttons`, `quickReplies`, `richCard`, or `title`).

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "send": {
    "message": {}
  }
}
```

{% endtab %}

{% tab title="Corrected" %}

```json
{
  "send": {
    "message": {
      "text": "Welcome to Acme Support!"
    }
  }
}
```

{% endtab %}
{% endtabs %}

***

### Carousel With Fewer Than 2 Cards

**Error message:**

```
/workflows/0/actions/0/send/message/carousel must NOT have fewer than 2 items
```

**Root cause:** The `carousel` array MUST contain a minimum of 2 items. A single-card carousel is invalid.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "send": {
    "message": {
      "carousel": [
        {
          "title": "Acme Pro Runner",
          "description": "Lightweight carbon-fiber sole.",
          "media": "https://cdn.acme.com/products/pro-runner.jpg"
        }
      ]
    }
  }
}
```

Only 1 card — minimum is 2.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "send": {
    "message": {
      "carousel": [
        {
          "title": "Acme Pro Runner",
          "description": "Lightweight carbon-fiber sole.",
          "media": "https://cdn.acme.com/products/pro-runner.jpg"
        },
        {
          "title": "Acme Trail Blazer",
          "description": "All-terrain grip with waterproof membrane.",
          "media": "https://cdn.acme.com/products/trail-blazer.jpg"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

***

### Carousel With More Than 10 Cards

**Error message:**

```
/workflows/0/actions/0/send/message/carousel must NOT have more than 10 items
```

**Root cause:** The `carousel` array MUST NOT exceed 10 items.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "send": {
    "message": {
      "carousel": [
        { "title": "Product 1", "description": "Desc 1", "media": "https://cdn.acme.com/p1.jpg" },
        { "title": "Product 2", "description": "Desc 2", "media": "https://cdn.acme.com/p2.jpg" },
        { "title": "Product 3", "description": "Desc 3", "media": "https://cdn.acme.com/p3.jpg" },
        { "title": "Product 4", "description": "Desc 4", "media": "https://cdn.acme.com/p4.jpg" },
        { "title": "Product 5", "description": "Desc 5", "media": "https://cdn.acme.com/p5.jpg" },
        { "title": "Product 6", "description": "Desc 6", "media": "https://cdn.acme.com/p6.jpg" },
        { "title": "Product 7", "description": "Desc 7", "media": "https://cdn.acme.com/p7.jpg" },
        { "title": "Product 8", "description": "Desc 8", "media": "https://cdn.acme.com/p8.jpg" },
        { "title": "Product 9", "description": "Desc 9", "media": "https://cdn.acme.com/p9.jpg" },
        { "title": "Product 10", "description": "Desc 10", "media": "https://cdn.acme.com/p10.jpg" },
        { "title": "Product 11", "description": "Desc 11", "media": "https://cdn.acme.com/p11.jpg" }
      ]
    }
  }
}
```

11 cards — maximum is 10.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "send": {
    "message": {
      "carousel": [
        { "title": "Product 1", "description": "Desc 1", "media": "https://cdn.acme.com/p1.jpg" },
        { "title": "Product 2", "description": "Desc 2", "media": "https://cdn.acme.com/p2.jpg" },
        { "title": "Product 3", "description": "Desc 3", "media": "https://cdn.acme.com/p3.jpg" },
        { "title": "Product 4", "description": "Desc 4", "media": "https://cdn.acme.com/p4.jpg" },
        { "title": "Product 5", "description": "Desc 5", "media": "https://cdn.acme.com/p5.jpg" },
        { "title": "Product 6", "description": "Desc 6", "media": "https://cdn.acme.com/p6.jpg" },
        { "title": "Product 7", "description": "Desc 7", "media": "https://cdn.acme.com/p7.jpg" },
        { "title": "Product 8", "description": "Desc 8", "media": "https://cdn.acme.com/p8.jpg" },
        { "title": "Product 9", "description": "Desc 9", "media": "https://cdn.acme.com/p9.jpg" },
        { "title": "Product 10", "description": "Desc 10", "media": "https://cdn.acme.com/p10.jpg" }
      ]
    }
  }
}
```

Trimmed to 10 cards.
{% endtab %}
{% endtabs %}

***

### Button Missing Required `title` or `payload`

**Error message:**

```
/workflows/0/actions/0/send/message/buttons/0 must have required property 'title'
/workflows/0/actions/0/send/message/buttons/0 must have required property 'payload'
```

**Root cause:** Buttons of type `weburl`, `postback`, and `call` MUST include both `title` and `payload`.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "buttons": [
    {
      "type": "weburl",
      "payload": "https://shop.acme.com"
    }
  ]
}
```

Missing `title` for `weburl` button.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "buttons": [
    {
      "type": "weburl",
      "title": "Shop now",
      "payload": "https://shop.acme.com"
    }
  ]
}
```

{% endtab %}
{% endtabs %}

***

### Quick Reply Missing Required `title`

**Error message:**

```
/workflows/0/actions/0/send/message/quickReplies/0 must have required property 'title'
```

**Root cause:** Quick replies of type `text` and `postback` MUST include `title`.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "quickReplies": [
    {
      "type": "text",
      "payload": "track_order"
    }
  ]
}
```

Missing `title` for `text` quick reply.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "quickReplies": [
    {
      "type": "text",
      "title": "Track my order",
      "payload": "track_order"
    }
  ]
}
```

{% endtab %}
{% endtabs %}

***

### `waitFor` Missing Required `data` or `content`

**Error message:**

```
/workflows/0/actions/0/waitFor must have required property 'data'
/workflows/0/actions/0/waitFor must have required property 'content'
```

**Root cause:** Both `data` and `content` are required in a `waitFor` object.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "waitFor": {
    "timeout": "5m"
  }
}
```

Missing `data` and `content`.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "waitFor": {
    "data": "text",
    "content": "userInput",
    "timeout": "5m"
  }
}
```

{% endtab %}
{% endtabs %}

***

### Invalid `content` Attribute Name in `waitFor`

**Error message:**

```
/workflows/0/actions/0/waitFor/content must match pattern "^[a-zA-Z][a-zA-Z0-9_]*$"
```

**Root cause:** The `content` attribute name MUST start with a letter and contain only letters, digits, and underscores. Hyphens, dots, and leading digits are not permitted.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "waitFor": {
    "data": "text",
    "content": "user-email"
  }
}
```

`user-email` contains a hyphen — not permitted.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "waitFor": {
    "data": "text",
    "content": "userEmail"
  }
}
```

{% endtab %}
{% endtabs %}

***

### Invalid `attributePath` in `assignAttributes`

**Error message:**

```
/workflows/0/actions/0/assignAttributes/attributes/0/attributePath must match pattern "^[a-zA-Z][a-zA-Z0-9_.]*$"
```

**Root cause:** `attributePath` MUST match `^[a-zA-Z][a-zA-Z0-9_.]*$`. It may use dot notation for nested paths but MUST start with a letter.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "assignAttributes": {
    "attributes": [
      {
        "attributePath": "2ndAddress",
        "value": "123 Main St"
      }
    ]
  }
}
```

`2ndAddress` starts with a digit — not permitted.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "assignAttributes": {
    "attributes": [
      {
        "attributePath": "secondaryAddress",
        "value": "123 Main St"
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

***

### `send.request` POST Without `content`

**Error message:**

```
/workflows/0/actions/0/send/request must have required property 'content'
```

**Root cause:** When `method` is `POST`, `PUT`, or `PATCH`, the `content` property is required.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "send": {
    "request": {
      "url": "https://api.acme.com/v1/leads",
      "method": "POST",
      "dataFormat": "json",
      "headers": {
        "Authorization": "Bearer {apiToken}"
      }
    }
  }
}
```

`method` is `POST` but `content` is absent.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "send": {
    "request": {
      "url": "https://api.acme.com/v1/leads",
      "method": "POST",
      "dataFormat": "json",
      "headers": {
        "Authorization": "Bearer {apiToken}",
        "Content-Type": "application/json"
      },
      "content": {
        "phone": "{contactPhone}",
        "source": "rcs_chat"
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

***

### `name` Exceeds Maximum Length

**Error message:**

```
/ must NOT have more than 100 characters (name)
```

**Root cause:** The root `name` property has a `maxLength` of 100 characters.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "name": "Acme Corporation Complete Omnichannel Customer Support and Order Management Conversational Bot Experience Version 2",
  "workflows": []
}
```

Name is 111 characters — maximum is 100.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "name": "Acme Customer Support Bot v2",
  "workflows": []
}
```

{% endtab %}
{% endtabs %}

***

### Workflow or Action Name Uses Reserved `nm:` Prefix

**Error message:** Runtime error — the schema does not enforce this at the JSON Schema level, but the nativeMsg platform rejects names beginning with `nm:` at deploy time.

**Root cause:** Workflow and action names MUST NOT begin with the prefix `nm:`. This prefix is reserved for internal nativeMsg system identifiers.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "name": "Support Bot",
  "workflows": [
    {
      "name": "nm:greeting",
      "intents": ["greeting"],
      "actions": [
        {
          "name": "nm:send-welcome",
          "send": {
            "message": { "text": "Hello!" }
          }
        }
      ]
    }
  ]
}
```

{% endtab %}

{% tab title="Corrected" %}

```json
{
  "name": "Support Bot",
  "workflows": [
    {
      "name": "greeting",
      "intents": ["greeting"],
      "actions": [
        {
          "name": "send-welcome",
          "send": {
            "message": { "text": "Hello!" }
          }
        }
      ]
    }
  ]
}
```

{% endtab %}
{% endtabs %}

***

### RichCard With No Content Fields

**Error message:**

```
/workflows/0/actions/0/send/message/carousel/0 must match at least one schema in anyOf
```

**Root cause:** Each `RichCard` in a carousel MUST include at least one of: `title`, `description`, or `media`.

{% tabs %}
{% tab title="Invalid" %}

```json
{
  "carousel": [
    {
      "mediaHeight": "MEDIUM",
      "cardOrientation": "VERTICAL"
    },
    {
      "title": "Acme Trail Blazer",
      "description": "All-terrain grip.",
      "media": "https://cdn.acme.com/products/trail-blazer.jpg"
    }
  ]
}
```

First card has no `title`, `description`, or `media`.
{% endtab %}

{% tab title="Corrected" %}

```json
{
  "carousel": [
    {
      "title": "Acme Pro Runner",
      "description": "Lightweight carbon-fiber sole.",
      "media": "https://cdn.acme.com/products/pro-runner.jpg",
      "mediaHeight": "MEDIUM",
      "cardOrientation": "VERTICAL"
    },
    {
      "title": "Acme Trail Blazer",
      "description": "All-terrain grip.",
      "media": "https://cdn.acme.com/products/trail-blazer.jpg",
      "mediaHeight": "MEDIUM",
      "cardOrientation": "VERTICAL"
    }
  ]
}
```

{% endtab %}
{% endtabs %}

***

## Debugging Checklist

Follow these steps in order when an experience fails validation or does not behave as expected at runtime.

{% stepper %}
{% step %}

### Validate against the schema locally

Run the experience JSON through AJV (Node.js), `jsonschema` (Python), or `ajv-cli` before deploying. See [Quick Start — Schema Validation](/rcs-experience-schema/rcs-experience-schema/quick-start) for complete examples. Fix all reported errors before proceeding.
{% endstep %}

{% step %}

### Verify all required fields are present

Confirm that `name` and `workflows` exist at the root. Confirm that `intents` and `actions` exist on every workflow object. Confirm that `data` and `content` exist on every `waitFor` object. Confirm that `content` and `pattern` exist on every `validation` object.
{% endstep %}

{% step %}

### Check attribute name patterns

All attribute names used in `waitFor.content` MUST match `^[a-zA-Z][a-zA-Z0-9_]*$`. All `attributePath` values in `assignAttributes` MUST match `^[a-zA-Z][a-zA-Z0-9_.]*$`. Names starting with digits or containing hyphens are invalid.
{% endstep %}

{% step %}

### Confirm carousel lengths

Carousels MUST have 2–10 items.&#x20;
{% endstep %}

{% step %}

### Verify button and quick reply required fields

`weburl`, `postback`, and `call` buttons MUST have both `title` (max 25 chars) and `payload` (max 1000 chars). `text` and `postback` quick replies MUST have `title` (max 25 chars).
{% endstep %}

{% step %}

### Check HTTP method and `content` consistency

If `send.request.method` is `POST`, `PUT`, or `PATCH`, `content` MUST be present. If `dataFormat` is `json` and method is `POST`/`PUT`/`PATCH`, `content` MUST be a JSON object or a string starting with `{`.
{% endstep %}

{% step %}

### Confirm workflow and action names do not use the `nm:` prefix

Any name beginning with `nm:` will be rejected at deploy time even if it passes JSON Schema validation.
{% endstep %}

{% step %}

### Check cross-references

Every value in `execute`, `goto`, `welcomeMessageExecute`, `executeOnError`, `executeOnTimeout`, and `fallback` MUST match an existing workflow `name` or action `name` within the experience. Typos in these references cause silent no-ops or runtime errors.
{% endstep %}

{% step %}

### Review channel-specific constraints

If the experience targets multiple channels, ensure that rich-content actions (`carousel`, `list`, `richCard`, `buttons`, `quickReplies`) are restricted with `channel` or `channelTypes` conditions, and that text fallbacks are present for non-RCS channels.&#x20;
{% endstep %}

{% step %}

### Inspect placeholder syntax

Attribute placeholders use the form `{attributeName}`. Curly braces MUST be plain ASCII `{` and `}`. Smart quotes or Unicode variants will not be substituted. Verify that every placeholder references an attribute that is assigned before the action that reads it.
{% endstep %}

{% step %}

### Review `Validation` objects

The `pattern` field MUST be a valid ECMA-262 regular expression. Unterminated or malformed patterns cause runtime errors. Test patterns against representative values before deploying.
{% endstep %}

{% step %}

### Check for duplicate workflow names

If two workflows share the same `name`, resolution behavior is undefined. Ensure all workflow names within a single experience are unique.
{% endstep %}
{% endstepper %}


