high scale applications

Plans and pricing

$25/month + messaging and numbers

Pay per message, in and out, and $1 per virtual number. Includes SSL support. US and Canada only, for now.

$250/month + messaging and numbers

Messaging at Carousel plan terms. Includes phone and SMS-based support with technical staff, account representative.

Ask about custom integration, white label and custom-tailored plans.

or cancel

How will I be charged?

At the beginning of your monthly billing cycle, we charge you a recurring monthly fee against the credit card we have securely on file. Message usage is charged against your account, which you can recharge automatically.

Any virtual numbers (DIDs) you purchase are automatically renewed monthly. You're charged for those on initial purchase and every month until you cancel them.

What customization do you offer?

If you contact us, we can discuss everything from integration, to using your own carrier connections for messaging via our platform. We can work with you on a custom application on our API. And we may even be able to offer specialized API calls.

Are there any restrictions on my messaging?

The only restrictions are in abiding with US regulations on SMS use. That means your recipients should be opted in, and that your content follows our terms of service. SPAM is the biggest red flag.

Is my data secure?

All financial transactions are encrypted, and your payment details stored securely with our merchant. Your messaging data is secured on our servers and access is limited to troubleshooting. Once your messages hit the carriers, your data is protected to the extent that the carriers themselves allow. For more on privacy, see our privacy policy and terms of service.

Are there any limits to my messaging volume?

Because we use a prepaid system with automated recharges for messaging, we don't limit your usage or require any setup or pre-screening.

Am I eligible for any discounts?

If there's sufficient interest, we can offer a discount on annual versions of our monthly plans. Our Enterprise and custom plans offer invoicing options.

Service Layer API

The Carousel Service Layer (SL) API

The Carousel API helps you send text messages at scale. We handle the heavy lifting via our HTTP API and administrative console.


Use as the base address for working with the API.

  • Format of request: HTTP POST to endpoint with URL encoded parameters.
  • Format of response: JSON


Here are a couple of simple cases, via curl. First, send a single message:

curl ''


Then, send a 'mail merge' style message to multiple recipients:

curl ',%20%7B%7Bname%7D%7D&recipients=%7B%2213472643707%22:%7B%22name%22:%22Alex%22%7D,%221234567890%22:%7B%22name%22:%22Unknown%22%7D%7D'


API key

Every client application has an API key associated with it. This key is generated during the creation of the application and can be re-generated from the show/edit screens in the administrative console.

This key uniquely identifies an application during the calls it makes to the SL.


Set up a gateway URL to receive callbacks and incoming messages.


We offer modules to help automate common tasks, like setting up automated help information and welcome messages for first-time users.

These are optional.

Data types

Parameters that you specify in calls to the API and receive as callbacks from the API can be of different types.


Regular text string.


Integer number, either positive or negative.


We are quite flexible with booleans. They are case-insensitive and can take the following shapes:

  • TRUE: 1, t, true, y, yes
  • FALSE: 0, f, false, n, no

Dates and times

Dates and times parsing is very liberal in the SL. We are capable of parsing almost everything that can be interpreted as a date/time.

Here are some examples of what the dates can look like:

  • 16:30 - takes current date as basis and uses provided time
  • Aug 31 – take current year, users the given date and sets time to 00:00
  • 7/31 – the same as above
  • 2/9/2007 or 2007-02-09 – date only
  • 02-09-2007 12:30:44 AM or 2007-09-02T00:30:44Z - UTC (GMT) time
  • 02-09-2007 12:30:44 PM EST or 2007-09-02T12:30:44-0500 - localized time
  • Wednesday, January 10, 2001 - human-readable date

If you need a stricter definition, you can refer to RFC2822 (3.3. Date and Time Specification).


Handset, Phone

an end-user's mobile phone.

MT (Mobile Terminated)

Outgoing message from our system to a handset.

MO (Mobile Originated)

Incoming message from a handset to our system.

DID (Direct Inward Dialing), Virtual Number, Long Code

These are local, in and outbound phone numbers. In the US, they are standard, 10-digit phone numbers.

SMSC (Short Message Service Center)

The layer in the stack which stores and delivers text messages. In our case, we use internal SMSCs which can be made up of one DID or many. Multiple DIDs allow for threaded conversations and higher throughput.

The Service Layer uses various modules to save you the trouble of developing common pieces SMS business logic.


Modules let developers to add more standard processing options to the Service Layer. Modules are called whenever an incoming message arrives to the application. The modules are expected to return either nil if they don't know how to handle the message, or a hash with the following fields:

  • body -- string -- the response message to return to the cell phone.
    • empty -- no response
  • free -- string -- TRUE to send the response through the FTEU gateway
    • This is used only in cases where FTEU messaging is available. Please contact us if interested.

Modules are organized in a chain, and are processed in sequence.

Modules Modules_inverse

If the first module returns a result, the second listed isn't called nor instantiated.

One example of a module is in subscription processing. Given that many applications share this functionality, we extract the processing of keywords SUB, Y, STOP in a module and place it before the Client application call module. Then, if one of these keywords arrives, we can process them on the Service Layer instead of sending the request to your application.

An application can use any number of modules.

Available modules

If you'd like to automatically reply to inbound "help" messages, use our Help module.

To send a one-time welcome message to first-time users, try our Welcome module.

Help module

The Help module listens for help-related keywords sent to an application. When possible, we send replies as Free To End User (FTEU) messages.

The module doesn't do anything besides that. It delegates the processing to your application, and wraps the response in the proper structure.

Welcome module

