Skip to content

RCL Output Specification

The RCL compiler transforms RCL files into two output formats: JSON and JavaScript/TypeScript modules. This document defines the expected structure and schema for these outputs based on the actual implementation.

All output must validate against: https://rcl.rcsagents.io/schemas/rcl-output.schema.json

The JSON output contains three main sections:

{
"$schema": "https://rcl.rcsagents.io/schemas/rcl-output.schema.json",
"agent": {
"name": "BMWAgent",
"displayName": "BMW Virtual Assistant",
"start": "MainFlow",
"config": {
"description": "Your personal BMW assistant",
"logoUri": {
"type": "url",
"value": "https://example.com/logo.png"
},
"color": "#0066CC",
"phoneNumber": {
"type": "phone",
"value": "+1-555-BMW-HELP"
},
"phoneLabel": "Call BMW Support"
},
"defaults": {
"fallbackMessage": "I didn't understand that. Please choose from the available options.",
"messageTrafficType": "PROMOTION"
}
},
"flows": {
"MainFlow": {
"$schema": "https://rcl.rcsagents.io/schemas/csm/machine-definition.schema.json",
"id": "MainFlow",
"initial": "Welcome",
"states": {
"Welcome": {
"transitions": [
{
"pattern": "Order Coffee",
"target": "OrderCoffee",
"context": { "action": "order" }
},
{
"pattern": ":default",
"target": "Welcome"
}
],
"meta": {
"messageId": "Welcome"
}
}
}
}
},
"messages": {
"Welcome": {
"type": "richCard",
"title": "Welcome to Coffee Shop",
"description": "How can we help you today?",
"media": {
"type": "url",
"value": "https://example.com/welcome.jpg"
},
"suggestions": [
{
"type": "reply",
"text": "Order Coffee",
"postbackData": "order_coffee"
},
{
"type": "reply",
"text": "View Menu",
"postbackData": "view_menu"
}
],
"messageTrafficType": "PROMOTION"
}
}
}

The JavaScript output is an ES module with named exports:

// Generated from filename.rcl
export const agent = {
"name": "CoffeeShop",
"displayName": "Quick Coffee",
// ... agent configuration
};
export const messages = {
"Welcome": {
"type": "richCard",
"title": "Welcome",
// ... message definition
}
// ... other messages
};
export const flows = {
"MainFlow": {
"id": "MainFlow",
"initial": "Welcome",
"states": {
// ... state definitions
}
}
// ... other flows
};
// Utility functions
export function getMessage(messageId) {
return messages[messageId];
}
export function getFlow(flowId) {
return flows[flowId];
}
export function createMachine(flowId, options = {}) {
// Create state machine instance
}
// Default export
export default { messages, flows, agent, getMessage, getFlow, createMachine };

Must conform to the agentOutput definition in rcl-output.schema.json

Structure:

{
"agent": {
"name": "string (required from RCL agent declaration)",
"displayName": "string (required, max 100 chars)",
"brandName": "string (optional)",
"config": {
"description": "string",
"logoUri": { "type": "url", "value": "https://..." },
"heroUri": { "type": "url", "value": "https://..." },
"color": "#RRGGBB (hex color)",
"phoneNumber": { "type": "phone", "value": "+..." },
"phoneLabel": "string",
"email": { "type": "email", "value": "user@domain.com" },
"emailLabel": "string",
"website": { "type": "url", "value": "https://..." },
"websiteLabel": "string",
"address": "string",
"openingHours": "string",
"agentUseCase": "TRANSACTIONAL|PROMOTIONAL|OTP|MULTI_USE",
"hostingRegion": "NORTH_AMERICA|EUROPE|ASIA_PACIFIC"
},
"defaults": {
"messageTrafficType": "PROMOTION|TRANSACTION|AUTHENTICATION|SERVICEREQUEST|ACKNOWLEDGEMENT",
"fallbackMessage": "string",
"postbackData": "string (JS expression template)"
}
}
}

Must conform to the messageOutput definition in rcl-output.schema.json

Types and Structure:

