node package manager


Version Dependency Status Build Status


SMS framework with pluggable providers.

Key features:

  • Send messages
  • Receive messages
  • Delivery confirmations
  • Handle multiple providers with a single gateway
  • Event-driven interface
  • Reliable message handling
  • Leverages q promises
  • Unit-tested

Table of Contents

Supported Providers

SMSframework supports the following

Also see the full list of providers.


SMSframework handles the whole messaging thing with a single Gateway object.

Let's start with initializing a gateway:

var smsframework = require('smsframework')
var gateway = new smsframework.Gateway();

The Gateway() constructor currently has no arguments.


A Provider is a package which implements the logic for a specific SMS provider.

Each provider reside in an individual package smsframework-*. You'll probably want to install some of these first.

Gateway.addProvider(provider, alias, config)


  • provider: String is the name of the provider package.
  • alias: String is the provider alias: an arbitrary string that uniquely identifies the provider instance. You'll use this string in order to send messages via a specific provider.
  • config: Object is the Provider-dependent configuration object. Refer to the provider documentation for the details.

When a package is require()d, it registers itself under the smsframework.providers namespace. You don't need to require providers manually, as SMSframework does this for you.

gateway.addProvider('provider1', 'alias1', {}); // package 'smsframework-provider1' 
gateway.addProvider('provider2', 'alias2', {}); // package 'smsframework-provider2' 

The first provider becomes the default one, unless you use Message Routing.


An alternative syntax to add providers in bulk.


  • providers: Array.<{ provider: String, alias: String, config: Object? }> is an array that specifies multiple providers:

        { provider: 'provider1', alias: 'primary', config: {} },
        { provider: 'provider2', alias: 'secondary', config: {} }

This works perfectly when your providers are defined in the configuration. Consider using js-yaml:

    { provider: 'provider1', alias: 'primary', config: { apitoken: '123456' } }
    { provider: 'provider2', alias: 'secondary', config: { apitoken: '987654' } }

Then, in the application:

var config = yaml.load(fs.readFileSync('./config.yml', { encoding: 'utf8' }));


Get a provider instance by its alias.

You don't normally need this, unless the provider has some public API: see provider documentation.

Sending Messages

To send a message, you first create it with Gateway.message(to, body) which returns a fluid interface object.


  • to: String: Recipient number
  • body: String: Message body


  • message: OutgoingMessage: The wrapped Outgoing Message object


  • send():Q: Send the message.

    The method returns a promise for OutgoingMessage which is resolved when a message is sent, or rejected on sending error. See Handling Send Errors.

  • from(from: String): Set the message originating number.

    Is used to pick a specific source number if the gateway supports that.

  • provider(provider: String): Choose the provider by alias.

    If no provider is specified - the first one is used to deliver the message.

  • route(..values): Specify routing values. See Message Routing.

  • options(options: OutgoingMessageOptions): Specify sending options:

    • allow_reply: Boolean: Allow replies for this message. Default: false

    • status_report: Boolean: Request a delivery report. Default: false. See: status

    • expires: Number?: Message validity period in minutes. Default: none.

    • senderId: String?: SenderID to replace the source number. Default: none.

      NOTE: This advanced feature is not supported by all providers! Moreover, some of them can have special restrictions.

    • escalate: Boolean: Is a high-priority message: these are delivered faster, at a higher price. Default: false.

  • params(params: Object): Specify provider-dependent sending parameters: refer to the provider documentation for the details.

All the above methods are optional, you can just send the message as is:

gateway.message('+123456', 'hi there').send().done(); // using the default provider 

Here's the full example:

var smsframework = require('smsframework')
var gateway = new smsframework.Gateway();
gateway.addProvider('clickatell', 'primary', {});
gateway.message('+123456', 'hi there')
    .provider('primary') // use the named provider 
        allow_reply: true,
        status_report: false,
        expires: 60,
        senderId: 'smsframework'
        // some provider-dependent parameters here 
    // Handle success 
        console.log('Message sent successfully!');
        console.error('Failed to send the message', err.stack);

If you dislike promises, you can always get back to the old good NodeJS-style callbacks:

gateway.message('+123456', 'hi there').send()
    .nodeify(function(err, message){
        // NodeJS callback 

Handling Send Errors

When you send() a message, the promise may resolve to an error.

The error object is provided as an argument to the callback function, and can be one of the following:

  • Error: unknown provider specified. You may have a typo, or the provider package is missing.
  • Error: runtime error occurred somewhere in the code. Rare.
  • smsframework.errors.SendMessageError: An advanced error object. Has the code field which defines the error conditions.

See smsframework.errors.SendMessageError for the list of supported error codes.


gateway.message('+123456', 'hi there').send()
        if (err.code ===
            console.error('Not enough funds:', err);


The Gateway object is an EventEmitter which fires the following events:


Outgoing Message: a message is being sent.


NOTE: It is not yet known whether the message was accepted by the Provider or not. Also, the msgid and info fields are probably not populated.


Outgoing Message: a message that was successfully sent.


The message object is populated with the additional information from the provider, namely, the msgid and info fields.


Incoming Message: a message that was received from the provider.



Message Status: a message status reported by the provider.

A status report is only delivered when explicitly requested with options({ status_report: true }).



Error object reported by the provider.


  • error: Error|SendMessageError: The error object. See Error Objects.

Useful to attach some centralized logging utility. Consider winston for this purpose.

Functional Handlers

Events are handy, unless you need more reliability: if an event-handler fails to process the message, the Provider still sends 'OK' to the SMS service.. and the message is lost forever.

Functional handlers solve this problem: you register a callback function that returns a promise, and in case the promise is rejected - the provider reports an error to the SMS provider so it retries the delivery later.


// Handler for incoming messages 
    return Q.nmcall(db, 'save', message);
// Handler for incoming status reports 
    return Q.nmcall(db, 'update', status.msgid);

Whenever any of the handlers fail - the Provider reports an error to the SMS service, so the data is re-sent later.


Subscribe a callback to Incoming Messages. Can be called multiple times.


  • callback: function(IncomingMessage):Q: A callback that processes an Incoming Message. If it returns a rejection - the Provider reports an error to the SMS service.


Subscribe a callback to Message Statuses. Can be called multiple times.


  • callback: function(IncomingMessage):Q: A callback that processes a Message Status. If it returns a rejection - the Provider reports an error to the SMS service.

Data Objects

SMSframework uses the following objects to represent message flows.


A messsage received from the provider.

Source: lib/data/IncomingMessage.js.


A message being sent.

Source: lib/data/OutgoingMessage.js.


A status report received from the provider.

Source: lib/data/MessageStatus.js.

Error Objects

Source: lib/data/MessageStatus.js.

Provider HTTP Receivers

The Gateway has an internal express application, which is used by all providers to register their receivers: HTTP endpoints used to interact with the SMS services.

Each provider is locked under the /<alias> prefix.

The resources are provider-dependent: refer to the provider documentation for the details. The recommended approach is to use /im for incoming messages, and /status for status reports.

To use the receivers in your application, use method which returns an express middleware:

var smsframework = require('smsframework'),
    express = require('express')
// Gateway 
var gateway = new smsframework.Gateway();
gateway.addProvider('clickatell', 'primary', {}); // provider, alias 'primary' 
// Init express 
var app = express();
app.use('/sms',; // mount SMSframework middleware under /sms 
app.listen(80); // start 
// Ready to receive messages 
gateway.on('msg-in', function(message){
    console.log('SMS from ' + message.from + '' + message.body);

Assuming that the provider declares a receiver as '/receiver', we now have a 'http://localhost:80/sms/primary/receiver' path available.

In your Clickatell admin area, add this URL so Clickatell passes the incoming messages to us.


Sugar to start listening on the specified network address immediately.

Footprints (see net.Server):

  • `Gateway.listen(port[, host])
  • `Gateway.listen(path)
  • `Gateway.listen(handle)

Returns: a promise for the http.Server object.


    console.log('Listening on ', server.address()); // Get the address 
    server.close(); // stop listening 

Receiver Security

It's adviced to mount the receivers under some difficult-to-guess path: otherwise, attackers can send fake messages into your system

Secure example:


NOTE: Other mechanisms, such as basic authentication, are not typically useful as some services do not support that.

Message Routing

SMSframework requires you to explicitly specify the provider for each message, or uses the first one.

In real world conditions with multiple providers, you may want a router function that decides on which provider to use and which options to pick.

In order to achive flexible message routing, we need to associate some metadata with each message, for instance:

  • module: name of the sending module: e.g. "users"
  • type: type of the message: e.g. "notification"

These 2 arbitrary strings need to be standardized in the application code, thus offering the possibility to define complex routing rules.

When creating the message, use route() function to specify these values:

gateway.message('+1234', 'hi')
    .route('users', 'notification')

Now, set a router function: a function which gets an outgoing message + some additional routing values, and decides on the provider to use:

gateway.addProvider('clickatell', 'primary', {});
gateway.addProvider('clickatell', 'secondary', {});
gateway.addProvider('clickatell', 'usa', {});
gateway.setRouter(function(message, module, type){
    // Use 'usa' for all messages to USA 
    if (/^+1/.test(
        return 'usa';
    // Use 'secondary' for notifications 
    if (type === 'notification')
        return 'secondary';
    // Use 'primary' as a default 
    return 'primary';

Router function is also the right place to specify message options & parameters.

To unset the router function, call gateway.setRouter() with no arguments.

Bundled Providers

The following providers are bundled with SMSframework and thus require no additional packages.


Source: lib/providers/null.js

The 'null' provider just ignores all outgoing messages.


gw.addProvider('null', 'null'); // provider, alias 


Source: lib/providers/log.js

The 'log' provider just logs all outgoing messages without sending them anywhere.


  • log: function(OutgoingMessage): The logger function. Defalt: log message destination & text to the console.


gw.addProvider('log', 'log', {
    log: function(message){
        console.log('SMS to',, ':', message.body);


Source: lib/providers/loopback.js

The 'loopback' provider is used as a dummy for testing purposes.

It consumes all messages, supports delivery notifications, and even has a '/im' HTTP receiver.

All messages flowing through it get incremental msgids starting from 1.


LoopbackProvider stores all messages that go through it. To get those messages, call .getTraffic().

This method empties the message log.

gateway.addProvider('loopback', 'lo', {});
gateway.message('+123', 'hi').send()
        gateway.getProvider('lo').getTraffic(); // array of messages 

LoopbackProvider.receive(from, body):Q

Simulate an incoming message.


  • from: String: Source number
  • body: String: Message text

Returns: a promise for a message processed by SMSframework

gateway.on('msg-in', function(message){
    console.log(message.from, ':', message.body);
gateway.getProvider('lo').receive('+1111', 'notification!');

LoopbackProvider.subscribe(sim, callback)

Register a virtual subscriber which receives messages to the matching number.


  • sim: String: Subscriber phone number

  • callback: function(from: String, body: String, reply:function(String):Q): A callback which gets the messages sent to this subscriber.

    The last argument is a convenience function to send a reply. It wraps LoopbackProvider.receive().

gateway.getProvider('lo').subscribe('+123456', function(from, body, reply){
    reply('Hi '+from+'!');
gateway.message('+123456', 'hi').send();