The welcome module is intended to send "Welcome" responses to first-time users of either SMSCs or applications.

How it works

This module works on two levels: SMSCs and applications. In most cases, responses belong to a specific application and they will be pass through the application-level module. In rare cases, when a message is not associated with any application, the SMSC-level module will be used.

This module doesn't stop processing of the message. It sends the welcome message (if filled out; no message is sent if left empty) in addition to the further processing which may or may not result in any additional messages.


Welcome text message -- the message to send. If the message is not given, the module is inactive. The message field can be found on the application and SMSC edit page in the Service Layer UI.

Opt-in and out module

This module helps you opt-in and opt-out end users.

How it works

The opt-in and out module works on an application level.

Each application has an associated set of default opt-in (SUB SUBSCRIBE OPT-IN 'OPT IN' START) and opt-out keywords (STOP END QUIT CANCEL UNSUBSCRIBE Q STP 'RE: STOP' RE:STOP REMOVE TERMINATE 'OPT OUT'). Custom keywords can be specified.

When an application receives an opt-in keyword, it replies with the pre-configured opt-in message if the Send opt-in message option is set. It then unblacklists the phone number if it was previously blacklisted.

When an opt-out keyword is received, the application blacklists the phone number when the Auto-blacklisting option is set. It then closes the session with the phone and sends the Opt-out Message.


Client applications receive callbacks from the Service Layer through their Gateway URL. Different callbacks notify applications about user sessions, subscriptions, and more.


Callback date and type are set as individual fields in the POST request to the client application.

  • Field type contains the name/type of the function
  • Field api_key contains the API key of the app to which the callback belongs (in case several SL apps report to the same client-side app)
  • Other fields contain additional information that may be useful to the application in its processing of the callback -- phone numbers, message bodies and other parameters.

Implementation on the client application side

You're not required to take advantage of callbacks. In fact, you can ignore all of them, although the resulting application may be not do much. You ought to at least listen for incoming messages (MOs) through the incoming_message callback.


Some callbacks have optional responses expected. The format of the response is currently set to plain text, but later this may be changed to the JSON.


We suggest that you process the callback as fast as possible on the client application side to release Service Layer resources.

Incoming message (incoming_message)

Received when a new MO arrives and is routed to your application.

When a message initiates a session and the matching keyword has auto-subscription enabled, the auto-subscribe flag is set. This means that after this callback is processed, the message sender is automatically subscribed on the Service Layer side. There will be a subscription confirmation message sent to the user, as configured in the Service Layer UI. You may decide not to send your own automated response from this call to avoid sending two messages.

If the message is marked as auto_responded, it means that the message sender has already received the automated response message that was associated with the keyword. You may choose to send another message or stay silent to avoid duplicates.


  • type -- incoming_message
  • phone_number -- phone number of a texter
  • body -- message body
  • sent_at -- origination timestamp
  • tag -- optional -- conversation thread identifier, present if the message is a reply to a tagged outgoing message (see the send_message API method)
  • auto_subscribe -- boolean, optional -- flags this message as initiating a session with auto-subscription
  • auto_responded -- boolean, optional -- flags that this message as having already been responded to with the keyword-associated content
  • carrier_id -- optional -- carrier ID
  • carrier_name -- optional -- carrier name


  • Empty response not to send anything back to the texter
  • The message to send
  • JSON-formatted string with any number of the following keys:
    • body -- string -- the reply message body
    • close_session -- boolean -- instruction to close the session between the phone number and this app, so that any following incoming messages are not be routed directly to this app. Note that dedicated SMSCs will still messages routed to the app, but out of session.
    • reprocess -- string -- a text to send as an incoming message on behalf of the phone number that has sent the presently handled message. In combination with close_session it’s a powerful mechanism for transferring control to another application or redirecting to itself with a different keyword.
Example of JSON response

{"body":"Switching to another app","close_session":true,"reprocess":"another keyword"}

After receiving this response, the SL will:

  1. Send a message "Switching to another app" as a response to the initial MO.
  2. Close the session between the app and the phone.
  3. Pretend to receive (will send it to itself) an MO with text "another keyword" from the same phone.

Delivery report (delivery_report)

When the message is sent with send_message (either in the future or immediately), a list of message IDs is returned. If a message was longer than the maximum number of allowed characters and as a result was split into parts, each ID represents each of those parts.


  • message_id -- ID of the message the status of which has changed
  • final -- boolean -- TRUE if this report is final and no updates are to be expected
  • status - new status of the message, where the possible values are:
    • 0 -- Pending delivery
    • 1 -- Delivered
    • 2 -- Delivery failed
    • 4 -- Queued for delivery


  • Ignored -- This is a one-way fire and forget notification.


Callbacks involving user sessions.

The FAQ has more details on the concept of sessions.

Session closed (session_closed)

This callback is performed when a user session with an application is closed for any reason. It can be the result of a session expiration or due to an explicit command from a user.


  • type -- session_closed
  • phone_numbers -- comma-separated list of user phone numbers


For your application to know of and respond to various application events, we provide convenient callbacks.

Phone blacklisted (phone_blacklisted)

This callback is performed when a phone number is blacklisted. Usually that's the result of receiving one of several special opt-out keywords addressed by the STOP module.

STOP keywords

  • STOP
  • END
  • QUIT
  • Q
  • STP
  • RE: STOP
  • And a series of related natural language variations


  • phone_number -- the blacklisted phone number

Phone unblacklisted (phone_unblacklisted)

This is invoked when a phone number is unblacklisted by the STOP module.

