npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xenterprises/fastify-xrcs

v1.3.0

Published

Fastify plugin for building and sending RCS rich cards and carousels via Twilio

Downloads

56

Readme

@xenterprises/fastify-xrcs

Fastify plugin for building and sending RCS rich cards and carousels via Twilio's Content API. It provides fluent builders that enforce Twilio's structural constraints (character limits, button counts, carousel rules) at build time, plus a thin API layer for registering content templates and sending messages through a Twilio Messaging Service. For teams sending rich RCS/WhatsApp content from a Fastify service.

Installation

npm install @xenterprises/fastify-xrcs fastify@5

Minimal Example

import Fastify from "fastify";
import xRCS from "@xenterprises/fastify-xrcs";

const fastify = Fastify();

await fastify.register(xRCS, {
  accountSid: "AC...",              // your Twilio Account SID
  authToken: "your-auth-token",     // your Twilio Auth Token
  messagingServiceSid: "MG...",     // your 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);

Configuration arrives only via register options — the plugin never reads process.env. Pass values from your own env handling (e.g. accountSid: process.env.TWILIO_ACCOUNT_SID).

Options

| Name | Type | Default | Required | Description | |------|------|---------|----------|-------------| | active | boolean | true | No | Enable/disable the plugin. When false, registration is a no-op | | accountSid | string | — | No | Twilio Account SID (starts with AC). Must be non-empty and provided together with authToken | | authToken | string | — | No | Twilio Auth Token. Must be non-empty and provided together with accountSid | | messagingServiceSid | string | — | No | Twilio Messaging Service SID (starts with MG). Required at send time for sendMessage/sendCard/sendCarousel |

When accountSid and authToken are omitted the plugin runs in builder-only mode — you can construct and validate card/carousel payloads locally but the Twilio API methods throw. A typical consumer wires env vars itself:

await fastify.register(xRCS, {
  accountSid: process.env.TWILIO_ACCOUNT_SID,
  authToken: process.env.TWILIO_AUTH_TOKEN,
  messagingServiceSid: process.env.TWILIO_MESSAGING_SERVICE_SID,
});

Decorators

All methods and builders are exposed on the single fastify.xrcs namespace.

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 + ...) — never the full SID | | config.messagingServiceSid | string\|null | Messaging Service SID |

Classes & Constants (namespace properties and named exports)

| 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) |

The package's default export is the Fastify plugin. Named exports: CONTENT_TYPES, ACTION_TYPES, MEDIA_HEIGHT, CardBuilder, CarouselBuilder, ContentTemplateBuilder, and buildRcsApi(twilioClient, messagingServiceSid) (the API factory, exported so tests can inject a fake client).

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 card

Carousel 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 |

Routes

None. This plugin adds no routes and no hooks — only the fastify.xrcs decorator.

Error Behavior

Registration fails fast with actionable messages that name the plugin, the option, and an example:

| Error | When | |-------|------| | xrcs: option `active` must be a boolean, e.g. `app.register(xRCS, { active: false })` | Non-boolean active option | | xrcs: option `accountSid` must be a string, e.g. ... | Non-string accountSid option | | xrcs: option `accountSid` must be a non-empty string, e.g. ... | Empty or whitespace-only accountSid option | | xrcs: option `authToken` must be a string, e.g. ... | Non-string authToken option | | xrcs: option `authToken` must be a non-empty string, e.g. ... | Empty or whitespace-only authToken option | | xrcs: option `messagingServiceSid` must be a string, e.g. ... | Non-string messagingServiceSid option | | xrcs: options `accountSid` and `authToken` must be provided together, e.g. ... | Only one of the pair is provided |

Runtime and builder errors are prefixed with [xRCS]:

| Error | When | |-------|------| | [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 |

Twilio API errors are wrapped in a new Error carrying only the original err.message — raw SDK error objects (which may contain request details) are never rethrown or logged.

How It Works

The plugin wraps Twilio's Content API with a fluent builder pattern and a thin Fastify integration layer:

  1. Registration — the plugin validates options, initialises one Twilio client (reused for all calls; never per-request), and decorates the Fastify instance with fastify.xrcs.
  2. Builders — CardBuilder, CarouselBuilder, and ContentTemplateBuilder are 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.
  3. Template creation — createTemplate() accepts a builder or a raw payload object. With register=true (default) it calls twilioClient.content.v1.contents.create() to register the template with Twilio and returns the SID.
  4. Sending — sendMessage() sends a message via twilioClient.messages.create() using a registered template SID and the configured Messaging Service. sendCard() and sendCarousel() are convenience wrappers that create a template and send it in one call.
  5. Builder-only mode — when credentials are omitted the plugin still registers all builders, constants, and validation helpers. Only the API methods throw.

Requirements

  • Node.js >= 20
  • Fastify >= 5 (peer dependency)
  • Twilio account with RCS-capable Messaging Service (for API mode)

License

Proprietary — All Rights Reserved X Enterprises. See LICENSE.