{
"type": "text",
"text": "Message content (max 2048 chars)",
"suggestions": [ /* max 11 suggestions */ ],
"messageTrafficType": "PROMOTION",
"ttl": "3600s"
}
{
"type": "richCard",
"title": "Card Title (max 200 chars)",
"description": "Card description (max 2000 chars)",
"size": "small|medium|large|compact",
"media": {
"type": "url",
"value": "https://example.com/image.jpg"
},
"suggestions": [ /* max 11 suggestions */ ],
"messageTrafficType": "PROMOTION"
}
{
"type": "carousel",
"title": "Carousel Title",
"size": "medium",
"cards": [
{
"title": "Card 1",
"description": "First card",
"suggestions": [ /* max 4 suggestions per card */ ]
},
{
"title": "Card 2",
"description": "Second card",
"suggestions": [ /* max 4 suggestions per card */ ]
}
]
}
{
"suggestions": [
{
"type": "reply",
"text": "Button Text (max 25 chars)",
"postbackData": "callback_value (max 2048 chars)"
},
{
"type": "action",
"text": "Call Us",
"postbackData": "action_call",
"phoneNumber": "+1234567890"
},
{
"type": "action",
"text": "Visit Website",
"postbackData": "action_url",
"url": "https://example.com"
}
]
}

Must conform to CSM Machine Definition Schema

Structure:

{
"flows": {
"FlowId": {
"id": "FlowId",
"initial": "InitialStateName",
"states": {
"StateName": {
"transitions": [
{
"pattern": "user_input_pattern",
"target": "NextStateName",
"priority": 10,
"context": {
"key": "value"
}
},
{
"pattern": ":default",
"target": "FallbackState"
}
],
"meta": {
"messageId": "MessageToShow",
"transient": false
}
}
},
"meta": {
"name": "Flow Display Name",
"description": "What this flow does"
}
}
}
}

Key Concepts:

  • States: Represent conversation steps
  • Transitions: Define how to move between states based on user input
  • Patterns: Match against user input (postbackData from suggestions)
  • Context: Variables passed between states
  • Priority: Higher numbers checked first (auto-assigned by compiler)
  • :default: Fallback transition when no patterns match

RCL type tags are converted to typed objects:

<url https://example.com> → {"type": "url", "value": "https://example.com"}
<phone +1234567890> → {"type": "phone", "value": "+1234567890"}
<email user@domain.com> → {"type": "email", "value": "user@domain.com"}
<money 19.99> → {"type": "money", "value": "19.99", "currency": "USD"}
<datetime 2024-07-26> → {"type": "datetime", "value": "2024-07-26T00:00:00Z"}
<duration P1D> → {"type": "duration", "value": "P1D"}

When suggestions don't specify postbackData, it's auto-generated:

Rules:

  1. Convert to lowercase
  2. Replace non-alphanumeric with underscores
  3. Collapse multiple underscores to single
  4. Trim leading/trailing underscores

Examples:

  • "Order Coffee""order_coffee"
  • "Small ($3.50)""small_3_50"
  • "Yes, please!""yes_please"
  • "BMW M3 Series""bmw_m3_series"

Valid values (from schema):

  • MESSAGE_TRAFFIC_TYPE_UNSPECIFIED
  • AUTHENTICATION
  • TRANSACTION
  • PROMOTION (default)
  • SERVICEREQUEST
  • ACKNOWLEDGEMENT

Valid values (from schema):

  • AGENT_USE_CASE_UNSPECIFIED
  • TRANSACTIONAL
  • PROMOTIONAL
  • OTP
  • MULTI_USE

Valid values (from schema):

  • HOSTING_REGION_UNSPECIFIED
  • NORTH_AMERICA
  • EUROPE
  • ASIA_PACIFIC

The compiler validates output against these schemas:

  1. rcl-output.schema.json - Complete output structure
  2. csm/machine-definition.schema.json - Individual flows
  3. Internal validation - Cross-references, state transitions, message references

Validation Tools:

Terminal window
# Using ajv-cli
npx ajv validate -s https://rcl.rcsagents.io/schemas/rcl-output.schema.json -d output.json
# Using jsonschema (Python)
jsonschema -i output.json https://rcl.rcsagents.io/schemas/rcl-output.schema.json
  • Flat Structure: Output uses simplified, flat message structure (not nested RCS API format)
  • CSM Format: Flows use Conversation State Machine format, not XState
  • Auto-Generation: Many fields auto-generated from RCL (postbackData, priorities, etc.)
  • Validation: All output validated against schemas before generation
  • Type Safety: TypeScript definitions available matching schemas

If updating from older documentation expecting nested RCS format:

Old (Incorrect):

{
"agent": {
"rcsBusinessMessagingAgent": { /* nested config */ }
},
"messages": {
"Welcome": {
"contentMessage": {
"text": "...",
"suggestions": [...]
}
}
}
}

New (Correct):

{
"agent": {
"config": { /* flat config */ },
"defaults": { /* flat defaults */ }
},
"messages": {
"Welcome": {
"type": "text",
"text": "...",
"suggestions": [...]
}
}
}