OPT-IN keywords

  • SUB
  • OPT-IN
  • OPT IN
  • User defined


  • phone_number -- unblacklisted phone number

Auto-reply loop detected (auto_reply_loop)

Sometimes, an application response and user auto-responder may unintentionally initiate an infinite loop of messages. Let's say you send Hello to user Foo's phone, which has a vacation auto-response (or anything else clever, but not clever enough). Your application, to be helpful, replies with Sorry, we don't know what you mean!, which returns Foo's vacation response once more. Or worse, Foo, isn't a proper phone at all, but another application with no rate limit.

In such cases, we inform you in order for your application to take any necessary measures.


  • phone_number -- user phone number

Unicode not supported (no_unicode_support)

Carriers in some countries (destinations) don't accept unicode in the message body. We can't deliver such messages and let you know accordingly.


  • destination -- destination
  • message -- body of the message containing unicode

Invalid characters were founded in message invalid_characters

In this callback you can find what characters in message were problematic for our side. Some of them were replaced by similar valid alternatives and some of them were dropped. In any case usage of these characters is not good idea.


  • characters -- comma separated list of characters


This set of calls allows an application to associate and dissociate keywords. All necessary checks are performed automatically to avoid conflicts.

Validate keyword (validate_keyword)

Checks a keyword for validity and uniqueness.


  • api_key -- string -- API key of the application
  • keyword -- string -- keyword
  • mode -- integer -- 0 -- match exactly (default), 1 -- starts with


  • HTTP Code 200 with JSON hash (text/javascript):
    • errors - optional array of keyword-related error messages. If this element isn’t present, the operation was successful.

Associate keyword (associate_keyword)

Associates a keyword with an application.


  • api_key -- string -- API key of the application
  • keyword -- string -- keyword
  • mode -- integer -- 0 -- match exactly, 1 -- starts with
  • auto_subscribe -- boolean, optional -- TRUE -- keyword will initiate the subscription
  • auto_subscribe_tags -- string, optional -- comma-separated list of tags to assign to the newly created auto-subscription


  • HTTP Code 500 and error message in the body
  • HTTP Code 200 with JSON hash (text/javascript):
    • errors - optional array of error messages, in case of any problems with the keyword registration (duplicates, incorrect format, etc.). If this element isn't present, the operation was successful.

Dissociate keyword (dissociate_keyword)

Breaks the link between a keyword and an application.


  • api_key -- string -- API key of the application
  • keyword -- string -- keyword


  • HTTP Code 500 and error message in the body
  • HTTP Code 200 with JSON hash (text/javascript):
    • errors - optional array of error messages. For example, if the keyword belongs to a different application.


The sections describes all of the API calls associated with sending and receiving messages.

Send message (send_message)

If your application needs to send a message to a certain phone number, start with this call. The message can be of arbitrary length. If the message doesn't fit into the standard message size (160 characters for 7-bit alphabets), the message will be broken into several parts automatically, like this:

  • (1/3) The beginning…
  • (2/3) …continuation …
  • (3/3) …the final piece.

Scheduled messages are sent in batches every 15 minutes. The date format (see the on field) may vary from the strict 10/12/2008 12:55AM —0500 to the relaxed Tuesday 9 1:15PM. If the timezone isn't specified, then UTC (GMT) is assumed.

Split modes

When message splitting is necessary, it can be performed in one of three predefined ways. These can be selected on a per-application basis, configured in the Application preferences within the Service Layer UI.

  • Character boundary - the message will be split into parts without looking for gaps between the words and the ends of sentences. This is the most compact way of breaking up a message, ensuring that a minimal number of parts will be sent.

  • Word boundary - the message text is analyzed for gaps between words. It will split messages so that no word is broken unless the message becomes severely under-filled (more than a half of the message is empty). In this case the Character Boundary mode is used to fill the rest of the part and the operation switches back to the normal Word Boundary operation for the next part.

  • Sentence boundary - the message is broken into parts preferably on sentence edges. These are commas, exclamation and question marks, and line breaks. Again, if the message appears to be severely under-filled, the mode is switched to Word Boundary. And if even that doesn’t help, then it's lowered to the Character Boundary.

Test number

Phone numbers which start with 555 are used for testing purposes.

Messages to these numbers will not be sent to a real phone number. Consequently, there's no charge for sending message to these numbers.


  • api_key -- string -- API key of the application.
  • phone_number or phone_numbers -- string -- destination phone number (10 digits with or without country code) or a comma-separated list of phone numbers.
  • body -- string -- message text
  • action_expected -- boolean, optional -- send TRUE if a response from the target phone is expected. In this case, a session between the phone and the application is established for seamless direction of the response MOs without the need of sending a keyword.
  • on -- string, optional -- time when the message is to be sent.
  • tag -- string, optional -- conversation thread identifier. Messages with the same tag will all be sent via one DID.


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200
    • Comma-separated list of scheduled message IDs is returned with the message_ids key. This is useful for matching sent messages with delivery reports and message cancellation. Negative IDs are possible in these cases:
      • -1 -- if phone number was invalid.
      • -2 -- if phone number is currently blacklisted.
      • -3 -- if phone is being flooded and reached the limit of messages for a configured period of time for this app. Adjust or remove flood limits in the Application configuration UI.

Send messages (send_messages)

Sometimes you need to send several slightly different messages to multiple phone numbers. You could issue multiple send_message requests, but there's a better way. Instead, you can specify a template to use as the basis for your messages. Then, for each number, you need only provide the different parts to paste into the template for each number.

