@xenterprises/fastify-xrcs
v1.2.1
Published
Fastify plugin for building and sending RCS rich cards and carousels via Twilio
Downloads
62
Readme
@xenterprises/fastify-xrcs
Fastify plugin for building and sending RCS rich cards and carousels via Twilio's Content API.
Installation
npm install @xenterprises/fastify-xrcsQuick Start
import Fastify from "fastify";
import xRCS from "@xenterprises/fastify-xrcs";
const fastify = Fastify();
await fastify.register(xRCS, {
accountSid: process.env.TWILIO_ACCOUNT_SID,
authToken: process.env.TWILIO_AUTH_TOKEN,
messagingServiceSid: process.env.TWILIO_MESSAGING_SERVICE_SID,
});
// Build and send a product card
const card = fastify.xrcs
.card()
.title("iPhone 15 Pro")
.body("Starting at $999")
.media("https://example.com/iphone.jpg")
.quickReply("Buy Now", "buy_iphone")
.urlButton("Learn More", "https://example.com/iphone");
await fastify.xrcs.sendCard("+15551234567", card);Options
| Name | Type | Default | Required | Description |
|------|------|---------|----------|-------------|
| active | boolean | true | No | Enable/disable the plugin |
| accountSid | string | — | No | Twilio Account SID. Must be provided with authToken |
| authToken | string | — | No | Twilio Auth Token. Must be provided with accountSid |
| messagingServiceSid | string | — | No | Twilio Messaging Service SID. Required for sending messages |
When accountSid and authToken are omitted the plugin runs in builder-only mode — you can construct card/carousel payloads locally but cannot call the Twilio API.
Environment Variables
| Name | Required | Description |
|------|----------|-------------|
| TWILIO_ACCOUNT_SID | Yes (for API mode) | Twilio Account SID (starts with AC) |
| TWILIO_AUTH_TOKEN | Yes (for API mode) | Twilio Auth Token |
| TWILIO_MESSAGING_SERVICE_SID | Yes (for sending) | Twilio Messaging Service SID (starts with MG) |
Decorated Properties
All methods and builders are available on fastify.xrcs.
Builder Factory Methods
| Method | Returns | Description |
|--------|---------|-------------|
| fastify.xrcs.card() | CardBuilder | Create a new card builder |
| fastify.xrcs.carousel() | CarouselBuilder | Create a new carousel builder |
| fastify.xrcs.template(name) | ContentTemplateBuilder | Create a new content template builder |
API Methods (require credentials)
| Method | Returns | Description |
|--------|---------|-------------|
| createTemplate(builder, register?) | Promise<object> | Create and optionally register a content template. Pass register=false to get the payload without calling Twilio |
| sendMessage(to, contentSid, vars?) | Promise<object> | Send a message using a registered template SID |
| sendCard(to, card, name?) | Promise<object> | Create a template from a card and send it in one step |
| sendCarousel(to, carousel, name?) | Promise<object> | Create a template from a carousel and send it in one step |
| getTemplate(contentSid) | Promise<object> | Fetch a template by SID |
| listTemplates(limit?) | Promise<object[]> | List all content templates (default limit: 50) |
| deleteTemplate(contentSid) | Promise<boolean> | Delete a content template |
| getMessageStatus(messageSid) | Promise<object> | Get delivery status for a sent message |
Validation Helpers
| Method | Returns | Description |
|--------|---------|-------------|
| validate.card(card) | { valid, errors } | Validate a card object against RCS constraints |
| validate.carousel(carousel) | { valid, errors } | Validate a carousel object against RCS constraints |
Configuration
| Property | Type | Description |
|----------|------|-------------|
| config.hasCredentials | boolean | Whether Twilio credentials are configured |
| config.accountSid | string\|null | Masked account SID (first 8 chars) |
| config.messagingServiceSid | string\|null | Messaging Service SID |
Classes & Constants
| Export | Description |
|--------|-------------|
| CardBuilder | Class for building RCS card structures |
| CarouselBuilder | Class for building RCS carousel structures |
| ContentTemplateBuilder | Class for building Twilio Content API payloads |
| CONTENT_TYPES | Enum of supported content types (TEXT, MEDIA, CARD, CAROUSEL, QUICK_REPLY, CALL_TO_ACTION, LIST_PICKER) |
| ACTION_TYPES | Enum of button action types (QUICK_REPLY, URL, PHONE_NUMBER) |
| MEDIA_HEIGHT | Enum of media height options (SHORT, MEDIUM, TALL) |
Card Builder
const card = fastify.xrcs
.card()
.title("Product Name") // Max 200 chars
.body("Product description")
.media("https://example.com/image.jpg", "tall") // short | medium | tall
.quickReply("Buy", "action_buy") // Quick reply button (max 25 chars)
.urlButton("Details", "https://example.com/product") // URL button
.phoneButton("Call", "+15551234567"); // Phone button
const cardData = card.build(); // Max 2 buttons per cardCarousel Builder
const carousel = fastify.xrcs
.carousel()
.body("Choose a product") // RCS drops body text; WhatsApp shows it
.addCard(fastify.xrcs.card().title("Laptop").body("$999").quickReply("Buy", "buy_laptop"))
.addCard(fastify.xrcs.card().title("Tablet").body("$499").quickReply("Buy", "buy_tablet"));
await fastify.xrcs.sendCarousel("+15551234567", carousel);Constraints: 2–10 cards, button types must be in the same order across all cards.
Content Template Builder
Build complete Twilio Content API payloads for any content type:
// Text with variables
const template = fastify.xrcs
.template("Welcome")
.language("en")
.variables({ "1": "John" })
.text("Hello {{1}}, welcome!")
.build();
// Register with Twilio
const registered = await fastify.xrcs.createTemplate(template);
// Or get payload without registering
const payload = await fastify.xrcs.createTemplate(
fastify.xrcs.template("Preview").text("Hello"),
false
);Supported Content Types
| Type | Constant | RCS | WhatsApp |
|------|----------|-----|----------|
| Text | CONTENT_TYPES.TEXT | Yes | Yes |
| Media | CONTENT_TYPES.MEDIA | Yes | Yes |
| Card | CONTENT_TYPES.CARD | Yes | Yes |
| Carousel | CONTENT_TYPES.CAROUSEL | Yes | Yes |
| Quick Reply | CONTENT_TYPES.QUICK_REPLY | Yes | Yes |
| Call to Action | CONTENT_TYPES.CALL_TO_ACTION | No | Yes |
| List Picker | CONTENT_TYPES.LIST_PICKER | No | Yes |
Error Reference
All errors are prefixed with [xRCS].
| Error | When |
|-------|------|
| [xRCS] accountSid must be a string | Non-string accountSid option at startup |
| [xRCS] authToken must be a string | Non-string authToken option at startup |
| [xRCS] messagingServiceSid must be a string | Non-string messagingServiceSid option at startup |
| [xRCS] Both accountSid and authToken must be provided together | Only one of the pair is provided |
| [xRCS] Twilio credentials required to register templates | Calling createTemplate without credentials |
| [xRCS] Twilio credentials required to send messages | Calling sendMessage without credentials |
| [xRCS] Twilio credentials required | Calling any read API without credentials |
| [xRCS] messagingServiceSid required to send messages | Sending without messagingServiceSid |
| [xRCS] 'to' must be a non-empty string | Empty/missing recipient |
| [xRCS] 'contentSid' must be a non-empty string | Empty/missing template SID |
| [xRCS] 'messageSid' must be a non-empty string | Empty/missing message SID |
| [xRCS] 'limit' must be a positive number | Invalid limit for listTemplates |
| [xRCS] Card title must be 200 characters or less | Title exceeds 200 chars |
| [xRCS] Button title must be 25 characters or less | Button label exceeds 25 chars |
| [xRCS] Cards can have a maximum of 2 buttons | More than 2 actions on a card |
| [xRCS] Carousel must have at least 2 cards | Fewer than 2 cards |
| [xRCS] Carousel can have a maximum of 10 cards | More than 10 cards |
| [xRCS] Button types must be in the same order across all carousel cards | Inconsistent action types |
| [xRCS] Content template must have at least one content type | Empty template |
| [xRCS] Failed to create template: ... | Twilio API error on create |
| [xRCS] Failed to send message: ... | Twilio API error on send |
| [xRCS] Failed to get template '...': ... | Twilio API error on fetch |
| [xRCS] Failed to list templates: ... | Twilio API error on list |
| [xRCS] Failed to delete template '...': ... | Twilio API error on delete |
| [xRCS] Failed to get message status '...': ... | Twilio API error on status check |
How It Works
The plugin wraps Twilio's Content API with a fluent builder pattern and a thin Fastify integration layer:
- Registration — the plugin validates options, initialises a Twilio client (if credentials are supplied), and decorates the Fastify instance with
fastify.xrcs. - Builders —
CardBuilder,CarouselBuilder, andContentTemplateBuilderare plain classes with no network dependencies. They enforce Twilio's structural constraints (character limits, button counts, carousel card counts, button-order consistency) at build time so you get immediate validation errors. - Template creation —
createTemplate()accepts a builder or a raw payload object. Withregister=true(default) it callstwilioClient.content.v1.contents.create()to register the template with Twilio and returns the SID. - Sending —
sendMessage()sends a message viatwilioClient.messages.create()using a registered template SID and the configured Messaging Service.sendCard()andsendCarousel()are convenience wrappers that create a template and send it in one call. - Builder-only mode — when credentials are omitted the plugin still registers all builders, constants, and validation helpers. Only the API methods throw.
License
UNLICENSED
