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.
WebSocket messages sent downstream to clients are divided into three different channel types, each with their 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 or membership/role updates for specific users)
Connecting to the Gateway¶
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)
Faye/Bayeux WebSocket clients exist in many languages (JavaScript, Ruby, Python, etc.), and they're the preferred way of hitting GroupMe's Push Gateway. If your language doesn't have a Bayeux client or you really want a challenge you can implement the protocol manually using raw WebSockets or stick to polling for updates via HTTP (Not recommended, but still possible).
For simplicity, this example will be given in node.js using the faye npm package.
Start by initiating a connection with GroupMe's Faye server and configuring it to add your API token and a timestamp (in seconds since the Unix epoch) to any outgoing messages:
Subscribing to Channels
In order to actually start receiving messages, you must subscribe to a channel. The main channel you will always want to subscribe to is /user/:user_id
(where :user_id
is your GroupMe account's ID) as it is where most general updates will be sent. Any notification that will buzz your phone is sent over this channel.
In some circumstances, in order to catch certain events (like typing indicators) you will also need to subscribe to specific message channels. This differs slightly between groups and direct messages, where groups use /group/:group_id
, and DMs use /direct_message/:chat_id
. Note that subgroup channels in a group are considered their own channels, so in order to catch their updates you must subscribe to /groups/:subgroup_id
.
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 (e.g. 74938777_93645911
). 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.
Info
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
.
HTTP Request | |
---|---|
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.
Data | |
---|---|
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.
Data | |
---|---|
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:
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)
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:
HTTP Request | |
---|---|
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.
HTTP Request | |
---|---|
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:
HTTP Response | |
---|---|
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):
HTTP Request | |
---|---|
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.
HTTP Request | |
---|---|
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 Object Types¶
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.
Schemas for most WebSocket messages are documented below.
ping
¶
A keep-alive heartbeat message. This message is sent down every 30 seconds in order to ensure your client is still listening. You can generally ignore these messages if you dont plan on sending them, as the server will keep the connection alive on its own. Sending a ping
type message causes the server to echo one back and then supresses the server from sending pings for the next 30 seconds. By timing how long it takes to send this message and then receive an echoed ping from the server you can effectively calculate roundtrip ping latancy for the WebSocket gateway.
This and typing
are the only two message types clients are permitted by the API to send.
- type
string - Must be ping
.
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 member join/leave events). More on events can be found here.
Generally, incoming messages will look like this:
- type
string - Must be line.create
.
- alert
string - The text that would usually populate the push notification preview.
- subject
object - The message object this push is refering to.
- received_at
number - the timestamp corresponding to when the notification was sent.
However, in some cases you may observe a system message with an attached subject.event
property. These events are documented here.
- type
string - Must be line.create
.
- alert
string - The text that would usually populate the push notification preview.
- subject
object - The message object this push is refering to.
- received_at
number - the timestamp corresponding to when the notification was sent.
direct_message.create
¶
line.create
, but for received DMs.
Generally, incoming messages will look like this:
- type
string - Must be direct_message.create
.
- alert
string - The text that would usually populate the push notification preview.
- subject
object - The message object this push is refering to.
- received_at
number - the timestamp corresponding to when the notification was sent.
However, in some cases you may observe a system message with an attached subject.event
property. These are documented here.
- type
string - Must be line.create
.
- alert
string - The text that would usually populate the push notification preview.
- subject
object - The message object this push is refering to.
- received_at
number - the timestamp corresponding to when the notification was sent.
membership.create
¶
Fired when you're added to a group.
- type
string - Must be membership.create
.
- alert
string - The text that would usually populate the push notification preview.
- subject
object - A cut down group object describing the group that you've joined.
- received_at
number - the timestamp corresponding to when the notification was sent.
favorite
¶
Someone reacted to a message. This message is also sent when someone removes a reaction from a message.
- type
string - Must be favorite
.
- alert
string - The text that would usually populate the push notification preview. In this case, there is no text, so alert
will always be an empty string.
- subject.line
object - The message being reacted to.
- subject.reactions
array - A list of reaction objects currently tied to the message.
- received_at
number - the timestamp corresponding to when the notification was sent.
message.deleted
¶
A message was deleted. This type is limited to group or direct message channels where the deletion was observed, however it is always paired with a corresponding line.create
message containing a message.deleted
message event that can be caught from the main user channel.
- type
string - Must be message.deleted
.
- alert
string - The text that would usually populate the push notification preview.
- subject
object - The message object that needs to be deleted.
- received_at
number - the timestamp corresponding to when the notification was sent.
message.update
¶
A message was edited. This type is limited to group or direct message channels where the edit was observed, however it is always paired with a corresponding line.create
message containing a message.update
message event that can be caught from the main user channel.
- type
string - Must be message.update
.
- alert
string - The text that would usually populate the push notification preview.
- subject
object - The new message object that should replace the existing message.
- received_at
number - the timestamp corresponding to when the notification was sent.
typing
¶
Someone started typing. This type is limited to group or direct message channels where the typing indicator was observed. GroupMe will send these indicators every 5 seconds while typing. Clients assume typing has stopped if there is no new typing message after 5 seconds, or the user who is typing sends a message before the 5 seconds is up.
This and ping
are the only two messages clients are allowed to send.
- type
string - Must be typing
.
- user_id
string - The ID of the user who is typing.
- started
number - A timestamp corresponding to when the typing started.
Sending WebSocket Messages¶
After you've subscribed to channels, sending faye messages is relatively straightforward:
As far as we're aware, clients are only allowed to send ping
and typing
messages. Any other message type will be ignored by the server.