Here's an example:

  • template = "Hello {{name}}. You have {{credits}} credits remaining."
  • recipients = '{"0123456789": {"name": "John", "credits": "5"}, "9876543210": {"name": "Mary", "credits": "10"}}'

The phone number 0123456789 will receive Hello John. You have 5 credits remaining., while the phone number 9876543210 will receive Hello Mary. You have 10 credits remaining.


  • api_key -- string -- API key of the application.
  • template -- string -- template string to use for generation of recipient messages.
  • recipients -- string -- JSON-hash with phone numbers (10 digits) as keys and the substitution hash as values. Each substitution hash contains the names of tags you mentioned in the template body as keys and the replacement values as values.
  • action_expected -- boolean, optional -- send TRUE if a response from the target phone is expected. In this case, a session between the phone and the Application is established for seamless direction of the response MOs to the application without the need of sending the Keyword to select the Application first.
  • on -- string, optional -- time when the message is to be sent.
  • tag -- string, optional -- conversation thread identifier. Messages with the same tag will all be sent via one DID.


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200
    • Comma-separated list of scheduled message IDs (in no particular order) is returned with the message_ids key. Negative IDs are possible in these cases:
      • -1 -- if phone number was invalid.
      • -2 -- if phone number is currently blacklisted.

Cancel messages (cancel_messages)

This call cancels messages scheduled for future delivery (that is, those send_message calls with parameter on). The send_message call returns a list of IDs corresponding to all scheduled messages or parts of a longer message which was split into parts automatically. You can use these IDs to cancel future messages at any time.

Please note that this call can cancel only non-subscription messages. To cancel subscription-level messages, use the cancel_subscription_messages call instead.


  • message_ids -- string -- comma-separated list of scheduled message IDs


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200
    • canceled_count -- integer -- the number of canceled messages.

Delivery status (delivery_status)

This call accepts a single sent message ID or a list of IDs separated by commas. It returns the status for each of the corresponding messages. If the message wasn't found, it's not mentioned in the results. The statuses have numeric values, which are as follows:

  • 0 -- Pending delivery
  • 1 -- Delivered to the user's phone
  • 2 -- Not delivered to the user's phone (failed)


  • message_ids -- string -- comma-separated list of message IDs (either scheduled or not) returned from the send_message call


  • A JSON-formatted map of found message IDs to their statuses

Remove tag (remove_tag)

Allows you to reuse the conversation thread (and its associated DID) for communication with another recipient using the same tag. This frees a DID for new uses.


  • api_key -- string -- API key of the application.
  • phone_number -- string -- phone number of the message recipient.
  • tag -- string -- conversation thread identifier.


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200

Virtual numbers (DIDs)

Virtual numbers - DIDs - are used for sending and receiving SMS. Each DID has a limit of 60 messages per minute, set by the carriers. In order to provide greater throughput, an application must have several DIDs grouped into an SMSC.

List DIDs (dids)

Return a list of DIDs used by an application.


  • api_key -- string -- API key of the application.


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200
    • Comma-separated list of DIDs assigned to a dedicated SMSC belonging to a given application.

Find available DIDs for purchase (find_did)

Returns a list of DIDs available for purchase.


  • api_key -- string -- API key of the application.
  • number -- string, optional -- fragment of desired DID number (say, an area code you prefer).
  • country -- string, optional -- ISO 3166-1 alpha-2 country code of DID (default: US).


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200
    • Comma-separated list of DIDs matching criteria available for purchase.

Add DID (add_did)

Buy a DID and assign it to an application's dedicated SMSC.


  • api_key -- string -- API key of the application.
  • number -- string, optional -- DID number; when omitted, a random number for the specified country will be used.
  • country -- string, optional -- ISO 3166-1 alpha-2 country code of the DID (default: US).


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200
    • The DID.

Delete DID (delete_did)

Delete a DID from an application.


  • api_key -- string -- API key of the application.
  • number -- string, optional -- the DID you would like deleted.


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200


Sessions are used to maintain connections between a user's phone and the application. See the FAQ for more.

Close sessions (close_sessions)

Closes active sessions between the application and phone numbers.


  • api_key -- string -- API key of the application.
  • phone_numbers -- string -- comma-separated list of phone numbers (10 digits with or without country code) with which to close the session.


  • HTTP Code 500 and error message in the body.
  • HTTP Code 200


This set of calls manages end-user (Phone) blacklisting.

Users who opt-out are blacklisted, preventing them from receiving further messages.

You can unblacklist using these API calls, or by having the user text in a subscription keyword.

Blacklisting (blacklist)

Adds phone numbers to the blacklist arbitrarily.


  • api_key -- string -- API key of the application
  • phone_number or phone_numbers -- string -- phone number (10 digits with or without country code) or a comma-separated list of phone numbers.


  • A JSON-formatted map of specified phone numbers to statuses of blacklisting:
  • true -- phone number was successfully blacklisted;
  • false -- blacklisting operation failed.

Unblacklisting (unblacklist)

Removes phone numbers from the blacklist.


  • api_key -- string -- API key of the application
  • phone_number or phone_numbers -- string -- phone number (10 digits with or without a country code) or a comma-separated list of phone numbers.


  • A JSON-formatted map of specified phone numbers to their blacklist status:
  • true -- phone number was successfully removed from the blacklist
  • false -- unblacklisting operation failed

Blacklisting status (blacklist_status)

Query for the blacklist status of a given phone number or numbers.


  • api_key -- string -- API key of the application
  • phone_number or phone_numbers -- string -- phone number (10 digits with or without a country code) or a comma-separated list of phone numbers.


  • A JSON-formatted map of the specified phone numbers to their blacklist status:
  • true -- phone number is blacklisted
  • false -- phone number is not blacklisted

