WebSockets Overview¶
GroupMe’s real-time messaging is powered by a Faye-based Bayeux WebSocket protocol. Clients subscribe to various channels and receive structured push messages.
GroupMe's official documentation has some outdated and incomplete information, but it may be helpful to read alongside these docs.
WebSocket messages sent downstream to clients are divided into three channels, each with its own respective context and message types.
The most useful channel is /user/:user_id
, which sends messages about new events that any client will send you push notifications about.
(e.g., new messages in groups you're a part of or reactions attached to messages you have sent).
The other two less important channel types are /groups/:group_id
, and /direct_message/:dm_id
.
Both send information about specific channels that a client wouldn't need to send push notifications for but would need to render the channel when it's open correctly and on the screen.
(e.g., typing indicators, new reactions attached to other messages in the channel that you have not sent, or even membership and role updates for specific users)
Faye/Bayeux WebSocket clients exist in many languages (JavaScript, Ruby, Python, etc.), or you can implement the protocol manually.
For verbosity, we outline how to authenticate and connect using a Faye client library, in pure WebSockets (in case you don't have access to a library), and finally in pure HTTP to do manual long-polling.
Option 1: Using a Faye client (Recommended)¶
Start by initiating a connection with GroupMe's Faye server:
Next, we need to tell our Client to add our GroupMe API token to any subsequent subscription requests and subscribe to the main user channel:
Important
The timestamp
parameter is in seconds since the Unix epoch, not milliseconds.
Finally, we subscribe to any other channels we need to:
Tip
This step is usually overkill. Almost all important real-time updates will come through the /user/:user_id
channel. You will need to subscribe to individual groups or direct message channels if you want to catch read receipts or certain admin events.
Important
Direct Message channel IDs are reported within the REST API looking something like 74938777+93645911
, two user IDs separated with a +
. However, for whatever reason, the WebSocket server only accepts DM channel IDs when they are separated using an underscore (_
). Make sure to find and replace these symbols before attempting to subscribe to those channels.
Option 2: Pure WebSockets¶
If you're not using a Faye client library, you can still connect to GroupMe’s real-time Push Service by directly implementing the Bayeux protocol over WebSockets. This approach is transport-agnostic and works in any language that supports WebSockets and JSON.
For complex steps (like subscription formatting), we’ll show JavaScript snippets to help illustrate what your code might look like.
Step 1: Perform the Handshake (via HTTP)
Before opening a WebSocket, you must perform an initial handshake via HTTP to receive a clientId
. This is a one-time HTTP POST request to the Bayeux endpoint.
Send a JSON array with a channel of /meta/handshake
, the Bayeux version, and the supported connection types. You must include "websocket"
in supportedConnectionTypes
.
GroupMe will respond with a clientId
, which you'll use for all future messages. The response also includes a list of supported transport types (confirm "websocket"
is included), and an advice
object for reconnection behavior.
Step 2: Open a WebSocket to the Push Server
Once you’ve received a valid clientId
, initiate a WebSocket connection to:
After connecting, begin sending JSON-encoded Bayeux messages directly over the socket.
Step 3: Start the /meta/connect
Loop
Immediately after connecting, send a /meta/connect
message to initiate the message delivery loop. This step essentially "registers" your client as ready to receive pushes.
This message must be sent repeatedly after each /meta/connect
response — think of it as polling, but over a persistent socket.
In JavaScript, this could look like:
The server will respond with successful: true
and may include an advice
field specifying a timeout
or interval
before the next call.
Step 4: Subscribe to Channels
To receive push notifications, you must subscribe to the appropriate channel(s). Most useful real-time events will come through /user/:user_id
.
Subscriptions require authentication: you must include your GroupMe API access token and a Unix timestamp (in seconds) in the ext
field.
In JavaScript, constructing this might look like:
Step 5: Listen for Messages
All incoming WebSocket messages will be JSON arrays of Bayeux-style messages. Each one will include:
- A
channel
- A
data
payload - and potentially some metadata, like
id
orclientId
Example incoming message:
In JS, you'd handle this with something like:
Step 6: Maintain the Connection Loop
The /meta/connect
message must be sent repeatedly. This acts as a heartbeat and delivery mechanism for future messages.
Follow the advice.interval
and advice.timeout
values returned in /meta/connect
responses to avoid premature disconnection.
Optional: Subscribing to Group or DM Channels
You can also subscribe to /group/:group_id
and /direct_message/:direct_message_id
channels to get additional channel-specific messages that wouldn't usually buzz your phone, like typing indicators.
To do this: repeat step 4 as many times as necessary, setting the subscription
parameter to whatever channel you're interested in.
Important
Please note that when subscribing to DM channels, you must replace the +
in the conversation ID (as it appears in the REST API) with an _
instead. We're not entirely sure why this inconsistency exists, but it does.
Option 3: Manual Long-Polling over HTTP (Not Recommended, but still possible)¶
Start by establishing a connection with GroupMe's Faye server.
Send a POST request to https://push.groupme.com/faye
. It should look like this:
The response should look something like:
Note the clientId
value we've just received, as we will need it in the next step.
In order to subscribe to channels we need to send another POST request with the following body, inserting the ClientId
value we got from the last request in step one.
Important
- The
id
parameter should increment with each successive call to the server. Not doing so may lead to undefined behavior.>
- The
timestamp
parameter is in seconds since the Unix epoch. Divide whatever timestamp you have by 1000.
GroupMe's response should look something like this:
Tip
This step is usually overkill. Almost all important real-time updates will come through the /user/:user_id
channel. You will need to subscribe to individual groups or direct message channels if you want to catch read receipts or certain admin events.
The POST request for subscribing to a specific channel looks like this (Note that it is basically exactly the same except for a different subscription channel):
Important
Direct Message channel IDs are reported within the REST API looking something like 74938777+93645911
, two user IDs separated with a +
. However, for whatever reason, the WebSocket server only accepts DM channel IDs when they are separated using an underscore (_
). Make sure to find and replace these symbols before attempting to subscribe to those channels.
This step is already handled for you by most Faye libraries. However, if you're doing this manually via HTTP and not WebSockets, you will need to manually check for updates from the Faye server.
If GroupMe has nothing to report, it will respond with an array of placeholder objects for each of the channels you're subscribed to. That would look something like this:
If there is something to report, GroupMe will respond with something that might look like this:
Websocket Message Structure¶
When your client is connected to the GroupMe WebSocket server and subscribed to channels, you will receive messages. These messages follow the Bayeux protocol, and the core information is typically found within the data object of the incoming Faye message.
The most important field within data is data.type, which indicates the kind of event that has occurred.
ping
- A keep-alive message. You don't usually have to bother handling these.line.create
- A message was sent in a channel you participate in. This is the most common type of message, and includes many events that normally send system messages.like.create
- Someone reacted to one of your messages.
Messages specific to a group or direct message channel include:
favorite
- Someone likes a message that is not yours.direct_message.create
-line.create
, but for DM channels.message.deleted
- A message was deleted.message.update
- A message was edited.typing
- Someone started typing.
As far as we're aware, clients only send two types of messages upstream besides subscriptions. Those types are:
typing
- Used to initiate a typing indicator in a channel, these are good for 5 seconds and then must be resent to keep the indicator alive.ping
- A keep-alive or presence message, can be used to measure websocket latency.
Other Implementations¶
For those curious here are some other working implementations beyond the scope of the example posted in these docs:
-
My bot, Lowes. I'm sure I stole this working code from somewhere, but I can't for the life of me figure out where.
-
groupme_push, a GroupMe push implementation in Python.