This guide is part of a series on SMS best practices, pulled from requirements set by the Mobile Marketing Association's (MMA) best practices and our own experience building SMS applications.

General best practices when working with SMS

In particular, over long codes

  • Be careful with tobacco, alcohol and drug-related messaging.
  • Be even more careful with sweepstakes and contests. They're not a good use for long codes, anyway.
  • Be careful with the use of "free" in your advertising. In the US, recipients still pay for messages received, so the preferred wording is "Msg&Data Rates May Apply," which indicates that you're sending messages as part of a "Standard rate" program. Free to End User (FTEU) messaging does exist. Details on that category are beyond the scope of this document.
  • We recommend you purge old subscriptions after 18 months.

Notes on content

Note that certain content is taboo over long codes in the US: political marketing (or, generally speaking, marketing of any kind), encouraging the use of weapons, drugs, violence, advertising pornography, sending hate speech, referring to gambling or prostitution, and anything that would offend a particular mobile carrier. We're believers in the first amendment. Carriers are less interested in promulgating free speech. We don't suggest you respect that decision, but barring policy changes, we expect you to abide by it.

On the whole, these content restrictions are similar to those applying to short code programs.

We primarily support long code programs. These are meant to simulate peer to peer (P2P) traffics. That means chat, forwarding messages, group messaging and so forth. Long codes are explicitly not intended for marketing and mass messaging.

Do note that some messages may be subject to silent filtering by carriers, including messages which:

  • Appear to be duplicates (identical sender, recipient and content)
  • Contain known spam keywords, like 'loan' or 'payday advance' and so forth

This guide is part of a series on SMS best practices, pulled from requirements set by the Mobile Marketing Association's (MMA) best practices and our own experience building SMS applications.

Opt-in methods

From the MMA, appropriate methods of opt-in, for a user responding to some kind of call to action:

1. Subscriber may send a Mobile Originated (MO) message from their handset to the short code.

This means a user sends a message, typically containing a special keyword, to a phone number expecting it.

2. Subscriber may initiate opt-in from a web interface and ...from a WAP interface

Note This method is double opt-in.

In this case, you should reply with an outgoing message (MT) to the user with a request for opt-in confirmation. This request can contain a PIN code (anywhere in the message) and instructions for opting-in online or via an affirmative SMS reply.

3. Subscriber may initiate opt-in from an IVR system

In this case, a user requests to receive text messages via a voice-based, automated phone tree.

4. Subscriber may initiate opt-in from a paper-based consent form

We would suggest that the particular medium isn't material.

After the opt-in request, send a confirmation MT (outgoing message) to the user, containing

  • Service description
  • The wording "Msg&Data Rates May Apply"
  • Frequency of messaging - a good estimate will do; in many cases, this isn't practical or possible
  • Customer support information (HELP) - tell them they can text in for help
  • Opt-Out information (STOP) - tell them they can opt-out at any time


This opt-in applies only to the specific program a subscriber is subscribed to and should not be used as a blanket approval to promote other programs, products, and services. However, after the subscriber has been given the complete details about the opt-in scope, the subscriber may opt-in to receive other messages. A content provider may, however, communicate with existing opted-in subscribers through non-premium messages that a) notify subscribers of updates to their existing service or b) are part of a retention program for that particular service. Directions to unsubscribe from these messages must be clearly available with the delivery of each message.


Selling mobile opt-in lists is prohibited.

Finally, the canonical example:

This guide is part of a series on SMS best practices, pulled from requirements set by the Mobile Marketing Association's (MMA) best practices and our own experience building SMS applications.

Opt-out: terminating subscriptions

Here, we'll cover mandatory opt-out workflow, which is about as much fun as it sounds; like the procedural bits of the Ken Starr report (timely!). This applies to both US short and long codes, but is a good practice everywhere. Our focus is so-called "standard rate" messaging. For premium short codes, the rules are stricter still.

By thorough acceptance of STOP requests across all applications, we can help assure users that STOP is a universal opt-out mechanism in SMS. For instance, that means we should opt-out both users who have subscribed to your application in the past and those texting in STOP as their first message, to reassure them you're adhering to the standard.

As soon as a user subscribes to your program, you must explicitly tell them how to opt-out, and they must be able to do so at any time.

That said, consider the well-intentioned, but rigidly applied opt-out workflow within a complex chat application. How do we discount the false positives?

The notable stipulation is that "...programs should support mixed case opt-out commands and ignore subsequent non-keyword text.." This means that, ideally, applications should be able to catch wildcard STOP messages. In the case of a chat application, the user may not always be indicating an opt-out.

Keywords, which cannot be case sensitive, that an application should respond to with an opt-out and immediate blacklisting

  • STOP - this alone must be included in your message copy and advertising
  • END
  • QUIT
  • STOP ALL - this should opt users out of all applications, without presenting a menu

You're allowed to initiate opt-outs based on other keywords of your choice, but must respond to at least the keywords above. For consistency, you should send an opt-out confirmation message in response to any of these keywords, even if the user isn't subscribed to any program at all.

For instance, a user may text in STOP as their first message to your system and you must send back a proper opt-out response. You could customize this for first-time texters, but it may only add to the user's confusion.

When STOP, or any of the opt-out keywords above, is sent to a program, the program must respond with an MT message, whether or not the subscriber is subscribed to the program (this was recently cleared by the FCC).

For one-time messaging, an opt-out MT can simply say that the user was never subscribed, and then blacklist the user. In practice, it's simpler to have a common opt-out message for both subscribed an non-subscribed, first-time texters. The general principle behind the STOP command is to provide simple, consistent behavior so that users come to understand it as a universal opt-out mechanism. As the MMA says, "this is to avoid subscriber confusion around the use of the STOP command."

For the same reason, they note that "The STOP command should never result in an error being sent back to the subscriber."

A STOP request should result in a message (MT) sent to the subscriber and a complete blacklisting of the subscriber, with no further messages unless they explicitly opt-in again.

Opt-out confirmation message


Or the MMA's canonical example:

Multiple choice menus

For long code programs maintaining separate sets of phone numbers (DIDs) for each application, this may not be a concern. But if you do run multiple programs on the same numbers, keep the following in mind.

For subscribers who've opted into multiple programs, a simple STOP request isn't sufficiently descriptive. Rather than opting them out of all programs at once, you need to present a multiple choice menu.

You're encouraged to present STOP ALL as the final choice in the menu, and respond to STOP ALL at any time by opting users out of all subscribed programs (even if it's only one, or, as we learned above, none).

The menu need not include:

  • "Msg&Data Rates May Apply" - by the way, this copy changes every once in a while, rarely becoming more elegant
  • Sponsor contact information

Much like the Help menu, this could look like,

Subscribed to multiple programs. Rply w one of the following to stop:
A) First program
B) Second program
C) Another program

A reply with one of the options would return the relevant opt-out confirmation message. We would suggest accepting anything from "A" followed by any text as well as the words "First program." If users are going to the trouble of calling up a menu and replying to it, it's clear that they want out, so make it easy for them. Subscribers who don't want to be there will cost you more in the long-term and it's in yours and your users' best interest to do more than merely follow the letter of the law here.

However, this isn't a common worfklow. Most systems aren't sophisticated enough to pick up on these types of open-ended responses, so you're typically stuck with something that more closely resembles the example in the MMA handbook:

Farm League Baseball: which service to stop?
For Sports Reply STOP SPORT to cancel
For Horo Reply STOP HORO to cancel

And subscribers would reply with STOP HORO to end the Horoscope program. This results in a relatively poor user. Not to mention that it omits their own requirement of including STOP ALL as the final option. Quod licet Iovi, non licet bovi.

Alternately, you can opt users out of all programs when they reply STOP, bypassing the menu and treating opt-out keywords as a STOP ALL request.

Additional notes

For those doing IVR (automated phone tree) opt-ins

Any IVR system that offers the possibility to opt-in to a mobile service must also offer the possibility to opt-out. This should be available through the IVR, customer service, a web site, or SMS.

We would take this farther and allow users to easily opt-out via the web in any case.

And be aware that users will try to opt-out by non-standard means (so much for the universality of STOP). Users text in crazy, crazy things, often not realizing they're interacting with a machine.

The MMA acknowledges this by suggesting that,

Content providers should periodically scan their MO logs for subscribers that are clearly trying to unsubscribe to a service, but are not following the programmed rules. And then take the action to end their subscription based on those MO logs.

Log everything:

The content provider (or the aggregator) should record and store all opt-out transactions.

We take care of this for you, but would still encourage you to maintain your own records.

This guide is part of a series on SMS best practices, pulled from requirements set by the Mobile Marketing Association's (MMA) best practices and our own experience building SMS applications.

Responding to HELP requests

Your application should respond to requests for help. When a user texts in the keyword HELP or HLP, respond with a text message containing information about the application and where the user can find support.

From the MMA guidelines,

To help subscribers understand their participation, each program should respond with the program details listed below when the subscriber sends the keyword HELP...

You should send an automatic response to the user in response to a help request whether or not they're currently a subscriber.

HELP must always result in a response.

The automatic response should contain the following

  • Identity of program sponsor. This is defined as the program name, company name, or brand associated with the campaign.
  • Customer support info. Either a toll-free number, web address, or e-mail address.
  • Service description of program. For example, YourCo Employee Chat.
  • Opt-out information. Suggest that the suer can reply STOP or opt-out online.

All of this should be kept to 160 characters or fewer.

Here's how it comes together


Carousel - company chat. More at or email
Text STOP to end. Msg&Data Rates may apply.

Here's the MMA's own example:

Multiple choice

If a user is subscribed to multiple applications, you should return a multiple-choice menu. Note that if you have multiple programs available and the user is subscribed to only one of them, don't present the menu.

The menu itself doesn't need most of the information described above.

In the menu, you can leave out:

  • "Msg&Data Rates May Apply"
  • Opt-out instructions
  • Sponsor contact information

An example menu:

Subscribed to multiple programs. Rply w one of the following for info:
A) First program
B) Second program
C) Another program

Now, you have to determine how you want to handle replies, which can get tricky. Do you want to accept anything that starts with A, B, or C? Do you accept "First program" as a legitimate response? What if you have other keywords active within the same session which overlap with these answers?

With long codes, we can avoid some of these thornier questions by maintaining a strict delineation between phone numbers and applications.

Whatever the approach, the response MT should follow the example of a single-choice menu, above.

This guide is part of a series on SMS best practices, pulled from requirements set by the Mobile Marketing Association's (MMA) best practices and our own experience building SMS applications.

Writing terms and conditions

Directly quoting from the MMA guidelines, Terms and Conditions at a minimum must contain the following:

  • STOP and HELP instructions in bold lettering

  • Program sponsor information, defined as the program name, company name, or brand associated with the campaign

  • For standard rate programs: "Msg&Data Rates May Apply". Different forms of the above text include: Message and Data Rates May Apply, Msg&data rates may apply, Msg&data rates may apply.

  • Customer Service Contact Information: either a toll-free number, a web submission form or an email address.

  • Guidance on the frequency with which the subscriber may expect to receive messages for the duration of the program. Note that for many applications, this cannot be precisely predetermined by the content provider. In this case, the guidance should relate to the expected message frequency under normal circumstances.

We should interject: anticipating messaging frequency can be a ridiculous proposition; do this for short code programs, but for group messaging or chat applications, driven by users with varying demand, this is farcical.

  • If a checkbox is used to indicate a consumers’ acceptance of the terms and conditions, it is not permissible for the checkbox to be pre-checked. [this is good practice for any Terms page]

Other notable items that we can ignore in the case of long code programs:

  • Carrier compatibility - clearly and conspicuously disclose that content is not available on all carriers, as applicable. Include list of supported carrier names whilst excluding all other carrier names.

In this case, we have don't have per carrier restrictions the way that short code programs do.

  • If the content provider offers multiple services, separate T&C’s per service should be provided instead of generic T&C’s that cover all offered services.

Less relevant for long code programs, because we have the luxury of dividing them across DIDs/virtual numbers, rather than running a multi-tenant environment.

We have a few applications that should help you get started. At present, all are in Ruby or Javascript.

All available on Github

Introductory code sample

A simple application that sends messages with templates. Get the gist on Github.

Coaches and Players

An example group messaging application for coaches and players, taking advantage of our advanced number stickiness and tagging features. See coaches_and_players on Github.

Recess Paging System

A full-fledged application, website and billing system included. This was a live, production product with real users. Keep in mind, it is dated. RecessApp on Github.


A neat tool for anonymized chat with, in our use-case, nurses, doctors and emergency workers with their patients. Users could text in and receive messages back to their phones, while the operator texted back via their web browsers, without compromising the patient's anonymity by revealing their number.

See Clinic on Github.


A text-to-screen ("projection bombing") app. We also used this one in production, and again, it's pretty old :)

It also includes a less apparent feature: a very cool semi-anonymous proxy chat tool which we used for a "Singles Mingle" event. Users could text one another, using IDs on nametags issued at the event, without revealing their phone numbers to the recipient. After stripping out the IDs, we streamed the conversations on nearby displays.

View the source on Github.


Users send in email addresses and we parse them, sending out an automated text and email concurrently. We used this to cap off a presentation at a mobile conference. Effective and fun!

See Autoemail on Github.


An automated alert street-sweeping alert system. Subscribers would receive messages at set intervals (you can see the configuration options in the admin UI) about street-sweeping, ticketing and towing in their neighborhood.

The admin panel and subscriber management pieces can be found here and code for the website, a basic Wordpress theme, is also available on Github.

We've made available some useful tools for building SMS and other applications.

All available on Github

SMS Toolkit

Number parsing and other useful things. We'll be expanding this. Fork it on Github.


An excellent Node.js implementation of our API, by the tireless Stephen Rhyne,

On @srhyne's Github.


One of the libraries we've used for building billing plans. General purpose, but especially useful for SMS and other apps with usage-based billing.

Fork it on Github.


If you're integrating with Open Market over SMPP, this should come in handy. While we're not working with them, we'd be happy to share our knowledge of SMPP-based short code providers. Our open_market library on Github.

Aloha plugins and other stuff

In our Github profile, you'll find a number of plugins for the open-source Aloha editor, plus other projects we've OSd.


How do I receive messages from users? Do I need to poll the Service Layer (using websockets, etc.)?

For an application to accept incoming messages, it needs a gateway URL. You can register this URL on your application page. We'll direct all incoming messages to that address. The response will be in the form of a POST request containing all of the message details. You can even respond with a message body to send back to that phone as a reply if you'd like.

So you don't need to poll our system for new messages. Simply write a handler that accepts POST requests and do whatever your business logic requires with it. This handler will receive other events as well (subscriptions, delivery reports, etc.). You're free to implement only the parts you need.

For details, check the callbacks section.

Sending messages to phones

Single message to a single number

When you want to deliver a message to a single phone number, use the send_message API call, specifying the message body, phone number, whether a response is expected, and, if you'd like the message sent later, specify the time of delivery.

Single message to several numbers

When you have something to say to several phone numbers, you can use the send_message API call as you would with a the single phone number (see above). But instead of including only one number, specify all of the numbers you'd like the message delivered to as a comma-separated list.

Custom messages to several numbers

When you want to send a personalized message that follows a common pattern to several numbers, you can do that with the send_message API call. Outline the template, like "Hello {{name}}", and then provide a JSON-formatted hash of phone numbers to the substitutions map, like "{'0123456789': {'name': 'Jack'}, …}". We will place your custom values for each number in the corresponding placeholders and deliver.

How do I get the delivery status for the message?

When you send a message, we return the ID of this message that you can use for your internal records, and for asking the delivery status of the message. Even though you will be notified through the callback when the status of the message changes, you can still ask the Service Layer for the status directly at any moment.

What are the delivery statuses?

When the delivery status is reported or you get it back as the result of your direct query, it will be one of the following values:

  • 0 -- Message is still in the queue and was not pushed to the mobile provider for delivery. This can happen when you've submitted the message for the future delivery or when the mobile provider hasn't confirmed that they've received the message from us. The latter happens periodically due to the asynchronous nature of SMS.
  • 1 -- Message was successfully delivered to the gateway and subsequently, the phone.
  • 2 -- Message was not delivered to the gateway or phone. Likely causes: either an unsupported carrier, land line or the phone has text messaging disabled.
  • 4 -- Message is queued for delivery on the mobile provider side. Sometimes recipients have no reception, their phones are off or the network is overloaded. The mobile provider reports that the message is received from us and that it's in the queue for delivery. There will be another status update soon after this one.


What is a session and why is it important?

SMS is innately stateless. When a message from a cell phone arrives, it's the Service Layer's responsibility to identify which application to send this message to. The Service Layer maintains a table of sessions (virtual links with phone numbers and applications). If there's a record for the cell phone number in question, it knows the application to send the message body to.

Once a user is in session with your application, you can treat a shared SMSC as though it were dedicated, with a wide-open keyword space. For instance, if you're building a chat application, there's no need for your users to memorize strange commands, like "reply to every message with MYCHATKEYWORD then your message," in order to send in free-form messages. Just initiate a session and let them text naturally.

How are sessions established?

If there's no record in the sessions table, the Service Layer takes the body of the message and attempts to find an application that is expecting something resembling that message body. For example, if your application is associated with the keyword Surf, when a message with this body arrives, the Service Layer will establish a virtual link between the source phone number and your application. All of the following messages (no matter the content) from this phone number will be sent directly to your application until the session expires (based on inactivity type) or until the phone number sends an opt-out message, such as STOP.

For applications using dedicated SMSCs with one or many DIDs, all messages sent to those DIDs are automatically routed to the application.


What are keywords?

Traditionally (insofar as SMS has traditions), keywords were the primary means of interacting with a text messaging system. A user can text in a predefined keyword to begin interacting with an automated system, to be routed to a specific application or person.

With our API, keywords are used to identify which application a user intends to talk to. Any application can have many keyword associations. All keywords can be matched in either Starts with or Matches exactly modes. We treat keywords as only the starting point for a truly functional SMS application.

Why do I need keywords?

You can have a completely open-ended application, on a dedicated SMSC, where all inbound messages arrive for you to parse. Or you can add structure, where users initiate conversations with your application by texting in a predefined keyword, so that you know what they intend to do and can then decide where to route them. On a shared SMSC, you'll need users to text into a keyword or initiate the conversation with an outbound message.

When a message arrives from a phone number with no active session, we scan it for keywords and see which application matches, create a session for it and send the message over.

What are the Starts with and Matches exactly modes?

Starts with mode matches any keywords a user texts in that begins with the keyword you associate. For example, if you have a weather forecasting application, you may want to associate the keyword Forecast and set the mode to Starts with. That will allow your users to send messages like Forecast Miami, Florida. The Service Layer will see your target keyword at the beginning, establish a session and send the whole message to your application for processing.

Matches exactly mode is used when the message is supposed to contain only the target keyword. It can have spaces and letters in mixed cases, but only this keyword will match and your application will be selected.

Note that keywords are case insensitive. Talk to us if you need more advanced tools, like RegEx-generated keywords.

What are the Auto-subscribe and Auto-subscribe Tags?

If you have the Subscription Module enabled for your application, you can create keywords to automatically subscribe users. If you specify a list of tags, they will be used to tag the user when being subscribed through this keyword.

You can then send messages to groups of users by tags.

What is Don't start session?

Imagine that you have an app that just sends one-off responses, like weather forecasts for a given area or time table information for a particular station. You don't need to maintain a persistent session between the user and the application.

Another scenario is when your application sends alerts of different types and you want to minimize the implementation effort. For different keywords, you enable the auto-subscription feature (along with the Subscription Module) and auto-tag the users without creating a session.

Then, a user can send several keywords one after another and all you will need to do is to send messages to your subscribers filtered by tags.

What is an Auto-response?

The auto-response is an automatic reply sent to users in response to a specific keyword. It can be used with the other options in different combinations. In the above scenario, the auto-response can be used to send confirmations back to the users saying that you've successfully subscribed them to your alerts.

This feature has a side-effect. If you use the Subscription Module in your application and the Auto-response for the keyword that has auto-subscription enabled, your users will receive at least two messages - one for your Auto-response and another one for the confirmation on successful subscription.

We say at least two messages, because there is a possible third message. If enabled, this could be the First-Timers Message when the user texts your system the first time. That's entirely optional, but helpful if you're building a system with deeper interaction, where you'd like to provide an introductory instructional message. Or, generally, if you'd like all first-time users to receive a piece of information that's most relevant at the beginning, like your contact information separate from the subscription welcome message.

Customizing module messages

There are three main modules that work together with an application -- the Welcome, Help and Subscription modules. Each of them has associated messages that can be customized or left blank, in which case nothing will be sent to the end user when triggered. We'll examine them in the following sections.

Welcome message

Every application can be configured to welcome its new users with a message. We know when it's the first time someone has texted you and can send a reply to them automatically, but we also allow you to provide your own content. Think of this welcome message as an optional additional message. You can leave it blank if you don't need it.

Help message

If your response to HELP message never changes, you may want to use our next module -- the Help Module. It will send an automatic response to anyone asking for help. If you leave this message blank, the help request will be sent to your application as any other message, with no automatic reply sent to the user.

Subscription messages

We have two types of messages that work only when the Subscription Module is enabled:

  • Subscribe message -- used to confirm that the user has been subscribed successfully.
  • Stop message -- used to confirm a successful unsubscribe.

Customize these messages to include a name and links to your web site.

Search results