├── src
├── overrides
│ ├── assets
│ │ ├── sprocket_hero.jpg
│ │ ├── sprocket.svg
│ │ ├── groupmejs_logo.svg
│ │ ├── opengm_logo.svg
│ │ ├── sprocket_colorized.svg
│ │ ├── opengm_logo_colorized.svg
│ │ └── groupmejs_logo_colorized.svg
│ ├── main.html
│ ├── home.html
│ └── .icons
│ │ └── OpenGM
│ │ └── sprocket.svg
└── preprocess.py
├── LICENSE-CODE.md
├── docs
├── index.md
├── api
│ ├── conversations
│ │ ├── index.md
│ │ ├── gallery.md
│ │ ├── pins.md
│ │ └── reactions.md
│ ├── common
│ │ ├── index.md
│ │ └── attachments.md
│ ├── uploads
│ │ ├── index.md
│ │ ├── video.md
│ │ ├── files.md
│ │ └── images.md
│ ├── index.md
│ ├── groups
│ │ ├── campus.md
│ │ ├── subgroups.md
│ │ ├── polls.md
│ │ ├── members.md
│ │ └── messages.md
│ ├── bots
│ │ └── index.md
│ └── dms
│ │ └── index.md
├── contributing
│ ├── licensing.md
│ ├── index.md
│ └── styleguide.md
├── examples
│ ├── index.md
│ ├── dinobot.md
│ └── pingpong.md
└── assets
│ ├── javascripts
│ └── docs-links.js
│ └── stylesheets
│ └── admonitions.css
├── .github
├── workflows
│ └── ci.yml
└── assets
│ └── opengm_logo_colorized.svg
├── readme.md
└── mkdocs.yml
/src/overrides/assets/sprocket_hero.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/groupme-js/GroupMeCommunityDocs/HEAD/src/overrides/assets/sprocket_hero.jpg
--------------------------------------------------------------------------------
/src/overrides/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% extends "base.html" %}
4 |
5 |
6 | {% block extrahead %}
7 | {{ super() }}
8 |
9 | {% endblock %}
10 |
11 |
12 | {% block scripts %}
13 | {{ super() }}
14 |
15 | {% endblock %}
16 |
17 |
18 | {% block announce %}
19 | For updates join the API Development Group on GroupMe :
20 |
21 | https://groupme.com/join_group/27317261/ibNNhx
22 |
23 | {% endblock %}
--------------------------------------------------------------------------------
/LICENSE-CODE.md:
--------------------------------------------------------------------------------
1 | Copyright 2025 The OpenGM Project
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | template: home.html
3 | title: Home
4 | description: Community-led effort to document the reverse-engineered GroupMe API.
5 | hide:
6 | - toc
7 | - footer
8 | ---
9 |
10 | # Welcome to the OpenGM Project
11 |
12 | This is the community-driven, open-source documentation for the GroupMe Public API. Our goal is to provide a comprehensive, up-to-date resource for developers looking to build on the GroupMe platform.
13 |
14 | ## Why this project?
15 |
16 | - **Up-to-Date:** The official documentation is extremely outdated. We strive to keep our docs current with the latest API changes.
17 | - **Community-Powered:** Anyone can contribute! If you find an error or a missing endpoint, you can help improve the docs for everyone.
18 | - **Rich Examples:** These docs are here to provide practical code samples and way more detail than the official docs ever did to make development easier.
19 | - **High Coverage:** These docs cover nearly all endpoints, including those not documented by GroupMe.
20 |
21 | Ready to dive in? Check out the [**API Reference**](/api/index.md) guide or browse the provided [**Examples**](/examples/index.md).
22 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - main
7 | permissions:
8 | contents: write
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-latest
12 | env:
13 | MKDOCS_GIT_COMMITTERS_APIKEY: ${{ secrets.GITHUB_TOKEN }}
14 | steps:
15 | - uses: actions/checkout@v4
16 | with:
17 | fetch-depth: 0
18 | - name: Configure Git Credentials
19 | run: |
20 | git config user.name github-actions[bot]
21 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com
22 | - uses: actions/setup-python@v5
23 | with:
24 | python-version: 3.x
25 | - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
26 | - uses: actions/cache@v4
27 | with:
28 | key: mkdocs-material-${{ env.cache_id }}
29 | path: .cache
30 | restore-keys: |
31 | mkdocs-material-
32 | - run: pip install mkdocs-material "mkdocs-material[imaging]" mkdocs-git-revision-date-localized-plugin mkdocs-git-committers-plugin-2
33 | - run: "sudo apt-get install pngquant"
34 | - name: Preprocess Markdown
35 | run: python src/preprocess.py
36 | - name: Debug docs directory
37 | run: |
38 | echo "Files in ./src:"
39 | ls -R src
40 | - run: mkdocs gh-deploy --force
41 |
--------------------------------------------------------------------------------
/docs/api/conversations/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Conversation Features
3 | description: Learn about features and endpoints common to both Group and Direct Message conversations.
4 | ---
5 |
6 | # Conversation Features
7 |
8 | While Groups and Direct Messages have distinct management endpoints, they share a rich set of features for interaction within a chat. This section documents the API endpoints that are common to all types of conversations, whether it's a large group or a one-on-one DM.
9 |
10 | Here, you'll find information on how to interact with features like message reactions, calendar events, pinned messages, and shared media galleries. These endpoints typically use a generic `:conversation_id` in their path, which can be substituted with either a `group_id` or a DM's `conversation_id`.
11 |
12 |
13 |
14 | - **:material-calendar: [Calendar Events](calendar.md)**
15 |
16 | Create, view, update, and RSVP to events in any conversation.
17 |
18 | - **:material-image-search: [Image Gallery](gallery.md)**
19 |
20 | Browse the history of images and files shared in a group or DM.
21 |
22 | - **:material-pin: [Pinned Messages](pins.md)**
23 |
24 | Programmatically pin and unpin important messages in a conversation.
25 |
26 | - **:fontawesome-solid-face-smile-wink: [Reactions & Likes](reactions.md)**
27 |
28 | Like, unlike, and react to messages using both the classic like system and the newer emoji reactions.
29 |
30 |
--------------------------------------------------------------------------------
/docs/api/common/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Common API Structures
3 | description: An overview of common data structures used across the GroupMe API, such as attachments, events, and emoji.
4 | ---
5 |
6 | # Common API Structures
7 |
8 | While the GroupMe API has many endpoints for interacting with groups, direct messages, and users, several common data structures appear consistently across all of them. Understanding these core structures is essential for building rich, interactive applications.
9 |
10 | Messages are the lifeblood of the API, and they often contain more than just simple text. They can carry special objects that represent rich media, describe system-level occurrences, or encode custom emoji. This section provides detailed documentation on these fundamental building blocks.
11 |
12 |
13 |
14 | - **:material-attachment-plus: [Message Attachments](attachments.md)**
15 |
16 | Learn how to add rich content to your messages. This guide covers all known attachment types, from images and videos to replies, mentions, and locations.
17 |
18 | - **:material-flag-triangle: [Message Events](events.md)**
19 |
20 | Discover the system-generated events that report on changes within a chat, such as members joining, the group name changing, or a message being pinned.
21 |
22 | - **:fontawesome-solid-face-smile-wink: [Emoji (GroupMe PowerUps)](emoji.md)**
23 |
24 | Dive into GroupMe's custom emoji system. This document explains the `charmap` structure and how to parse and send messages with "PowerUp" emoji.
25 |
26 |
--------------------------------------------------------------------------------
/docs/api/uploads/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: CDN management
3 | description: Guide to managing the CDN for the GroupMe Community API documentation.
4 | ---
5 |
6 | # Uploading Media
7 |
8 | GroupMe provides its own Content Delivery Network (CDN) to host images, videos, and other files that can be attached to messages. To send a message with a media attachment, you must first upload the file to the appropriate GroupMe service. This process will return a URL on the GroupMe CDN that you can then use in your API calls.
9 |
10 | This is a two-step process:
11 |
12 | 1. **Upload:** Send your media file to the correct upload service.
13 | 2. **Attach:** Use the URL returned in Step 1 to create a message with an `image`, `video`, or `file` attachment.
14 |
15 | ## Upload Services
16 |
17 | Each type of media has a specific endpoint and set of requirements.
18 |
19 |
20 |
21 | - **:material-image: [Images](images.md)**
22 |
23 | Learn how to upload images to the GroupMe image service to get a `picture_url` that can be included in messages. This section covers accepted formats, size limits, and the full API workflow.
24 |
25 | - **:material-video: [Videos](video.md)**
26 |
27 | This guide details the process for uploading video files. It covers supported video formats, resolution and size constraints, and how to get a shareable URL for use in a `video` attachment.
28 |
29 | - **:material-file-cog: [Files](files.md)**
30 |
31 | For other content like documents or generic file types, use the file attachment service. This section explains how to upload arbitrary files and attach them to messages.
32 |
33 |
--------------------------------------------------------------------------------
/docs/api/uploads/video.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Video Service"
3 | description: "Learn how to interact with GroupMe's video CDN via the API."
4 | ---
5 |
6 | # Uploading Videos
7 | To upload a video to be processed to GroupMe's video processing service, POST to `https://video.groupme.com/transcode` with the following included in the HTTP header:
8 |
9 | ```linenums="1"
10 | Content-Type: multipart/form-data;boundary=[MIME_BOUNDARY]
11 | X-Conversation-Id: [GROUP_ID]
12 | X-Access-Token: [ACCESS_TOKEN]
13 | ```
14 |
15 | Note: if you are using some kind of library to send the file, you shouldn't have to worry about the content type, as the library should handle that part for you.
16 |
17 | The video should be in the POST request as multipart/form-data file
18 | The equivalent curl command is:
19 |
20 | ```bash linenums="1"
21 | curl 'https://video.groupme.com/transcode' -X POST -H "X-Access-Token: [ACCESS_TOKEN]" -H "X-Conversation-Id: [GROUP_ID]" --form file="@[FILE_NAME]"
22 | ```
23 |
24 | A properly uploaded video should have a response of `200 OK` to the previous request, along with a JSON object containing a job status id:
25 | ```json linenums="1" title="HTTP Response"
26 | Status: 200 OK
27 | {
28 | "status_url":"https://video.groupme.com/status?job=[JOB_UUID]"
29 | }
30 | ```
31 |
32 | To get the status of a working job, GET `https://video.groupme.com/status?job=[JOB_UUID]`
33 | A complete job will contain the following JSON in the response, with a header of `201 Created`:
34 | ```json linenums="1" title="HTTP Response"
35 | Status: 201 Created
36 | {
37 | "status":"complete",
38 | "url":"[VIDEO_URL]",
39 | "thumbnail_url":"[THUMB_IMAGE_URL]"
40 | }
41 | ```
42 |
43 | The completed video upload is ready to send as an [attachment](../common/attachments.md)
44 |
--------------------------------------------------------------------------------
/docs/contributing/licensing.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Project Licensing"
3 | description: "Licensing information for the GroupMe Community API Docs project."
4 | ---
5 |
6 | # Project Licensing
7 |
8 | This project is provided under a dual-license model to ensure that our documentation is shared openly and our code examples are freely usable.
9 |
10 | ***
11 |
12 | ## Documentation License
13 |
14 | All documentation content in this repository, including all text, tutorials, and illustrations within `.md` files, is licensed under the [**Creative Commons Attribution 4.0 International License (CC-BY-4.0)**](https://creativecommons.org/licenses/by/4.0/).
15 |
16 | Under this license, you are free to:
17 | * **Share** — copy and redistribute the material in any medium or format.
18 | * **Adapt** — remix, transform, and build upon the material for any purpose, even commercially.
19 |
20 | You must give appropriate credit, provide a link to the license, and indicate if changes were made.
21 |
22 | ***
23 |
24 | ## Code License
25 |
26 | All source code, including standalone files (e.g., example projects) and **all code snippets embedded within any documentation file**, is licensed under the permissive [**MIT License**](https://opensource.org/license/MIT).
27 |
28 | This ensures that you can freely use any code you find in this project in your own applications, whether they are open-source or commercial, without worrying about attribution requirements for the code itself.
29 |
30 | ***
31 |
32 | A full copy of the Creative Commons Attribution 4.0 license as well as the MIT License is available in the root directory of this project's repository [on GitHub](https://github.com/groupme-js/GroupMeCommunityDocs).
33 |
34 | ***If you have any questions about the licensing or how to comply with it, please feel free to reach out in our [Developer Chat](https://groupme.com/join_group/27317261/ibNNhx) or open an issue on GitHub.***
35 |
36 | ***
--------------------------------------------------------------------------------
/docs/api/uploads/files.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "File Service"
3 | description: "Learn how to interact with GroupMe's file CDN via the API."
4 | ---
5 |
6 | # Uploading Files to GroupMe
7 | To upload a file to be processed to GroupMe's file service, POST to `https://file.groupme.com/v1/[GROUP_ID]/files?name=[FILE_NAME]` with the following included in the HTTP header:
8 |
9 | ```json linenums="1"
10 | Host: file.groupme.com
11 | Content-Type: application/json
12 | Accept-Encoding: gzip, deflate
13 | Connection: close
14 | X-Access-Token: [ACCESS_TOKEN]
15 | ```
16 |
17 | Note that the content type is application/json, even though a data binary is going to be uploaded. Files must be less than 50MB to upload properly. Any file extension can be uploaded.
18 |
19 | The file should be in the POST request as a data binary
20 | The equivalent curl command is:
21 |
22 | ```bash linenums="1"
23 | curl -i -s -k -X "POST" -H "Host: file.groupme.com" -H "Content-Type: application/json" -H "X-Access-Token: [ACCESS_TOKEN]" -H "Accept-Encoding: gzip, deflate" -H "Connection: close" --data-binary @[FILE_NAME] https://file.groupme.com/v1/[GROUP_ID]/files?name=[FILE_NAME]
24 | ```
25 |
26 | A properly uploaded file should have a response of `201 OK` to the previous request, along with a JSON object containing a job status id:
27 | ```json linenums="1"
28 | {
29 | "status_url": "https://file.groupme.com/v1/[GROUP_ID]/uploadStatus?job=[JOB_UUID]"
30 | }
31 | ```
32 |
33 | To get the status of a working job, GET `https://file.groupme.com/v1/[GROUP_ID]/uploadStatus?job=[JOB_UUID]`
34 | A complete job will contain the following JSON in the response, with a header of `200 OK`:
35 | ```json linenums="1"
36 | {
37 | "status":"complete",
38 | "file_id":"[FILE_ID]"
39 | }
40 | ```
41 |
42 | The completed file upload is ready to send as a file [attachment](../common/attachments.md), using the `file_id` as a reference to the uploaded file. Files uploaded to one group may not be shared to another group
43 |
--------------------------------------------------------------------------------
/src/preprocess.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 |
4 | SOURCE_DIR = "docs"
5 |
6 | # Mapping of GitHub-style admonition types to MkDocs-style
7 | ADMONITION_MAP = {
8 | "note": "note",
9 | "important": "important",
10 | "tip": "tip",
11 | "info": "info",
12 | "warning": "warning",
13 | "caution": "caution",
14 | "danger": "danger",
15 | "error": "danger",
16 | "success": "success",
17 | "question": "question",
18 | "abstract": "abstract",
19 | "summary": "summary",
20 | "todo": "todo",
21 | "quote": "quote",
22 | "seealso": "seealso",
23 | "example": "example",
24 | "bug": "bug",
25 | }
26 |
27 | def convert_admonitions(text):
28 | # Matches > [!WARNING] style GitHub markdown
29 | pattern = re.compile(r'> \[!(\w+)\]\n((?:> .*\n?)*)')
30 |
31 | def replacer(match):
32 | raw_label = match.group(1).lower()
33 | body = match.group(2).strip()
34 | label = ADMONITION_MAP.get(raw_label)
35 |
36 | if not label:
37 | return match.group(0)
38 |
39 | # Extract lines starting with "> "
40 | lines = [line[2:] for line in body.splitlines() if line.startswith("> ")]
41 |
42 | # Detect title: first non-empty line is bolded
43 | title = None
44 | content_lines = []
45 |
46 | for i, line in enumerate(lines):
47 | if not line.strip():
48 | continue
49 | title_match = re.match(r"\*\*(.+?)\*\*", line.strip())
50 | if title_match:
51 | title = title_match.group(1).strip()
52 | content_lines = lines[i + 1 :]
53 | else:
54 | content_lines = lines[i:]
55 | break
56 |
57 | # Build the admonition
58 | header = f'!!! {label} "{title}"' if title else f"!!! {label}"
59 | body = "\n".join(f" {line}" for line in content_lines)
60 | return f"{header}\n{body}"
61 |
62 | return pattern.sub(replacer, text)
63 |
64 | def process_docs():
65 | for root, _, files in os.walk(SOURCE_DIR):
66 | for file in files:
67 | if file.endswith(".md"):
68 | filepath = os.path.join(root, file)
69 | with open(filepath, "r", encoding="utf-8") as f:
70 | content = f.read()
71 | new_content = convert_admonitions(content)
72 | with open(filepath, "w", encoding="utf-8") as f:
73 | f.write(new_content)
74 |
75 | if __name__ == "__main__":
76 | process_docs()
77 |
--------------------------------------------------------------------------------
/docs/api/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "API Overview"
3 | description: "Learn how to interact with GroupMe via the API."
4 | ---
5 |
6 | # API Overview
7 |
8 | GroupMe has a thriving developer community which has created a variety of applications, bots, and tools. However, while the platform has constantly matured, the public documentation has not. In an effort to help other developers understand and utilize the platform better, this is a community-led effort to document everything possible. If you discover something not listed here, or you find an error in what is listed, please bring it up on the discussion page or submit a pull request.
9 |
10 | Much of this information is pulled from [dev.groupme.com](https://dev.groupme.com/), which is the official GroupMe developers site. However, most of its information is outdated or incomplete, so please help keep this repo up-to-date.
11 |
12 | If you have any questions or would like to get in touch, please join the [GroupMe API Development Group](https://groupme.com/join_group/27317261/ibNNhx) or open an issue.
13 |
14 | **This documentation is not associated with GroupMe or Microsoft**
15 |
16 | ## Quick Links:
17 |
18 |
19 |
20 | - :material-robot-excited: __[Webhook Bots]__ – How to create, index, and manage bots
21 | - :octicons-arrow-switch-16: __[Websocket Gateway]__ – How to connect to the Push service to get real-time interaction with your program
22 | - :material-account-cog: __[Account Management]__ – How to access/update information about your account, turning on/off SMS mode, and configuring profile details
23 | - :material-account-group: __[Group Management]__ – How to manage groups, members, and messages
24 | - :material-message: __[Direct Messages]__ – How to list, read, and send DMs, as well as how to block/unblock users
25 | - :material-chat-processing: __[Conversation Management]__ – How to make calls allowed in both DMs and Groups, such as creating calendar events, pinning messages, reacting to messages and more
26 | - :material-code-json: __[Common API Structures]__ – How to leverage common structures used in the API, such as attacments, emojis, and system events
27 | - :material-image: __[CDN Management]__ – How to upload Images to GroupMe's CDN
28 |
29 |
30 |
31 | [Webhook Bots]: bots/index.md
32 | [Websocket Gateway]: ws/index.md
33 | [Account Management]: account/index.md
34 | [Group Management]: groups/index.md
35 | [Direct Messages]: dms/index.md
36 | [Conversation Management]: conversations/index.md
37 | [Common API Structures]: common/index.md
38 | [CDN Management]: uploads/indexs.md
39 |
40 |
41 | ***
42 |
--------------------------------------------------------------------------------
/docs/examples/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Example Projects
3 | description: A collection of example projects that utilize the GroupMe Community API.
4 | ---
5 |
6 | # Example Projects
7 |
8 | The GroupMe API allows for two primary methods of creating bots: **Webhook Bots** and **User Bots**. Each has distinct advantages and is suited for different tasks. Understanding their differences is key to building your project.
9 |
10 | ***
11 |
12 | ## Webhook Bots vs. User Bots
13 |
14 | ### Webhook Bots (Callback/Webhook Bots)
15 |
16 | Webhook Bots are the most common and straightforward type. They function like a web server, passively listening for events.
17 |
18 | - **How they work:** You provide GroupMe with a **Callback URL**. When a message is posted in a group where your bot is present, GroupMe sends an HTTP POST request to that URL containing the message data.
19 | - **Identity:** They are distinct entities with their own name and avatar, created through the [Bot Creation Form](https://dev.groupme.com/bots/new).
20 | - **Authentication:** They use a `bot_id` to post messages.
21 | - **Scope:** They are "blind" by default and can only see messages in the specific groups they have been added to.
22 | - **Best for:** Simple, event-driven tasks within a single group, like responding to commands, posting daily updates, or running games.
23 |
24 | ### User Bots (Push/WebSocket Bots)
25 |
26 | User Bots are more powerful and complex. They act on behalf of a real user account, subscribing to a real-time stream of events. By their nature they can do nearly everything a regular user can do (with some exceptions), but they are more complex to set up and manage.
27 |
28 | - **How they work:** Instead of waiting for a callback, they open a persistent **WebSocket connection** to GroupMe's Push Service. This gives them a real-time firehose of all activity visible to the user account they are linked to.
29 | - **Identity:** They act *as a user*. They post messages and perform actions under that user's name and do not have their own separate bot identity.
30 | - **Authentication:** They use a user's personal `access_token`.
31 | - **Scope:** They can see everything the associated user can see—all group chats, direct messages, and events like users joining/leaving.
32 | - **Best for:** Advanced applications that require monitoring activity across multiple groups, managing chats, or providing real-time logging or cross-group functionality.
33 |
34 | ***
35 |
36 | ## Our Examples
37 |
38 | Here are some example projects to help you get started.
39 |
40 |
41 |
42 | - :ping_pong: [PingPong Bot](pingpong.md) – A simple bot that replies "pong" when you say "ping". (getting started with callbacks and attachments)
43 | - :t_rex: [DinoBot](dinobot.md) – A bot that responds to messages in a GroupMe group by sending dinosaur emojis. (emoji system demo)
44 |
45 |
--------------------------------------------------------------------------------
/src/overrides/home.html:
--------------------------------------------------------------------------------
1 | {% extends "main.html" %}
2 |
3 | {% block announce %}{% endblock %}
4 |
5 | {% block tabs %}
6 | {{ super() }}
7 |
8 |
57 |
58 |
59 |
60 |
61 |
62 |
{{ config.home_tagline }}
63 |
{{ config.home_description }}
64 |
65 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | {% endblock %}
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
7 |
GroupMe Community API Docs
8 |
9 | The complete, up-to-date, and community-driven documentation for the GroupMe API.
10 |
11 |
12 |
13 |
19 |
20 |
21 |
22 |
23 |
24 | ## About This Project
25 |
26 | The GroupMe platform has a vibrant developer community, but the official API documentation has not kept pace with the platform's evolution. This project is a community-led effort to create a comprehensive, accurate, and up-to-date resource for anyone building on the GroupMe API.
27 |
28 | Here you will find:
29 |
30 | - ✅ **Complete Endpoint Reference:** Detailed documentation for the v3, v4, and other unlisted API endpoints.
31 | - 📚 **In-Depth Guides:** Clear explanations of core concepts like the WebSocket gateway, attachments, and the emoji system.
32 | - 🤖 **Practical Examples:** Working bot examples to help you get started quickly.
33 | - ✨ **Undocumented Features:** Information on hidden parameters, event types, and API behavior not found anywhere else.
34 |
35 | > [!important]
36 | > **Disclaimer:** This documentation is a community effort and is not officially associated with, or endorsed by, GroupMe or Microsoft.
37 |
38 | ## Quick Links
39 |
40 | - **[API Reference](https://groupme-js.github.io/GroupMeCommunityDocs/api)** - A complete breakdown of all known endpoints.
41 | - **[Bot Examples](https://groupme-js.github.io/GroupMeCommunityDocs/examples)** - See how to build functional bots.
42 |
43 | ## Contributing
44 |
45 | This project thrives on community contributions! Whether you're fixing a typo, documenting a newly discovered feature, or improving an example, your help is welcome.
46 |
47 | To get started, please read our **[Contributing Guide](https://groupme-js.github.io/GroupMeCommunityDocs/api/contributing)**, which covers:
48 |
49 | - How to submit changes and open a pull request.
50 | - The **[Style Guide](https://groupme-js.github.io/GroupMeCommunityDocs/api/contributing/styleguide)** for keeping the docs consistent.
51 |
52 | ## Community
53 |
54 | Have a question, an idea, or just want to chat with other GroupMe developers?
55 |
56 | ➡️ **[Join the GroupMe API Development Chat](https://groupme.com/join_group/27317261/ibNNhx)**
57 |
58 | ## License
59 |
60 | This project is under a dual-license model:
61 |
62 | - All documentation content is licensed under **[CC-BY-4.0](./docs/LICENSE.md)**.
63 | - All source code, including examples and snippets, is licensed under the **[MIT License](./docs/LICENSE-CODE.md)**.
64 |
65 | By contributing, you agree to license your contributions under these terms.
--------------------------------------------------------------------------------
/docs/api/uploads/images.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Image Service
3 | description: Learn how to interact with GroupMe's Image Service via the API.
4 | ---
5 |
6 | # Image Service
7 |
8 |
9 | Images uploaded to the GroupMe Image CDN will have URLs that look like this:
10 |
11 | ```
12 | https://i.groupme.com/{width}x{height}.{format}.{id}
13 | ```
14 |
15 | Where {width} and {height} are in pixels, {format} is for example "png" or "jpeg" and {id} is a unique id e.g.
16 |
17 | ```
18 | https://i.groupme.com/480x325.jpeg.9e20b71dd6af4b58bbd132d4a7dec009
19 | ```
20 |
21 | ## To try this out via cURL:
22 |
23 | Store your access token in the GM_TOKEN environment variable first.
24 |
25 | ```bash
26 | curl 'https://image.groupme.com/pictures' -X POST -H "X-Access-Token: $GM_TOKEN" -H "Content-Type: image/jpeg" --data-binary @AwesomePicture.jpg
27 | ```
28 |
29 | ## Thumbnails
30 |
31 | Images are automatically thumbnailed at the following sizes:
32 |
33 | | **suffix** | **size** | **example** |
34 | |------------|------------------------------------------------------------|-------------------------------------------------------------------|
35 | | preview | 200x200, centered and cropped | https://i.groupme.com/100x100.png.123456789.preview |
36 | | large | 960x960, preserve aspect ratio so largest side is 960 | https://i.groupme.com/100x100.png.123456789.large |
37 | | avatar | 60x60, centered and cropped | https://i.groupme.com/100x100.png.123456789.avatar |
38 |
39 | ## Images
40 |
41 | Nearly every instance of an image URL within the API **MUST** be processed by the Image CDN before it can be used.
42 |
43 | You can upload a variety of different kinds of image formats (including GIFs) to the image CDN in order for them to be processed, stored, and thumbnailed.
44 | ### Uploading local images
45 |
46 | If you want to send an image you have stored locally, you first have to upload it to GroupMe's servers via their [image service](images.md). This is done with a simple request:
47 |
48 | ```json linenums="1" title="HTTP Request"
49 | POST https://image.groupme.com/pictures
50 | ```
51 |
52 | Importantly, this request MUST be done with the following headers:
53 |
54 | * **Content-Type**: "image/jpeg" (For some reason it doesn't work with "image/png" as far as I can tell, but you can still send .png files under "image/jpeg")
55 | * **Content-Length**: The size of your image in bytes
56 | * **X-Access-Token**: Your user's API token
57 |
58 | Then, send the binary data of your image file.
59 |
60 | Issues with this feature are often caused by problems with the user token.
61 |
62 | ```json linenums="1" title="HTTP Response"
63 | Status: 200 OK
64 | {
65 | "payload": {
66 | "url": "https://i.groupme.com/123456789",
67 | "picture_url": "https://i.groupme.com/123456789"
68 | }
69 | }
70 | ```
71 |
72 | ### Uploading remote images
73 |
74 | If you want to send a remote image by its URL, you'll still have to upload it to GroupMe's servers via their [image service](images.md). This will behave similar to uploading local images, but with a new url parameter:
75 |
76 | ```json linenums="1" title="HTTP Request"
77 | POST https://image.groupme.com/pictures?url=
78 | ```
79 |
80 | You must to provide the **X-Access-Token** user API token as a header.
81 |
82 | **Response**
83 |
84 | Your response will be of the same format as above:
85 | ```json linenums="1" titile="HTTP Response"
86 | Status: 200 OK
87 | {
88 | "payload": {
89 | "url": "https://i.groupme.com/123456789",
90 | "picture_url": "https://i.groupme.com/123456789"
91 | }
92 | }
93 | ```
94 |
--------------------------------------------------------------------------------
/docs/examples/dinobot.md:
--------------------------------------------------------------------------------
1 | # DinoBot
2 |
3 | DinoBot is a simple webhook bot for GroupMe, originally written in C# by Microsoft as a demo for GroupMe's Bot API. This is a Node.js port of the original bot, which responds to messages in a GroupMe group by sending dinosaur emojis.
4 |
5 | ## Setup
6 |
7 | Before you can run DinoBot, you need to set up a GroupMe bot and a callback URL that points to your server/host where the bot will receive and handle messages. The easisest way to create a bot is to use [GroupMe's Bot Creation form](https://dev.groupme.com/bots/new), where you can specify the bot's name, avatar, and callback URL. Alternatively, you can create a bot using the API and your user account's API token. Read more about creating and managing bots in the [Bots API Reference](../api/bots/index.md).
8 |
9 | After registering a new bot with GroupMe, create a new Node.js project and install express.js (An HTTP server framework for JavaScript we will use to catch GroupMe's POST messags to our callback URL).
10 |
11 | You can do this by running the following commands in your terminal:
12 |
13 | ```bash
14 | npm init -y
15 | npm install express
16 | ```
17 |
18 | ## DinoBot Script
19 |
20 | This is the main script for DinoBot, which listens for messages in a GroupMe group and responds with dinosaur emojis based on certain triggers. The bot can respond to requests for dinos, mentions of its name, or random questions.
21 |
22 | You can save this code in a file named `DinoBot.js`, and run it using Node.js with this commmand:
23 |
24 | ```bash
25 | node DinoBot.js
26 | ```
27 |
28 | Without further ado, here's the code for DinoBot. Feel free to modify it to suit your needs or add more features!
29 |
30 | ```js linenums="1" title="DinoBot.js"
31 | const express = require('express'); // Import the express framework for handling HTTP requests
32 |
33 | const app = express();
34 | app.use(express.json());
35 |
36 | const PORT = process.env.PORT || 3000; // Port for the bot to listen on
37 | const CALLBACK_URL = '/DinoCallback' // The URL path where GroupMe will send POST requests
38 | const BOT_ID = '[Your Bot ID Here]'; // Your Bot ID from the GroupMe Developer Portal
39 |
40 | const MAX_DINOS = 100; // Maximum number of dinos DinoBot can send in one go
41 | const RESPONSE_WEIGHT = 0.075; // Probability DinoBot will answer a question
42 | const CAN_ADDRESS_DINO = []; // empty means everyone, add user IDs to only allow specific users to address the bot
43 |
44 | async function postEmoji(count, BOT_ID) {
45 | if (count > MAX_DINOS) count = MAX_DINOS;
46 |
47 | const placeholder = '�' // Placeholder character to be replaced by the emoji
48 | const charmap = Array(count).fill([1, 62]); // 1 is the emoji's pack ID, 62 is the pack index for the dinosaur emoji
49 |
50 | await fetch('https://api.groupme.com/v3/bots/post', {
51 | method: 'POST',
52 | headers: { 'Content-Type': 'application/json' },
53 | body: JSON.stringify({
54 | bot_id: BOT_ID,
55 | text: placeholder.repeat(count),
56 | attachments: [
57 | {
58 | type: 'emoji',
59 | placeholder,
60 | charmap
61 | }
62 | ]
63 | }),
64 | });
65 | }
66 |
67 | // handle POST requests from GroupMe hitting DinoBot's callback URL
68 | app.post(CALLBACK_URL, async (req, res) => {
69 | const msg = req.body;
70 |
71 | if (!msg || !msg.text) {
72 | return res.status(400).send('Invalid request');
73 | }
74 |
75 | const text = msg.text.toLowerCase();
76 |
77 | // respond if someone asks for dinos. Try saying "1 dino" or "5 dinosaurs"
78 | const DINO_REGEX = /^(\d+)\s+(dino(?:s|saur)?)/; // Regex to match "N dino(s)" or "N dinosaur(s)"
79 | const dinoMatch = DINO_REGEX.exec(text);
80 | if (dinoMatch) {
81 | const num = Math.min(parseInt(dinoMatch[2]), MAX_DINOS);
82 | if (num > 0) {
83 | await postEmoji(num);
84 | return res.status(201);
85 | }
86 | }
87 |
88 | // respond if someone says DinoBot's name. Try saying "What's up DinoBot?"
89 | if (text.includes('dinobot')) {
90 | await postEmoji(1);
91 | return res.status(201);
92 | }
93 |
94 | // respond on the off chance dino knows a good answer. Try asking "What is the meaning of life?" or "How do I make a sandwich?"
95 | if (text.includes('?') && Math.random() < RESPONSE_WEIGHT) {
96 | await postEmoji(1);
97 | return res.status(201);
98 | }
99 |
100 | res.status(200);
101 | });
102 |
103 | // Start the bot
104 | app.listen(PORT, () => console.log(`🦕 DinoBot running on port ${PORT}`));
105 | ```
--------------------------------------------------------------------------------
/docs/api/conversations/gallery.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Image Galleries"
3 | description: "Learn how to interact with GroupMe's channel-specific image galleries via the API."
4 | ---
5 |
6 | # Image Gallery
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## Index
33 |
34 | List a Group or DM conversation's previous *messages* that contain images or files stored in the Gallery.
35 |
36 | The response is paginated (sort of), with a default of 100 images per page. Specifying a `before` timestamp allows you to fetch beyond the first page of results.
37 |
38 | ```json linenums="1" title="HTTP Request"
39 | GET /conversations/:conversation_id/gallery
40 | ```
41 |
42 | **Parameters**
43 |
44 | * *acceptFiles*
45 |
46 | boolean - A flag to tell the image service your client is capable of receiving non-image files. Setting this value to `0` or omitting it entirely will NOT omit messages with file attachments in the response; these messages will still be included, but the text property will be overwritten with `Please upgrade to download this file.` The file attachment will still be fully intact, however. In practice this means that if you care about the text in the message with an attachment, you should set `acceptFiles=1`.
47 |
48 | * *limit*
49 |
50 | integer - Defines page size. Defaults to 100.
51 |
52 | * *before*
53 |
54 | string - A timestamp in ISO 8601 format denoting the latest image timestamp to include in the response. This is used for pagination: the server will return images older than this timestamp. If omitted, it fetches the most recent images.
55 |
56 | * *after*
57 |
58 | string - A timestamp in ISO 8601 format denoting the oldest image timestamp to include in the response. This is used for pagination: the server will return images newer than this timestamp. Can be used with or without the `before` parameter.
59 |
60 | ```json linenums="1" title="HTTP Response"
61 | Status: 200 OK
62 | {
63 | "messages": [
64 | {
65 | "attachments": [
66 | {
67 | "type": "image",
68 | "url": "https://i.groupme.com/274x184.jpeg.6a3a12a63bb4453ea085e29c76825830"
69 | }
70 | ],
71 | "avatar_url": "https://i.groupme.com/184x184.jpeg.63692bfcaa18457eaeaa1dbde8cecb6d",
72 | "created_at": 1747233197,
73 | "favorited_by": [
74 | "103829605"
75 | ],
76 | "gallery_ts": "2025-05-14T14:33:17.8688Z",
77 | "group_id": "98296943",
78 | "id": "174723319786892122",
79 | "name": "jack",
80 | "sender_id": "112904724",
81 | "sender_type": "user",
82 | "source_guid": "B5562890-BCCF-457F-B07D-3FDAB8C773EE",
83 | "system": false,
84 | "text": null,
85 | "user_id": "115904724"
86 | },
87 | {
88 | "attachments": [
89 | {
90 | "type": "image",
91 | "url": "https://i.groupme.com/542x606.jpeg.10f96a004a52451192a673a38371cfac"
92 | }
93 | ],
94 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.52f411008b064201932e9cf98a3d407a",
95 | "created_at": 1747227084,
96 | "favorited_by": [],
97 | "gallery_ts": "2025-05-14T12:51:24.7745Z",
98 | "group_id": "98296943",
99 | "id": "174722708477454148",
100 | "name": "alice",
101 | "sender_id": "130870470",
102 | "sender_type": "user",
103 | "source_guid": "19472C2B-5DE2-4BD4-A3E3-783BDFC9976C",
104 | "system": false,
105 | "text": null,
106 | "user_id": "130850470"
107 | }
108 | ]
109 | }
110 | ```
111 | ***
112 |
--------------------------------------------------------------------------------
/docs/api/conversations/pins.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Pinned Messages"
3 | description: "Learn how to interact with GroupMe's pins system via the API."
4 | ---
5 |
6 | # Pins
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## Pin a message
33 |
34 | Pins a message.
35 |
36 | Note that trying to pin a message which is already pinned will result in an unhelpful 400 error. It may be difficult to programatically determine whether a given call fails due to an actual bad request, or if it's just because the message has already been pinned. For now, you just have to enumerate the pinned mesages and see if there's a match, or get the messatge object and see if the "pinned_at" field is null
37 |
38 | ```json linenums="1" title="HTTP Request"
39 | POST /conversations/:conversation_id/messages/:message_id/pin
40 | ```
41 |
42 | **Parameters**
43 |
44 | * *conversation_id*
45 |
46 | string - The ID of the conversation. For groups, this is just the group ID, whereas for DMs, it's the DM ID, which is of the form `first_user_id+second_user_id` - so, for example, `1234+9876`. The lower ID seems to always be first.
47 |
48 | * *message_id*
49 |
50 | string - The ID of the message to pin.
51 |
52 | ```json linenums="1" title="HTTP Response"
53 | Status: 200 OK
54 | ```
55 |
56 | ***
57 |
58 | ## Unpin a message
59 |
60 | Unpins a message.
61 |
62 | The same note on errors applies - trying to unpin a message which is not already pinned will result in an unhelpful 400 error.
63 |
64 | ```json linenums="1" title="HTTP Request"
65 | POST /conversations/:conversation_id/messages/:message_id/unpin
66 | ```
67 |
68 | **Parameters**
69 |
70 | * *conversation_id*
71 |
72 | string - The ID of the conversation. For groups, this is just the group ID, whereas for DMs, it's the DM ID, which is of the form `first_user_id+second_user_id` - so, for example, `1234+9876`. The lower ID seems to always be first.
73 |
74 | * *message_id*
75 |
76 | string - The ID of the message to unpin.
77 |
78 | ```json linenums="1" title="HTTP Response"
79 | Status: 200 OK
80 | ```
81 |
82 | ***
83 |
84 | ## List all pinned messages
85 |
86 | List all of the pinned messages. The request varies slightly for groups and DMs, unlike the above methods.
87 |
88 | ```json linenums="1" title="HTTP Request (For Groups)"
89 | GET /pinned/groups/:group_id/messages/
90 | ```
91 |
92 | ```json linenums="1" title="HTTP Request (For DMs)"
93 | GET /pinned/direct_messages
94 | ```
95 |
96 | **Parameters**
97 |
98 | * *conversation_id* (required for groups)
99 |
100 | string - The ID of the conversation. For groups, this is just the group ID, whereas for DMs, it's the DM ID, which is of the form `first_user_id+second_user_id` - so, for example, `1234+9876`. The lower ID seems to always be first.
101 |
102 | * *other_user_id* (required for DMs)
103 |
104 | string - The ID of the user (other than you) in the DM channel you're getting the Pins for
105 |
106 | ```json linenums="1" title="HTTP Response (For Groups)"
107 | Status: 200 OK
108 | {
109 | "count": 1,
110 | "messages": [
111 | {
112 | "id": "12345",
113 | "name": "Dasaniel Royer",
114 | "pinned_by": "54321",
115 | "pinned_at": 123312312
116 | ...
117 | }
118 | ]
119 | }
120 | ```
121 |
122 | ```json linenums="1" title="HTTP Response (For DMs)"
123 | Status: 200 OK
124 | {
125 | "count": 1,
126 | "messages": [
127 | {
128 | "id": "12345",
129 | "name": "Dasaniel Royer",
130 | "pinned_by": "54321",
131 | "pinned_at": 123312312
132 | ...
133 | }
134 | ]
135 | }
136 | ```
137 |
138 | For the full form of the message object, see [the Group Messages docs](../groups/messages.md). Note the new fields: `pinned_by` and `pinned_at`. `pinned_by` is the user who pinned it, and `pinned_at` is the timestamp (in seconds) of when it was pinned. When a message hasn't been pinned (which won't happen here, but will happen in other message-getting methods), `pinned_by` will be an empty string (`""`), and `pinned_at` will be `null`.
139 |
140 | ***
141 |
--------------------------------------------------------------------------------
/docs/contributing/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Contributing to the Docs"
3 | description: "Learn how you can contribute to and help improve the GroupMe Community API documentation."
4 | ---
5 |
6 | # Contributing to the Documentation
7 |
8 | Thank you for your interest in contributing! This project is a community effort, and we're thrilled to have you here. Whether you're fixing a typo, documenting a new endpoint, or correcting an error, every contribution helps create a better resource for all GroupMe API developers.
9 |
10 | This guide will walk you through the process of contributing.
11 |
12 | ***
13 |
14 | ## How Can I Contribute?
15 |
16 | There are many ways to contribute, and not all of them require writing code or documentation yourself.
17 |
18 | * **🐛 Reporting Issues:** Find a typo, a broken link, or information that seems incorrect or outdated? The best way to let us know is by [opening an issue](https://github.com/groupme-js/GroupMeCommunityDocs/issues) on our GitHub repository.
19 | * **✍️ Editing and Improving Content:** You can help by clarifying confusing sentences, adding more detailed explanations, or correcting inaccuracies directly.
20 | * **✨ Adding New Content:** If you've discovered a new API endpoint, an undocumented event type, or a hidden parameter, you can help by adding a new page or section.
21 | * **💡 Suggesting Enhancements:** Have an idea for how to make the documentation better? Maybe a new example bot, a different page structure, or a better explanation of a core concept? Feel free to open an issue to discuss your ideas.
22 |
23 | ***
24 |
25 | ## Submitting a Change (Pull Requests)
26 |
27 | For those who want to directly edit or add content, we use the standard GitHub **Fork & Pull Request** workflow. If you've never contributed to an open-source project before, don't worry! Here's a step-by-step guide.
28 |
29 | ***
30 |
31 | ### Step 1: Fork the Repository
32 |
33 | First, you need to create your own copy of the repository. Go to the [main repository page](https://github.com/groupme-js/GroupMeCommunityDocs) and click the **"Fork"** button in the top-right corner. This will create a copy under your own GitHub account.
34 |
35 | ***
36 |
37 | ### Step 2: Clone Your Fork
38 |
39 | Next, clone your forked repository to your local machine so you can make changes.
40 |
41 | ```bash
42 | git clone https://github.com/YOUR_USERNAME/GroupMeCommunityDocs.git
43 | cd GroupMeCommunityDocs
44 | ```
45 |
46 | ***
47 |
48 | ### Step 3: Create a New Branch
49 |
50 | It's best practice to make your changes in a new branch instead of the `main` branch. This keeps your work organized. Choose a descriptive name for your branch.
51 |
52 | ```bash
53 | # For a bug fix or correction
54 | git checkout -b fix/correct-reactions-endpoint
55 |
56 | # For a new feature or page
57 | git checkout -b feat/document-calendar-api
58 | ```
59 |
60 | ***
61 |
62 | ### Step 4: Make Your Changes
63 |
64 | Now you're ready to make your changes! Open the project in your favorite text editor (like VS Code) and start editing the Markdown (`.md`) files in the `docs/` directory.
65 |
66 | Make sure to follow the existing style and structure of the documentation. This is laid out in our [Style Guide](styleguide.md), but if you have any questions please reach out *before* making your change. Here are some key points to remember:
67 |
68 | * Use clear, concise language.
69 | * Use code blocks for examples, specifying the language (e.g., `json`, `bash`, `js`).
70 | * Use admonitions (like `!!! note`, `!!! warning`, `!!! tip`) to highlight important information.
71 | * Use headings and subheadings to structure your content clearly.
72 | * If you're documenting an API endpoint, include the HTTP method and path, and provide example requests and responses. Refer to existing endpoint documentation for examples.
73 |
74 | ```markdown
75 |
76 | !!! note
77 |
78 | We use a special flavor of Markdown that supports additional features like admonitions and special UI elements. You can dind its reference in the [Material for MKDocs Reference Guide](https://squidfunk.github.io/mkdocs-material/reference/)
79 |
80 | ***
81 |
82 | ### Step 5: Commit Your Changes
83 |
84 | Once you're happy with your changes, commit them with a clear and descriptive message.
85 |
86 | ```bash
87 | # Example commit
88 | git commit -m "docs: clarify the difference between reply_id and base_reply_id"
89 | ```
90 |
91 | ***
92 |
93 | ### Step 6: Push Your Changes
94 |
95 | Push your new branch and its commits to your fork on GitHub.
96 |
97 | ```bash
98 | git push origin fix/correct-reactions-endpoint
99 | ```
100 |
101 | ***
102 |
103 | ### Step 7: Open a Pull Request
104 |
105 | Go back to your fork on GitHub. You should see a prompt to **"Compare & pull request"**. Click it, and it will take you to a new page.
106 |
107 | * Give your pull request a clear title.
108 | * In the description, explain **what** you changed and **why**.
109 | * Click **"Create pull request"**.
110 |
111 | That's it! A project maintainer will review your changes, provide feedback if needed, and merge it once it's ready.
112 |
113 | Thank you again for your contribution!
114 |
115 | ***
116 |
117 | By submitting a pull request, you agree to license your contribution under the respective licenses of this project as described in our [license](./licensing.md).
118 |
119 | ***
--------------------------------------------------------------------------------
/src/overrides/assets/sprocket.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
42 |
--------------------------------------------------------------------------------
/src/overrides/.icons/OpenGM/sprocket.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
42 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: OpenGM API Documentation
2 | docs_dir: docs
3 | site_url: https://groupme-js.github.io/GroupMeCommunityDocs/
4 | repo_url: https://github.com/groupme-js/GroupMeCommunityDocs
5 | copyright: Copyright © 2025 - OpenGM Project
6 |
7 | site_description: "The OpenGM Project: Community GroupMe API documentation for all."
8 |
9 | # Custom Homepage Elements
10 | home_cover_image: assets/sprocket.svg
11 | home_tagline: GroupMe Community Docs
12 | home_description: Community GroupMe API documentation for all.
13 |
14 | home_gradient_hsla: hsla(160deg,47%,55%,1)
15 |
16 | home_button1_name: API Reference
17 | home_button1_url: api/
18 | home_button1_theme: md-button md-button--primary
19 |
20 | home_button2_name: Example Projects
21 | home_button2_url: examples/
22 | home_button2_theme: md-button
23 |
24 | theme:
25 | name: material
26 |
27 | font:
28 | text: Mona Sans
29 |
30 | icon:
31 | logo: OpenGM/sprocket
32 | repo: fontawesome/brands/github
33 |
34 | favicon: overrides/assets/sprocket.svg
35 |
36 | code:
37 | highlight_theme: dracula
38 |
39 | custom_dir: src/overrides
40 |
41 | edit_uri: edit/master/docs
42 |
43 | features:
44 | - content.action.edit
45 | - content.action.view
46 | - content.tabs.link
47 | - content.code.copy
48 | - content.tooltips
49 | - navigation.instant
50 | - navigation.instant.progress
51 | - navigation.top
52 | - navigation.tabs
53 | - navigation.sections
54 | - navigation.footer
55 | - navigation.indexes
56 | - search.suggest
57 | - search.share
58 | - header.autohide
59 | - announce.dismiss
60 |
61 | palette:
62 | - media: "(prefers-color-scheme: light)"
63 | scheme: default
64 | primary: light blue
65 | accent: blue
66 | toggle:
67 | icon: material/weather-sunny
68 | name: Switch to dark mode
69 |
70 | - media: "(prefers-color-scheme: dark)"
71 | scheme: slate
72 | primary: light blue
73 | accent: blue
74 | toggle:
75 | icon: material/weather-night
76 | name: Switch to light mode
77 |
78 | plugins:
79 | - social
80 | - search
81 | - tags
82 | - git-revision-date-localized:
83 | enable_creation_date: true
84 | - git-committers:
85 | repository: groupme-js/GroupMeCommunityDocs
86 | branch: master
87 | token: !ENV MKDOCS_GIT_COMMITTERS_APIKEY
88 | exclude_comitters:
89 | - web-flow
90 |
91 | extra:
92 | social:
93 | - icon: simple/groupme
94 | link: https://groupme.com/join_group/27317261/ibNNhx
95 | - icon: fontawesome/brands/github
96 | link: https://github.com/groupme-js/GroupMeCommunityDocs
97 | - icon: fontawesome/brands/discord
98 | link: https://discord.gg/5yxWtuuEZg
99 |
100 | markdown_extensions:
101 | - abbr
102 | - pymdownx.snippets
103 | - attr_list
104 | - md_in_html
105 | - admonition
106 | - codehilite
107 | - toc:
108 | permalink: true
109 | - pymdownx.details
110 | - pymdownx.superfences
111 | - pymdownx.highlight:
112 | anchor_linenums: true
113 | - pymdownx.inlinehilite
114 | - pymdownx.snippets
115 | - pymdownx.tabbed:
116 | alternate_style: true
117 | slugify: !!python/object/apply:pymdownx.slugs.slugify
118 | kwds:
119 | case: lower
120 | - def_list
121 | - pymdownx.tasklist:
122 | custom_checkbox: true
123 | - pymdownx.emoji:
124 | emoji_index: !!python/name:material.extensions.emoji.twemoji
125 | emoji_generator: !!python/name:material.extensions.emoji.to_svg
126 | options:
127 | custom_icons:
128 | - overrides/.icons
129 |
130 | nav:
131 | - Home: index.md
132 | - API Reference:
133 | - api/index.md
134 | - Webhook Bots:
135 | - api/bots/index.md
136 | - WebSocket Gateway:
137 | - api/ws/index.md
138 | - Message Types: api/ws/messageTypes.md
139 | - Account Management:
140 | - api/account/index.md
141 | - Contacts: api/account/contacts.md
142 | - Oauth/MFA: api/account/oauth.md
143 | - Groups:
144 | - api/groups/index.md
145 | - Subgroups: api/groups/subgroups.md
146 | - Members: api/groups/members.md
147 | - Messages: api/groups/messages.md
148 | - Polls: api/groups/polls.md
149 | - Discovery: api/groups/directories.md
150 | - Campus Directories: api/groups/campus.md
151 | - Direct Messages:
152 | - api/dms/index.md
153 | - Conversation Features:
154 | - api/conversations/index.md
155 | - Emoji Reactions: api/conversations/reactions.md
156 | - Calandar Events: api/conversations/calendar.md
157 | - Image Galleries: api/conversations/gallery.md
158 | - Pinning Messages: api/conversations/pins.md
159 | - Common Structures:
160 | - api/common/index.md
161 | - Message Attachments: api/common/attachments.md
162 | - Emoji Powerups: api/common/emoji.md
163 | - Message Events: api/common/events.md
164 | - CDN Management:
165 | - api/uploads/index.md
166 | - Image Uploads: api/uploads/images.md
167 | - Video Uploads: api/uploads/video.md
168 | - File Uploads: api/uploads/files.md
169 | - Examples:
170 | - examples/index.md
171 | - PingPong Bot: examples/pingpong.md
172 | - DinoBot: examples/dinobot.md
173 | - Contributing:
174 | - contributing/index.md
175 | - Style Guide: contributing/styleguide.md
176 | - Licensing: contributing/licensing.md
--------------------------------------------------------------------------------
/docs/assets/javascripts/docs-links.js:
--------------------------------------------------------------------------------
1 | // allows for linking to the official documentation using a comment beneath any header in the format:
2 | //
3 |
4 | function getMaterialLinkIcon() {
5 | const existingTwemoji = document.querySelector('.twemoji svg');
6 | if (existingTwemoji) {
7 | const iconClone = existingTwemoji.cloneNode(true);
8 | const path = iconClone.querySelector('path');
9 | if (path) {
10 | path.setAttribute('d', 'M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1M8 13h8v-2H8zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4a5 5 0 0 0 5-5 5 5 0 0 0-5-5');
11 | path.setAttribute('fill', 'currentColor');
12 | }
13 | return iconClone.outerHTML;
14 | }
15 |
16 | return ` `;
17 | }
18 |
19 | const materialLinkIcon = getMaterialLinkIcon();
20 |
21 | function addPermanentLinks() {
22 | console.log("Adding permanent links to headers...");
23 | const walker = document.createTreeWalker(
24 | document.body,
25 | NodeFilter.SHOW_COMMENT,
26 | null,
27 | false
28 | );
29 |
30 | let comment;
31 | const processedComments = new Set();
32 |
33 | while (comment = walker.nextNode()) {
34 | if (comment.nodeValue.trim().startsWith("official-doc:") && !processedComments.has(comment)) {
35 | processedComments.add(comment);
36 |
37 | const url = comment.nodeValue.trim().replace("official-doc:", "").trim();
38 |
39 | let currentNode = comment.previousSibling;
40 | let foundHeader = null;
41 |
42 | // Search for header in previous siblings
43 | while (currentNode && !foundHeader) {
44 | if (currentNode.nodeType === Node.ELEMENT_NODE &&
45 | /^H[1-6]$/.test(currentNode.tagName)) {
46 | foundHeader = currentNode;
47 | break;
48 | }
49 | currentNode = currentNode.previousSibling;
50 | }
51 |
52 | // If no header found, search in parent's previous siblings
53 | if (!foundHeader) {
54 | let parentNode = comment.parentNode;
55 | while (parentNode && parentNode !== document.body) {
56 | currentNode = parentNode.previousSibling;
57 | while (currentNode && !foundHeader) {
58 | if (currentNode.nodeType === Node.ELEMENT_NODE) {
59 | if (/^H[1-6]$/.test(currentNode.tagName)) {
60 | foundHeader = currentNode;
61 | break;
62 | }
63 | const headerInNode = currentNode.querySelector('h1, h2, h3, h4, h5, h6');
64 | if (headerInNode) {
65 | const headers = currentNode.querySelectorAll('h1, h2, h3, h4, h5, h6');
66 | foundHeader = headers[headers.length - 1];
67 | break;
68 | }
69 | }
70 | currentNode = currentNode.previousSibling;
71 | }
72 | if (foundHeader) break;
73 | parentNode = parentNode.parentNode;
74 | }
75 | }
76 |
77 | if (foundHeader) {
78 | // Check if link already exists
79 | const existingLinks = Array.from(foundHeader.querySelectorAll('a.headerlink'));
80 | if (existingLinks.some(link => link.href === url)) continue;
81 |
82 | // Create and configure the link
83 | const link = document.createElement("a");
84 | link.href = url;
85 | link.title = "Permanent link";
86 | link.className = "headerlink";
87 | link.target = "_blank";
88 | link.rel = "noopener";
89 | link.innerHTML = materialLinkIcon; // Add the icon as content
90 |
91 | // Actually append the link to the header
92 | foundHeader.appendChild(link);
93 |
94 | console.log(`Added link to header: ${foundHeader.textContent} -> ${url}`);
95 | } else {
96 | console.warn(`No header found for comment: ${comment.nodeValue}`);
97 | }
98 | }
99 | }
100 | }
101 |
102 | // Run on DOM ready
103 | document.addEventListener("DOMContentLoaded", addPermanentLinks);
104 |
105 | // Monitor for dynamic content changes
106 | const observer = new MutationObserver(function(mutations) {
107 | let shouldProcess = false;
108 | mutations.forEach(function(mutation) {
109 | if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
110 | for (let node of mutation.addedNodes) {
111 | if (node.nodeType === Node.ELEMENT_NODE &&
112 | (node.tagName && /^H[1-6]$/.test(node.tagName) ||
113 | node.querySelector && node.querySelector('h1, h2, h3, h4, h5, h6'))) {
114 | shouldProcess = true;
115 | break;
116 | }
117 | }
118 | }
119 | });
120 |
121 | if (shouldProcess) {
122 | console.log("DOM mutation detected, processing links...");
123 | clearTimeout(window.linkProcessingTimeout);
124 | window.linkProcessingTimeout = setTimeout(addPermanentLinks, 100);
125 | }
126 | });
127 |
128 | observer.observe(document.body, {
129 | childList: true,
130 | subtree: true
131 | });
--------------------------------------------------------------------------------
/docs/examples/pingpong.md:
--------------------------------------------------------------------------------
1 | # PingPong Bot
2 |
3 | PingPong Bot is a simple yet effective example that demonstrates how a bot can **reply directly to a specific message** in a GroupMe chat. When a user sends `!ping`, the bot responds with "Pong! 🏓" as a threaded reply, visually linking its response to the original command.
4 |
5 | This example is perfect for showing how to use the `reply` and `mention` attachments, which are core feature of the GroupMe API for creating interactive, conversational bots.
6 |
7 | ## Setup
8 |
9 | Before you can run PingPong Bot, you need a GroupMe bot and its associated Bot ID.
10 |
11 | 1. **Create a Bot:** The easiest way to create a bot is to use [GroupMe's Bot Creation form](https://dev.groupme.com/bots/new). Set a name and make sure to configure the **Callback URL** to point to your server (e.g., `https://your-server-url.com/callback`).
12 |
13 | 2. **Install Dependencies:** This script requires the `express` framework to run a web server. Create a new Node.js project and install it by running the following commands in your terminal:
14 |
15 | ```bash
16 | npm init -y
17 | npm install express
18 | ```
19 |
20 | 3. **Configure Bot ID:** You must add your Bot ID to the script. Open the `PingPong.js` file and replace the `[Your Bot ID Here]` placeholder with your actual Bot ID.
21 |
22 | You can read more about creating and managing bots in the [Bots API Reference](../api/bots/index.md).
23 |
24 | ## The `reply` Attachment
25 |
26 | Unlike a standard message, a reply visually links the bot's response to the user's original message in the chat. This is accomplished by including an `attachments` array in your post request with a `reply` type. The `reply` object must contain the `reply_id` of the message you are replying to. This creates a much more natural, threaded conversation flow.
27 |
28 | Replies have a unique identifier called `base_reply_id`, which is the ID of the original message. It basically goes unused by GroupMe as none of the current clients support it, but it should typically be set to the same value as `reply_id` for consistency (Or the first message in a reply chain if you know it, but sometimes it's a pain to find).
29 |
30 | ## The `mention` Attachment
31 |
32 | The `mention` attachment allows you to mention a user in your bot's response. This is useful for drawing attention to the user who sent the original message. The `user_ids` array should contain the ID of the user you want to mention, and the `loci` array specifies the range of text that will be highlighted as a mention, where the first number is the start index and the second number is the length of the mention text.
33 |
34 | For more information on how to use message attachements, see the [Attachments](../common/index.md#attachments) documentation.
35 |
36 | ## PingPong Bot Script
37 |
38 | This script creates a web server that listens for POST requests from GroupMe. It checks incoming messages for the `!ping` command. When it finds one, it extracts the original message's ID and uses it to send a threaded reply back to the group.
39 |
40 | You can save this code in a file named `PingPong.js` and run it using Node.js with this command:
41 |
42 | ```bash
43 | node PingPong.js
44 | ```
45 |
46 | Here's the corrected and documented code for PingPong Bot:
47 |
48 | ```js linenums="1" title="PingPong.js"
49 | const express = require('express'); // Import the express framework for handling HTTP requests
50 |
51 | const app = express();
52 | app.use(express.json());
53 |
54 | // --- Configuration ---
55 | const PORT = process.env.PORT || 3000; // Port for the bot to listen on
56 | const BOT_ID = '[Your Bot ID Here]'; // Your Bot ID from the GroupMe Developer Portal
57 | const CALLBACK_URL = '/callback'; // The URL path where GroupMe will send POST requests
58 |
59 | app.post(CALLBACK_URL, async (req, res) => {
60 | const message = req.body;
61 |
62 | // Basic validation to ensure we have the data we need.
63 | if (!message || !message.text) {
64 | return res.status(400).send('Invalid request');
65 | }
66 |
67 | const text = message.text.toLowerCase();
68 |
69 | if (text.startsWith('!ping')) {
70 | try {
71 | // Construct the API request payload, including the reply attachment.
72 | const payload = {
73 | bot_id: BOT_ID,
74 | text: message.name + " Pong! 🏓",
75 | attachments: [
76 | {
77 | type: 'mention', // This is optional, but can be used to mention the user who sent the original message.
78 | user_ids: [ message.sender_id ], // The ID of the user who sent the original message.
79 | loci: [0, message.name.length] // The range of the text to mention.
80 | },
81 | {
82 | type: 'reply',
83 | reply_id: message.id, // The ID of the message to reply to.
84 | base_reply_id: message.id, // The base message of the reply thread.
85 | }
86 | ],
87 | };
88 |
89 | await fetch('https://api.groupme.com/v3/bots/post', {
90 | method: 'POST',
91 | headers: { 'Content-Type': 'application/json' },
92 | body: JSON.stringify(payload), // The body must be a JSON string.
93 | });
94 |
95 | // Let GroupMe know the reply was successfully initiated.
96 | return res.status(202).send('Accepted');
97 |
98 | } catch (error) {
99 | console.error('Error sending pong:', error);
100 | return res.status(500).send('Failed to send pong');
101 | }
102 | }
103 |
104 | // If the message wasn't '!ping', acknowledge it with a 200 OK status.
105 | // This is important to prevent GroupMe from resending the same message.
106 | res.status(200).send('OK');
107 | });
108 |
109 | // Start the bot
110 | app.listen(PORT, () => console.log(`🏓 PingPong Bot running on port ${PORT}`));
111 | ```
--------------------------------------------------------------------------------
/docs/api/groups/campus.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Campups Directories"
3 | description: "Learn how to interact with GroupMe's university directories via the API."
4 | ---
5 |
6 | # Campus Directories
7 |
8 | > [!note]
9 | > These calls are exclusive to users who are already members of [campus directories](https://groupme.com/campus). For simplicity, this documentation will not cover how to join a campus domain via the API, but it does discuss how to navigate and manage one once you're a member.
10 |
11 | ## Index
12 |
13 | List information about the campus directory you're a member of
14 |
15 | ```json linenums="1" title="HTTP Request"
16 | GET /directories
17 | ```
18 |
19 | ```json linenums="1" title="HTTP Response"
20 | Status: 200 OK
21 | {
22 | "id": 1928,
23 | "name": "Brigham Young University",
24 | "avatar_url": "",
25 | "type": "school_directory",
26 | "color": "#00458D",
27 | "short_name": "BYU",
28 | "members_count": 10380,
29 | "groups_count": 170,
30 | "share_url": "https://groupme.com/join_community/1928/6rRBO8DD",
31 | "share_qr_code_url": "https://image.groupme.com/qr/join_community/1928/6rRBO8DD/preview"
32 | }
33 | ```
34 |
35 | ***
36 |
37 | ## Leave Domain
38 |
39 | Disconnect your account from a campus directory
40 |
41 | ```json linenums="1" title="HTTP Request"
42 | DELETE /directories/:directory_id/membership
43 | ```
44 |
45 | **Parameters**
46 |
47 | * *directory_id*
48 |
49 | String - the ID of the directory you're trying to leave
50 |
51 | ```json linenums="1" title="HTTP Response"
52 | Status: 200 OK
53 | []
54 | ```
55 |
56 | ***
57 |
58 | ## Index Groups
59 |
60 | Returns a paginated list of groups that are a part of the domain.
61 |
62 | > [!important]
63 | > This request is relative to `https://api.groupme.com/v1/`, not `https://api.groupme.com/v3/`.
64 |
65 | ```json linenums="1" title="HTTP Request"
66 | GET https://api.groupme.com/v1/search/directories
67 | ```
68 |
69 | **Parameters**
70 |
71 | * *per_page*
72 |
73 | Integer - Defines how many groups to return in the response. This value maxes out at 10,000 and defaults to 20 if omitted.
74 |
75 | * *from*
76 |
77 | Integer - The offset index to begin returning results from in the paginated list of groups. For example, from=0 returns results starting at the beginning, from=20 skips the first 20 groups. Defaults to 0 if omitted.
78 |
79 | ```json linenums="1" title="HTTP Response"
80 | Status: 200 OK
81 | {
82 | "directories": [
83 | {
84 | "id": "101838896",
85 | "name": "BYU Geology Club",
86 | "description": "New geoclub group chat!😊",
87 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.ec0495b9ac3f47b79f4ae279e1aeb4a5",
88 | "directory_id": "1928",
89 | "group_type": "private",
90 | "max_members": 5000,
91 | "members_count": 116,
92 | "children_count": 0
93 | },
94 | {
95 | "id": "104386559",
96 | "name": "BYU Wildlife and Range",
97 | "description": "🦅Wildlife and Range Student Association 🦌",
98 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.0d027c5a80934f22a2ac2e6204b9e563",
99 | "directory_id": "1928",
100 | "group_type": "private",
101 | "max_members": 5000,
102 | "members_count": 81,
103 | "children_count": 0
104 | }
105 | ...
106 | ]
107 | }
108 | ```
109 |
110 | ## Index Members
111 |
112 | Return a list containing all of the users who are visible to you within the campus directory. Note that the responses you get are also dependant on the privacy status of other users in the directory. Some people have their profiles set to be visible to only their major or graduation year, or hidden entirely.
113 |
114 | > [!important]
115 | > This request is relative to `https://api.groupme.com/v1/`, not `https://api.groupme.com/v3/`.
116 |
117 | ```json linenums="1" title="HTTP Request"
118 | GET https://api.groupme.com/v1/search/directory/users
119 | ```
120 |
121 | **Parameters**
122 |
123 | * *per_page*
124 |
125 | Integer - Defines how many groups to return in the response. This value maxes out at 10,000 and defaults to 20 if omitted.
126 |
127 | * *from*
128 |
129 | Integer - The offset index to begin returning results from in the paginated list of groups. For example, from=0 returns results starting at the beginning, from=20 skips the first 20 groups. Defaults to 0 if omitted.
130 |
131 | * *majors*
132 |
133 | Integer - A major code you'd like to filter by. The full dictionary of majors and their assigned codes can be found here: https://web.groupme.com/assets/majors/majors.en-US.json. You can include this parameter multiple times in one request to filter by multiple majors.
134 |
135 | * *graduation_year*
136 |
137 | Integer - A graduation year you'd like to filter users by.
138 |
139 | ```json linenums="1" title="HTTP Response"
140 | Status: 200 OK
141 | {
142 | "users": [
143 | {
144 | "id": "105436130",
145 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.63a049689246446ca2685764191ce7c3",
146 | "name": "Mikey",
147 | "bio": "",
148 | "graduation_year": "",
149 | "majors": ["3702"],
150 | "social_media_links": [],
151 | "shared_group_ids": null,
152 | "created_at": "2022-08-24T17:52:38+0000",
153 | "photo_urls": null,
154 | "song_url": "",
155 | "interests": null
156 | },
157 | {
158 | "id": "93031586",
159 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.4a1dca36b02744b0bac8cfe85a2d0331",
160 | "name": "Spencer",
161 | "bio": "",
162 | "graduation_year": "2024",
163 | "majors": ["3702"],
164 | "social_media_links": [],
165 | "shared_group_ids": null,
166 | "created_at": "2021-05-04T17:25:12+0000",
167 | "photo_urls": null,
168 | "song_url": "",
169 | "interests": null
170 | },
171 | ...
172 | ]
173 | }
174 | ```
175 |
176 | ***
177 |
--------------------------------------------------------------------------------
/src/overrides/assets/groupmejs_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GroupMe.JS
56 |
--------------------------------------------------------------------------------
/docs/contributing/styleguide.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Documentation Style Guide"
3 | description: "Guidelines and best practices for contributing to the GroupMe Community API documentation."
4 | ---
5 |
6 | # Documentation Style Guide
7 |
8 | Welcome, contributor! To ensure our documentation is clear, consistent, and easy to navigate, we've established this set of style guidelines. Following these standards will help everyone, from first-time readers to experienced developers, get the most out of this resource.
9 |
10 | ***
11 |
12 | ## General Philosophy
13 |
14 | * **Clarity is Key:** Write for a technical audience, but don't assume they know everything about the GroupMe API. Explain GroupMe-specific concepts clearly.
15 | * **Be Direct:** Use a professional and informative tone. Get straight to the point.
16 | * **Show, Don't Just Tell:** Every endpoint should be accompanied by clear request and response examples.
17 |
18 | ***
19 |
20 | ## Page Structure
21 |
22 | Every documentation page should follow a consistent structure.
23 |
24 | ### 1. YAML Frontmatter
25 |
26 | All pages must begin with a YAML frontmatter block. This provides metadata for the page. At a minimum, include a `title` and a `description`.
27 |
28 | ```yaml
29 | ---
30 | title: "Page Title"
31 | description: "A brief, one-sentence summary of the page's content."
32 | ---
33 | ```
34 |
35 | ***
36 |
37 | ### 2. Page Title
38 |
39 | The first element after the frontmatter must be a Level 1 Heading (`#`) that matches the `title` from the frontmatter.
40 |
41 | ```markdown
42 | # Page Title
43 | ```
44 |
45 | ***
46 |
47 | ### 3. API Boilerplate (For Endpoint Docs)
48 |
49 | For pages documenting API endpoints, include the standard boilerplate introduction immediately after the title. This sets the context for all subsequent requests. Use a horizontal rule (`***`) to separate it from the main content.
50 |
51 | ```markdown
52 | Unless otherwise stated, endpoints are relative to `https://api.groupme.com/v3/` and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
53 |
54 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
55 |
56 | Finally, all responses are wrapped in a response envelope of the following form:
57 |
58 | `"``json linenums="1"
59 | {
60 | "response": {
61 | "id": "12345",
62 | "name": "Family"
63 | ...
64 | },
65 | "meta": {
66 | "code": 200,
67 | "errors": []
68 | }
69 | }
70 | `"``
71 |
72 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
73 |
74 | ***
75 | ```
76 |
77 | ***
78 |
79 | ## Content Formatting
80 |
81 | ### Headings and Sections
82 |
83 | * **`# Heading 1`**: Only for the main page title.
84 | * **`## Heading 2`**: For major sections, typically a single API endpoint (e.g., "Create a poll", "Index Members").
85 | * **`***` (Horizontal Rule)**: Use a horizontal rule to separate each major endpoint section (`##`).
86 |
87 | ***
88 |
89 | ### Code Blocks
90 |
91 | Code blocks are essential for showing examples. Use the following format for consistency:
92 |
93 | ````markdown
94 | ```json linenums="1" title="HTTP Request"
95 | GET /groups/:group_id/messages
96 | ```
97 |
98 | ```json linenums="1" title="HTTP Response"
99 | Status: 200 OK
100 | {
101 | "count": 123,
102 | "messages": []
103 | }
104 | ```````
105 |
106 | * **Language:** Always specify the language (`json`, `js`, `bash`, etc.).
107 | * **Line Numbers:** Use `linenums="1"` to enable line numbering.
108 | * **Title:** Provide a `title` that describes the block (e.g., "HTTP Request", "HTTP Response", "Object Structure").
109 | * **Status Codes:** For `HTTP Response` blocks, include the status line (e.g., `Status: 200 OK`) as the first line.
110 | * **Indentation** We use 2 spaces for indentation in code blocks, not tabs. This ensures consistent formatting across different editors and viewers.
111 |
112 | ***
113 |
114 | ### Parameter Lists
115 |
116 | When describing parameters for an endpoint, use a bulleted list.
117 |
118 | * **Bold** the `Parameters` heading.
119 | * Italicize the parameter name (`*parameter_name*`).
120 | * Follow with the parameter's type (`string`, `boolean`, `array`, etc.) and a brief description.
121 | * Indicate if a parameter is `(required)` or `(optional)`.
122 |
123 | **Example:**
124 |
125 | ```markdown
126 | **Parameters**
127 |
128 | * *group_id* (required)
129 |
130 | string - The ID of the group where the event is located.
131 |
132 | * *limit* (optional)
133 |
134 | integer - The number of results to pull. Defaults to 20.
135 | ```
136 |
137 | ### Text Formatting
138 |
139 | * Use `**bold**` for emphasis on headings within text, such as **Parameters**.
140 | * Use `*italic*` for parameter names and for gentle emphasis on a word.
141 | * Use `` `inline code` `` for:
142 | * Endpoint paths: `` `/users/me` ``
143 | * Variable names: `source_guid`
144 | * Literal values: `true`, `"image"`, `null`
145 |
146 | ***
147 |
148 | ### Callouts (Admonitions)
149 |
150 | Use callouts to highlight important information.
151 |
152 | * `!!! note`: For helpful, non-critical information or tips.
153 | ```markdown
154 | !!! note
155 | This call is limited to admins and owners in the group.
156 | ```
157 |
158 | !!! note
159 | This call is limited to admins and owners in the group.
160 |
161 | * `!!! warning`: For non-breaking but important warnings users must be aware of.
162 | ```markdown
163 | !!! warning "Breaking Change: Device Verification"
164 | This endpoint now requires platform-level device verification...
165 | ```
166 |
167 | !!! warning "Breaking Change: Device Verification"
168 | This endpoint now requires platform-level device verification...
169 |
170 | * `!!! important`: For critical information that might otherwise be missed, especially regarding API versions.
171 | ```markdown
172 | !!! important
173 | This request is relative to `https://api.groupme.com/v4/`, not `https://api.groupme.com/v3/`.
174 | ```
175 |
176 | !!! important
177 | This request is relative to `https://api.groupme.com/v4/`, not `https://api.groupme.com/v3/`.
178 |
179 | ***
180 |
181 | Thank you for helping us keep the documentation clean and consistent!
182 |
183 | ***
--------------------------------------------------------------------------------
/src/overrides/assets/opengm_logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Open GM
64 |
--------------------------------------------------------------------------------
/src/overrides/assets/sprocket_colorized.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
77 |
--------------------------------------------------------------------------------
/docs/api/bots/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Webhook Bots"
3 | description: "Learn how to interact with GroupMe webhooks via the API."
4 | ---
5 |
6 | # Webhook Bots
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## Important notes
33 |
34 |
35 |
36 | The bots described in this page are what GroupMe describes as "bots". These bots have serious limitations, including but not limited to:
37 |
38 | 1. They cannot like messages
39 |
40 | 2. They cannot be in more than one group
41 |
42 | 3. They cannot be DM'd (Though they can be attached to their creator's DMs)
43 |
44 | 4. They cannot read previous messages or any other data in a group - they only have access to a message once, when it's sent to it via the callback URL.
45 |
46 | These limitations can produce incredibly powerful and interesting bots, and if your application can deal with these limitations, it's generally safer to use this style of bot. However, if your application needs to be able to do more than that, it may be a better idea to simply create a new user with a different email and phone number, and automate that account by connecting it to GroupMe's push service, and using the rest of the API to do things like create groups, send messages, etc. An example of this (Perhaps not a good example, but an example nonetheless) is [my primary bot, Lowes](https://github.com/2CATteam/gmuserbot). If you have a better, better-documented example, feel free to submit a pull request changing this section.
47 |
48 | Lastly, while GroupMe does offer API endpoints for creating, destroying, and indexing bots, and these are documented below, they also offer [a web form for managing them](https://dev.groupme.com/bots/), so you should probably use that to do that unless you need to automate these tasks. They also have an official [bot tutorial](https://dev.groupme.com/tutorials/bots) and a [bot sample project](https://github.com/groupme/bot-tutorial-nodejs), so if you're just beginning to play around with bots and APIs, you may find those useful.
49 |
50 | ***
51 |
52 | ## Create
53 |
54 |
55 | Create a bot. The response will include your bot_id - do NOT publish or let anyone else see this! Anyone with this will be able to send messages using your bot!
56 |
57 | ```json linenums="1" title="HTTP Request"
58 | POST /bots
59 | {
60 | "bot": {
61 | "name": "Dasani Bot",
62 | "group_id": "1234567890",
63 | "avatar_url": "https://assets.bwbx.io/images/users/iqjWHBFdfxIU/ipvgzU.b0q4M/v0/1000x-1.jpg",
64 | "callback_url": "https://example.herokuapp.com/",
65 | "dm_notification": false
66 | }
67 | }
68 | ```
69 |
70 | **Parameters**
71 |
72 | * *bot* (required)
73 |
74 | object - an object with the following properties:
75 |
76 | * *name* (required)
77 |
78 | string - the name the bot will show up with
79 |
80 | * *group_id* (required)
81 |
82 | string - the id of the chat the bot should be active in
83 |
84 | * *avatar_url*
85 |
86 | string - a URL to an image which will be the bot's avatar. This image MUST be proccessed by GroupMe's [Image Service](../uploads/images.md) before it can be sent.
87 |
88 | * *callback_url*
89 |
90 | string - if provided, whenever the bot receives a message, it will be sent as a POST request to this URL.
91 |
92 | ```json linenums="1" title="HTTP Response"
93 | Status: 201 Created
94 | {
95 | "bot_id": "1234567890",
96 | "group_id": "1234567890",
97 | "name": "Dasani Bot",
98 | "avatar_url": "https://assets.bwbx.io/images/users/iqjWHBFdfxIU/ipvgzU.b0q4M/v0/1000x-1.jpg",
99 | "callback_url": "https://example.herokuapp.com/",
100 | "dm_notification": false
101 | }
102 | ```
103 |
104 | ***
105 |
106 | ## Send Message
107 |
108 |
109 | Post a message from a bot
110 |
111 | ```json linenums="1" title="HTTP Request"
112 | POST /bots/post
113 | {
114 | "bot_id": "1234567890",
115 | "text": "Hello World",
116 | "attachments": [
117 | {
118 | "type": "image",
119 | "url": "https://i.groupme.com/123456789"
120 | }
121 | ]
122 | }
123 | ```
124 |
125 | **Parameters**
126 |
127 | * *bot_id* (required)
128 |
129 | string - the ID of the bot.
130 |
131 | * *text* (required)
132 |
133 | string - maximum length of 1000 character
134 |
135 | * *attachments*
136 |
137 | array - A polymorphic list of attachments (locations, images, replies, etc). You may have more than one of any type of attachment, provided clients can display it.
138 |
139 | For more information on types of attachments and how to send them, check out the [attachments documentation](../common/attachments.md)
140 |
141 | ```json linenums="1" title="HTTP Response"
142 | Status: 201 Created
143 | ```
144 |
145 | ***
146 |
147 | ## Index
148 |
149 |
150 | List bots that you have created. The response will include multiple `bot_id`s - do NOT publish these or let anyone else see these! Anyone with this will be able to send messages using your bot!
151 |
152 | ```json linenums="1" title="HTTP Request"
153 | GET /bots
154 | ```
155 |
156 | ```json linenums="1" title="HTTP Response"
157 | Status: 200 OK
158 | [
159 | {
160 | "bot_id": "1234567890",
161 | "group_id": "1234567890",
162 | "name": "hal9000",
163 | "avatar_url": "https://i.groupme.com/123456789",
164 | "callback_url": "https://example.com/bots/callback",
165 | "dm_notification": false
166 | }
167 | ]
168 | ```
169 |
170 | ***
171 |
172 | ## Destroy
173 |
174 |
175 | Remove a bot that you have created
176 |
177 | ```json linenums="1" title="HTTP Request"
178 | POST /bots/destroy
179 | ```
180 |
181 | **Parameters**
182 |
183 | * *bot_id* (required)
184 |
185 | ```json linenums="1" title="HTTP Response"
186 | Status: 200 OK
187 | ```
188 |
--------------------------------------------------------------------------------
/.github/assets/opengm_logo_colorized.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Open GM
95 |
--------------------------------------------------------------------------------
/src/overrides/assets/opengm_logo_colorized.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Open GM
95 |
--------------------------------------------------------------------------------
/src/overrides/assets/groupmejs_logo_colorized.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | GroupMe. JS
101 |
--------------------------------------------------------------------------------
/docs/api/conversations/reactions.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Reactions"
3 | description: "Learn how to interact with GroupMe's reaction system via the API."
4 | ---
5 |
6 | # Reactions
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## Like
33 |
34 |
35 | Like a message.
36 |
37 | ```json linenums="1" title="HTTP Request"
38 | POST /messages/:conversation_id/:message_id/like
39 | ```
40 |
41 | ```json linenums="1" title="HTTP Response"
42 | Status: 200 OK
43 | ```
44 |
45 | ***
46 |
47 | ## Reactions (Replacing likes in the new v7 update)
48 |
49 | React to a message with either a unicode or GroupMe emoji.
50 |
51 | GroupMe restricts reactions to [GroupMe powerups](../common/emoji.md) and the 15 unicode options displayed in the client (❤️ 👍 🤣 🎉 🔥 😮 👀 😭 🥺 🙏 💀 🫶 🤬 💅 🫠). Any other unicode emojis will be rejected by the server.
52 |
53 | There is no way to apply more than one reaction at a time to any given message, attempting to do so will overwrite the original reaction with the new one.
54 |
55 | ```json linenums="1" title="HTTP Request"
56 | POST /messages/:conversation_id/:message_id/like
57 | {
58 | "like_icon": {
59 | "type": "unicode",
60 | "code": "❤️"
61 | }
62 | }
63 | ```
64 |
65 | or
66 |
67 | ```json linenums="1" title="HTTP Request"
68 | POST /messages/:conversation_id/:message_id/like
69 | {
70 | "like_icon": {
71 | "type": "emoji",
72 | "pack_id": 1,
73 | "pack_index": 12
74 | }
75 | }
76 | ```
77 |
78 | **Parameters**
79 |
80 | * *like_icon* (optional)
81 | object — can contain reaction objects of type `unicode` (for standard unicode characters/emojis) or `emoji` for GroupMe emoji/powerups. `unicode` type reacions have a `code` parameter that specifies what text should be displayed. `emoji` type reactions have parameters `pack_id` and `pack_index`. See the [emoji documentation](../common/emoji.md) for more information on what these values mean.
82 |
83 | ```json linenums="1" title="HTTP Response"
84 | Status: 200 OK
85 | {
86 | "reactions": [
87 | {
88 | "type": "unicode",
89 | "pack_id": 0,
90 | "pack_index": 0,
91 | "code": "❤️"
92 | }
93 | ]
94 | }
95 | ```
96 |
97 | ***
98 |
99 | ## Unlike
100 |
101 |
102 | Unlike / remove your reactions from a message.
103 |
104 | ```json linenums="1" title="HTTP Request"
105 | POST /messages/:conversation_id/:message_id/unlike
106 | ```
107 |
108 | ```json linenums="1" title="HTTP Response"
109 | Status: 200 OK
110 | ```
111 |
112 | ***
113 |
114 | ## Leaderboard
115 |
116 | A list of the liked messages in the group for a given period of time. Messages are ranked in order of number of likes.
117 |
118 | ```json linenums="1" title="HTTP Request"
119 | GET /groups/:group_id/likes?period=
120 | ```
121 | **Parameters**
122 |
123 | * *period* (required)
124 | string — one of: 'day', 'week', or 'month'
125 |
126 | ```json linenums="1" title="HTTP Response"
127 | Status: 200 OK
128 | {
129 | "messages": [
130 | {
131 | "id": "1234567890",
132 | "source_guid": "GUID",
133 | "created_at": 1302623328,
134 | "user_id": "1234567890",
135 | "group_id": "1234567890",
136 | "name": "John",
137 | "avatar_url": "https://i.groupme.com/123456789",
138 | "text": "Hello world ",
139 | "system": true,
140 | "favorited_by": [
141 | "101",
142 | "66",
143 | "1234567890"
144 | ],
145 | "attachments": [
146 | {
147 | "type": "image",
148 | "url": "https://i.groupme.com/123456789"
149 | },
150 | {
151 | "type": "location",
152 | "lat": "40.738206",
153 | "lng": "-73.993285",
154 | "name": "GroupMe HQ"
155 | },
156 | {
157 | "type": "emoji",
158 | "placeholder": "",
159 | "charmap": [
160 | [1, 42],
161 | [2, 34]
162 | ]
163 | }
164 | ]
165 | },
166 | {
167 | "id": "1234567890",
168 | "source_guid": "GUID",
169 | "created_at": 1302623328,
170 | "user_id": "1234567890",
171 | "group_id": "1234567890",
172 | "name": "John",
173 | "avatar_url": "https://i.groupme.com/123456789",
174 | "text": "Hello world ",
175 | "system": true,
176 | "favorited_by": [
177 | "1",
178 | "2"
179 | ],
180 | "attachments": [
181 | {
182 | "type": "image",
183 | "url": "https://i.groupme.com/123456789"
184 | },
185 | {
186 | "type": "location",
187 | "lat": "40.738206",
188 | "lng": "-73.993285",
189 | "name": "GroupMe HQ"
190 | },
191 | {
192 | "type": "emoji",
193 | "placeholder": "",
194 | "charmap": [
195 | [1, 42],
196 | [2, 34]
197 | ]
198 | }
199 | ]
200 | }
201 | ]
202 | }
203 | ```
204 |
205 | ***
206 |
207 | ## My Likes
208 |
209 | A list of messages you have liked. Messages are returned in reverse chrono-order. Note that the payload includes a liked_at timestamp in ISO-8601 format.
210 |
211 | ```json linenums="1" title="HTTP Request"
212 | GET /groups/:group_id/likes/mine
213 | ```
214 |
215 | ```json linenums="1" title="HTTP Response"
216 | Status: 200 OK
217 | {
218 | "messages": [
219 | {
220 | "id": "1234567890",
221 | "source_guid": "GUID",
222 | "created_at": 1302623328,
223 | "user_id": "1234567890",
224 | "group_id": "1234567890",
225 | "name": "John",
226 | "avatar_url": "https://i.groupme.com/123456789",
227 | "text": "Hello world ",
228 | "system": true,
229 | "favorited_by": [
230 | "101",
231 | "66",
232 | "1234567890"
233 | ],
234 | "attachments": [
235 | {
236 | "type": "image",
237 | "url": "https://i.groupme.com/123456789"
238 | },
239 | {
240 | "type": "location",
241 | "lat": "40.738206",
242 | "lng": "-73.993285",
243 | "name": "GroupMe HQ"
244 | },
245 | {
246 | "type": "emoji",
247 | "placeholder": "",
248 | "charmap": [
249 | [1, 42],
250 | [2, 34]
251 | ]
252 | }
253 | ],
254 | "liked_at": "2014-05-08T18:30:31.6617Z"
255 | }
256 | ]
257 | }
258 | ```
259 |
260 | ***
261 |
262 | ## My Hits
263 | A list of messages others have liked.
264 |
265 | ```json linenums="1" title="HTTP Request"
266 | GET /groups/:group_id/likes/for_me
267 | ```
268 |
269 | ```json linenums="1" title="HTTP Response"
270 | Status: 200 OK
271 | {
272 | "messages": [
273 | {
274 | "id": "1234567890",
275 | "source_guid": "GUID",
276 | "created_at": 1302623328,
277 | "user_id": "1234567890",
278 | "group_id": "1234567890",
279 | "name": "John",
280 | "avatar_url": "https://i.groupme.com/123456789",
281 | "text": "Hello world ",
282 | "system": true,
283 | "favorited_by": [
284 | "101",
285 | "66",
286 | "1234567890"
287 | ],
288 | "attachments": [
289 | {
290 | "type": "image",
291 | "url": "https://i.groupme.com/123456789"
292 | },
293 | {
294 | "type": "location",
295 | "lat": "40.738206",
296 | "lng": "-73.993285",
297 | "name": "GroupMe HQ"
298 | },
299 | {
300 | "type": "emoji",
301 | "placeholder": "",
302 | "charmap": [
303 | [1, 42],
304 | [2, 34]
305 | ]
306 | }
307 | ]
308 | }
309 | ]
310 | }
311 | ```
312 |
--------------------------------------------------------------------------------
/docs/assets/stylesheets/admonitions.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --md-admonition-icon--groupme: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTExLjE2IDYuNTc0aDEuNjhWOC4xN2gtMS42OHpNMTguMiAwSDUuOEMzLjE1IDAgMSAyLjA0IDEgNC41NTZ2MTEuNzY4YzAgMi41MTYgMi4xNDkgNC41NTYgNC44IDQuNTU2aDQuMTI3TDExLjk4IDI0bDIuMDUyLTMuMTJIMTguMmMyLjY1IDAgNC44LTIuMDQgNC44LTQuNTU2VjQuNTU2QzIzIDIuMDQgMjAuODUgMCAxOC4yIDBNNy41NjggOC4xNjloMS43OFY2LjU3NGgtMS43OHYtMS43MmgxLjc4VjMuMTY2aDEuODEydjEuNjg4aDEuNjhWMy4xNjZoMS44MTJ2MS42ODhoMS43Nzl2MS43MmgtMS43OFY4LjE3aDEuNzh2MS43MmgtMS43OHYxLjY4OEgxMi44NFY5Ljg5aC0xLjY4djEuNjg4SDkuMzQ4VjkuODloLTEuNzh6bTEyLjc0NCA1LjI2M3MtMi4zOTIgNC4yNzYtOC4wNzEgNC4yNzZsLS4yMzQtLjAwMi0uMjMzLjAwMmMtNS42OCAwLTguMDcyLTQuMjc2LTguMDcyLTQuMjc2cy0uMTU1LS4yNzgtLjE1NS0uNTc5YS44My44MyAwIDAgMSAuMzk1LS43MSAxLjEgMS4xIDAgMCAxIC40Ni0uMTY3Yy42ODQtLjA3IDEuMDguMzUzIDEuNDA2LjgzNi4zNi41MzMgMi40MzYgMi44NjYgNi4xOTkgMi45NTUgMy43NjMtLjA4OSA1Ljg0LTIuNDIyIDYuMi0yLjk1NS4zMjUtLjQ4Mi43MzYtLjkwNiAxLjQwNi0uODM2LjE0Mi4wMTUuMjk2LjA2Ni40Ni4xNjcuMjkyLjE4My40MDYuNDQuMzk2LjcxLS4wMjMuMzk2LS4xNTcuNTgtLjE1Ny41OCIvPjwvc3ZnPg==');
3 | --md-admonition-icon--sprocket: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB3aWR0aD0iMTAwbW0iCiAgIGhlaWdodD0iMTAwbW0iCiAgIHZpZXdCb3g9IjAgMCAxMDAgMTAwIgogICB2ZXJzaW9uPSIxLjEiCiAgIGlkPSJzdmcxIgogICB4bWw6c3BhY2U9InByZXNlcnZlIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIxLjQgKDg2YThhZDcsIDIwMjQtMTAtMTEpIgogICBzb2RpcG9kaTpkb2NuYW1lPSJzcHJvY2tldC5zdmciCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJuYW1lZHZpZXcxIgogICAgIHBhZ2Vjb2xvcj0iIzUwNTA1MCIKICAgICBib3JkZXJjb2xvcj0iI2VlZWVlZSIKICAgICBib3JkZXJvcGFjaXR5PSIxIgogICAgIGlua3NjYXBlOnNob3dwYWdlc2hhZG93PSIwIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGlua3NjYXBlOnBhZ2VjaGVja2VyYm9hcmQ9IjAiCiAgICAgaW5rc2NhcGU6ZGVza2NvbG9yPSIjNTA1MDUwIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJtbSIKICAgICBpbmtzY2FwZTp6b29tPSIwLjQ2MjY4MjI0IgogICAgIGlua3NjYXBlOmN4PSI4ODAuNzM0MDQiCiAgICAgaW5rc2NhcGU6Y3k9Ijk1OC41NDEyMiIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjMwNzIiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTc5NiIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTEyIgogICAgIGlua3NjYXBlOndpbmRvdy15PSItMTIiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiIC8+PGRlZnMKICAgICBpZD0iZGVmczEiIC8+PGcKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIGlkPSJsYXllcjEiPjxwYXRoCiAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtb3BhY2l0eToxO3N0cm9rZS13aWR0aDowLjU2MzI2OCIKICAgICAgIGQ9Ik0gNDguNjk4NTU5LDk5Ljc4NDAyIEMgNDcuMDk0NDk0LDk5LjExNTU4NCA0Ni41NDk2MjMsOTguMzI2NzkzIDQ0Ljk4ODYwOCw5NC40MTMyNTQgNDEuNzg5NzU0LDg2LjM5MzU5OCA0MS40MTYxMDIsODYuMTcyNDQyIDMxLjA2MjMyNiw4Ni4xNzA3MjMgMjMuOTU0ODkxLDg2LjE2OTc0NiAyMi41ODk0MDgsODYuMDkwOTQyIDIwLjQ3MTUyNyw4NS41NjExMjUgMTIuNDcxMTYzLDgzLjU1OTcyMyA2LjE5ODY4MjEsNzYuMzI1NzQxIDUuMjU1Mjk0MSw2OC4wMTI0MDcgNC45NDU4MDUxLDY1LjI4NTEyNSA1LjEzMjUyMzEsMTguMDk1ODMyIDUuNDU4MjM0MSwxNi43MjMyNjUgNy41NDE5MDcxLDcuOTQyNTY0NSAxMy44MzIzNjEsMS42ODQzNjA0IDIyLjAwNDIxNywwLjI2MjExMTc1IGMgMi4wMjc2NTIsLTAuMzUyODk3NDcgNTMuNTg3MTYsLTAuMzQ3ODQ3MjcgNTUuNjkxOTI0LDAuMDA1NDYzIDguNDYwMDUxLDEuNDIwMDg4NjUgMTUuMzMzNzExLDguMjQwNzkyNzUgMTYuODg5MzExLDE2Ljc1OTE2ODI1IDAuNDM0MjQsMi4zNzc4OTYgMC40MTc1LDUwLjU0MzAxMyAtMC4wMTgzLDUyLjYwMjY0OCAtMS42OTg1Myw4LjAyNzY3MiAtNy41OTE4NSwxNC4xOTMyODkgLTE1LjMwMTQ2LDE2LjAwODQzMiAtMC41MDA0NTYsMC4xMTc4MjggLTEuMTExMTE4LDAuMjYxNzE0IC0xLjM1NzAxOSwwLjMxOTc2MSAtMC4yNDU5MDQsMC4wNTgwNCAtNC4wNzIyNTIsMC4xNDY3NTggLTguNTAzMDA0LDAuMTk3MTU5IC0xMC45MjYwMDYsMC4xMjQyODcgLTExLjQyMDg1OSwwLjM4OTY1OCAtMTQuMDY1OTk4LDcuNTQzMjAxIC0xLjU4NDU1Myw0LjI4NTI4NSAtMi4xMDI2OTUsNS4xNTQ5NTMgLTMuNDk5NTc0LDUuODczODE0IC0wLjkyNDI4NywwLjQ3NTY1MiAtMi4yODgzNDIsMC41Njc4MjIgLTMuMTQxNTU5LDAuMjEyMjcyIHogTSA1NC4yMTI3MTcsNzMuOTQ3ODY2IEMgNjYuNDUwOTI0LDcyLjU3OTE2OCA3OC4zNDE3MDEsNjUuMzAyMjk1IDgyLjQyNDg0Miw1Ni42ODI3MTcgODUuNTY3OTkyLDUwLjA0NzQ0OSA3OS4wODM0NDIsNDYuNjE2NjUzIDc1LjAzNTI3OCw1Mi43NzMxMDggNjMuOTIwNiw2OS42NzYzNDYgMzYuNDEwODYsNjkuNTIxMTEgMjUuMDAzMTgyLDUyLjQ5MDc3OCAyMi43NTk0MDYsNDkuMTQxMDg0IDIwLjQ0NTkxNiw0OC41MjIzOTUgMTguMDg4NzYxLDUwLjY0MTY4IGMgLTUuODE5NzI5LDUuMjMyNDM3IDcuMDM4NDU4LDE4LjQ5MzgyMiAyMS41MTc5MjcsMjIuMTkyNjY2IDQuNDY5MTc3LDEuMTQxNjcyIDEwLjM0NzMxOCwxLjU4OTgwOSAxNC42MDYwMjksMS4xMTM1MiB6IE0gNTMuMzcwNjU1LDUwLjYyMzg1OCBjIDAuNTMxMjE4LC0wLjQ0Njk5MyAwLjU4NDIwMywtMC42NTQzMjcgMC43NTQzOSwtMi45NTE5NzUgMC4xNTI5NzMsLTIuMDY1MjkzIDAuMzMwMjA1LC0yLjM1ODAwMiAxLjg3NjA0OSwtMy4wOTgzNjEgMS43MjY3MDMsLTAuODI2OTg1IDIuMDY1MDA2LC0wLjcxOTM2MiA0LjM1Njk1NSwxLjM4NjEwNSAxLjcxMzEsMS41NzM3MTUgMi4zMTIxNjgsMS40NjI4NzUgNC45MDcwNzIsLTAuOTA3ODk2IDIuODY5LC0yLjYyMTE5IDIuOTk3NTM3LC0zLjMwMzQwNSAxLjAxODc2LC01LjQwNjg2OCAtMS42MDEwMSwtMS43MDE4NzggLTEuNzU0MTkxLC0yLjEwOTM4IC0xLjMyOTkyNiwtMy41MzgwMjUgMC40ODI1NTEsLTEuNjI0OTI4IDEuMDc1Mjg5LC0yLjAwMTA4OCAzLjMwODgzMiwtMi4wOTk4MjggMy4xMjE2NjQsLTAuMTM4MDA2IDMuMzEzNzIzLC0wLjM5OTEyMSAzLjMwODgwMSwtNC40OTgzNzEgLTAuMDA1MSwtNC4wOTA1MDIgLTAuMjQ2Njc4LC00LjQzOTYyMSAtMy4wNzQ1NDMsLTQuNDM5NjIxIC0zLjcxNzM3NywwIC00Ljk1Mjk0NSwtMi45NDMwODIgLTIuMzcyNzI2LC01LjY1MTc0OSAxLjk2NzE3OSwtMi4wNjUxMDkgMS45MzAzNTMsLTIuNzg2OTYzIC0wLjI1Njg3MywtNS4wMzUwNTggLTIuOTg5Mzc1LC0zLjA3MjU2MyAtMy42NjY5NzUsLTMuMTY3OTA2IC02LjE5MzQ1NywtMC44NzE0NjEgLTEuNjI5MzAzLDEuNDgwOTU2IC0yLjI5MDk4NSwxLjYxNDk5MSAtMy45MDM3MDIsMC43OTA3NTYgLTEuMzg3MDkxLC0wLjcwODkyMSAtMS40OTE5MzksLTAuOTEyNzU2IC0xLjcwMzYzNiwtMy4zMTE5NzQgLTAuMjcyMzAxLC0zLjA4NjA3MTYgLTAuMzk3MjgsLTMuMTc5MTUgLTQuMjkyNDAxLC0zLjE5NjgwNzYgLTMuNzc1MTI1LC0wLjAxNzExOSAtMy45NjIyOTUsMC4xMjg2OTAxIC00LjEzMDY2LDMuMjE3ODExNiAtMC4xMTQ0MDQsMi4wOTkwNDIgLTAuMjE4NDk2LDIuMzMyMTAxIC0xLjM4MzAzMSwzLjA5NjUwNiAtMS41NTgxMTUsMS4wMjI3NTYgLTIuNDQ3MDM5LDAuOTE3Njg5IC00LjA1NTY5OSwtMC40NzkzNjQgLTIuNDcxNzQ1LC0yLjE0NjYwNiAtMi45NDM1MywtMi4yMjQwNTQgLTQuNTE5NjkyLC0wLjc0MTk3IC00LjMzODMxOCw0LjA3OTM3MiAtNC4zMDk4MzYsMy45NzQ2ODkgLTEuNzk3NTg2LDYuNjA2NTEgMS4xMjEyOTcsMS4xNzQ2NjcgMS4yNDkxMzMsMS40ODU3MTQgMS4xMzM3MTUsMi43NTg1NTkgLTAuMTg2OTY1LDIuMDYxODgzIC0xLjAwMjU5LDIuNjk1MTc2IC0zLjYyMTk3OSwyLjgxMjI4NiAtMy4wMjI3MzgsMC4xMzUxMzggLTMuMjQ1NTc4LDAuNDc1NzY5IC0zLjE2Njg0Nyw0Ljg0MDgwOCAwLjA2ODMxLDMuNzg3MTI5IDAuMjA0Mjg5LDMuOTUwOTU5IDMuNDAyLDQuMDk4Njk0IDIuMzAzMDY2LDAuMTA2NDA2IDIuNzMzODcxLDAuMzYzNzYzIDMuMzc5MTI1LDIuMDE4NjUyIDAuNTE4MjExLDEuMzI5MDU5IDAuMjA2OTcxLDIuMjEzODY3IC0xLjI5NTI0NCwzLjY4MjE2IC0yLjE3NTY0OSwyLjEyNjUyNCAtMi4wNzQ3NjIsMi42NTE4NTYgMS4wNzc1NjIsNS42MTEwMDQgMi40MTI4MDEsMi4yNjQ5NCAyLjk0NDAyNCwyLjI5MDcyNyA1LjA0NjI1OCwwLjI0NDk0OSAxLjY4Mjk2NywtMS42Mzc3NzUgMi4yNTM2NTQsLTEuNzU5MTA5IDQuMDIzNDA0LC0wLjg1NTQzMyAxLjU0Mzk4NywwLjc4ODM4NiAxLjY0NzczMSwwLjk3NDA3NiAxLjc4NDM2OCwzLjE5MzY3NyAwLjE5MzM0NywzLjE0MDg0NCAwLjEyMTUwMywzLjA4ODY5NiA0LjI2NjY4OSwzLjA5NjkyNiAzLjAwMTk3MSwwLjAwNiAzLjAwNzExMywwLjAwNTYgMy40NTQwMjIsLTAuMzcwNjQyIHogTSA0Ny43NDY1OTIsMzcuNzk5MTk0IGMgLTYuMzI1MDg2LC0xLjcwMTAzMiAtOC4xODE5OTQsLTkuNzQwMDQxIC0zLjI5NDk1OSwtMTQuMjY0NjgyIDUuMzcxNzE3LC00Ljk3MzM5NCAxNC4wMDg0NzMsLTEuMTc4OTIyIDE0LjAwODQ3Myw2LjE1NDQ4IDAsNS41MTY0MiAtNS4zMzY1NjksOS41NTYyNDEgLTEwLjcxMzUxNCw4LjExMDIwMiB6IG0gLTI1LjMwNDI1NCw1LjE0MjgzNCBjIDIuNzU2MTQ2LC0wLjc2NTM0IDMuMjUxNDE4LC00LjgyODA1MyAwLjc5MjQ5NiwtNi41MDA4NTggLTIuNjI1MjU2LC0xLjc4NTk1MSAtNi4xNTU3LDAuNjIzMjQgLTUuNDk5MjgxLDMuNzUyNzQgMC40NjA0MjIsMi4xOTUwNzMgMi40NzA5NzgsMy4zNjg5NjMgNC43MDY3ODUsMi43NDgxMTggeiBtIDU3LjkzNjk1NCwtMC4wMTAzMSBjIDIuNDkxNzYsLTAuNzQ2NTUxIDMuMTY1NjEsLTQuMTA0MjY0IDEuMjE1MTEsLTYuMDU0NzY2IC0yLjMyODI1LC0yLjMyODI1IC02LjE4MTMzMywtMC40NjcxNDYgLTUuOTY4MDM1LDIuODgyNjY0IDAuMTUyMDM4LDIuMzg3Njc2IDIuMzg4NzEzLDMuODgwNDM4IDQuNzUyOTI1LDMuMTcyMTAyIHogTSAyMy4wNzI1LDI2LjI1Njc0IGMgMy4wOTA1MzEsLTEuOTEwMDUyIDEuNzk1NzU2LC02LjcwMTg4IC0xLjgxMDg4NSwtNi43MDE4OCAtMy4wMzEyODEsMCAtNC43NTI0OTcsMy4xNjIyMjYgLTMuMTAyMDkzLDUuNjk5MTczIDAuOTYwNDM3LDEuNDc2MzYgMy4zNTU2MywxLjk2NTIwMyA0LjkxMjk3OCwxLjAwMjcwNyB6IG0gNTcuNjkwMTcyLDAuMDYxMzUgYyAzLjI3NDYzLC0xLjU0NTQ3NiAyLjI2OTkxLC02LjUwNDg5NiAtMS4zNjQ3LC02LjczNjMzIC0yLjIyMzkwMSwtMC4xNDE2MTMgLTMuNzk0MTQyLDEuMzYwMjI5IC0zLjc5NzgxLDMuNjMyMzY5IC0wLjAwNDUsMi43OTEwODggMi41NTkyNzgsNC4zMzI1NjcgNS4xNjI1MSwzLjEwMzk2MSB6IgogICAgICAgaWQ9InBhdGgxIiAvPjwvZz48L3N2Zz4K');
4 | }
5 |
6 | /* noicon admonition */
7 | .md-typeset .noicon > .admonition-title::before,
8 | .md-typeset .noicon > summary::before {
9 | display: none;
10 | }
11 |
12 | .md-typeset .noicon > .admonition-title,
13 | .md-typeset .noicon > summary {
14 | padding-left: 0.8em; /* match body text */
15 | }
16 |
17 | /* Optional: remove border radius if needed */
18 | .md-typeset .noicon > .admonition-title,
19 | .md-typeset .noicon > summary {
20 | border-radius: 0.2em 0.2em 0 0;
21 | }
22 |
23 | .md-typeset .admonition.noicon,
24 | .md-typeset details.noicon {
25 | border-color: rgb(158, 158, 158);
26 | }
27 |
28 | .md-typeset .noicon > .admonition-title,
29 | .md-typeset .noicon > summary {
30 | background-color: rgba(39, 42, 53, 0.1);
31 | }
32 |
33 |
34 | /* groupme admonition */
35 | .md-typeset .admonition.groupme,
36 | .md-typeset details.groupme {
37 | border-color: rgb(2, 166, 242);
38 | }
39 | .md-typeset .groupme > .admonition-title,
40 | .md-typeset .groupme > summary {
41 | background-color: rgba(2, 166, 242, 0.1);
42 | }
43 | .md-typeset .groupme > .admonition-title::before,
44 | .md-typeset .groupme > summary::before {
45 | background-color: rgb(2, 166, 242);
46 | -webkit-mask-image: var(--md-admonition-icon--groupme);
47 | mask-image: var(--md-admonition-icon--groupme);
48 | }
49 |
50 | /* sprocket admonition */
51 | .md-typeset .admonition.sprocket,
52 | .md-typeset details.sprocket {
53 | border-color: rgb(2, 166, 242);
54 | }
55 | .md-typeset .sprocket > .admonition-title,
56 | .md-typeset .sprocket > summary {
57 | background-color: rgba(2, 166, 242, 0.1);
58 | }
59 | .md-typeset .sprocket > .admonition-title::before,
60 | .md-typeset .sprocket > summary::before {
61 | background-color: rgb(2, 166, 242);
62 | -webkit-mask-image: var(--md-admonition-icon--sprocket);
63 | mask-image: var(--md-admonition-icon--sprocket);
64 | }
65 |
--------------------------------------------------------------------------------
/docs/api/groups/subgroups.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Subtopics"
3 | description: "Learn how to interact with GroupMe's subtopic system via the API."
4 | ---
5 |
6 | # Subtopics
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## Index
33 |
34 | List the authenticated user's active subgroups under a certain parent group.
35 |
36 | ```json linenums="1" title="HTTP Request"
37 | GET /groups/:group_id/subgroups
38 | ```
39 |
40 | **Parameters**
41 |
42 | The following parameters are assumed to match the behavior of `groups`. More testing is needed to determine if these default values match up exactly, so take these as a best guess.
43 |
44 | * *group_id* (required)
45 |
46 | string - the ID of the parent group to get the subgroups for
47 |
48 | * *page*
49 |
50 | integer - Fetch a particular page of results. Defaults to 1.
51 |
52 | * *per_page*
53 |
54 | integer - Define page size. Defaults to 10.
55 |
56 |
57 | ```json linenums="1" title="HTTP Response"
58 | Status: 200 OK
59 | [
60 | {
61 | "messages": {
62 | "count": 4,
63 | "last_message_id": "1234567890987654321",
64 | "last_message_created_at": 1715574721,
65 | "last_message_updated_at": 1715574721,
66 | "preview": {
67 | "nickname": "Bob Doe",
68 | "text": "Hello everyone!",
69 | "image_url": "https://i.groupme.com/1024x1024.jpeg.123456789e9876543211234567e",
70 | "attachments": [
71 | {
72 | "type": "image",
73 | "url": "https://i.groupme.com/123456789"
74 | },
75 | {
76 | "type": "image",
77 | "url": "https://i.groupme.com/123456789"
78 | }
79 | ]
80 | }
81 | },
82 | "id": 123456789,
83 | "parent_id": 123123123,
84 | "topic": "Test Topic 1",
85 | "description": "This is a testing topic",
86 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.123456789a9876543211234567a",
87 | "creator_user_id": 12345678,
88 | "created_at": 1715574084,
89 | "updated_at": 1715574721,
90 | "muted_until": null,
91 | "like_icon": null,
92 | "unread_count": null,
93 | "last_read_message_id": null,
94 | "last_read_at": null
95 | }
96 | ]
97 | ```
98 |
99 | ***
100 |
101 | ## Show
102 |
103 | Load a specific subgroup within a parent group.
104 |
105 | ```json linenums="1" title="HTTP Request"
106 | GET /groups/:group_id/subgroups/:subgroup_id
107 | ```
108 |
109 | **Parameters**
110 |
111 | * *group_id* (required)
112 |
113 | string - the ID of the parent group to get the subgroup for
114 |
115 | * *subgroup_id* (required)
116 |
117 | string - the ID of the subgroup to show details of
118 |
119 | ```json linenums="1" title="HTTP Response"
120 | Status: 200 OK
121 | {
122 | "messages": {
123 | "count": 18,
124 | "last_message_id": "1234567890987654321",
125 | "last_message_created_at": 1715574721,
126 | "last_message_updated_at": 1715574721,
127 | "preview": {
128 | "nickname": "Jane Doe",
129 | "text": "Hey everyone!",
130 | "image_url": "https://i.groupme.com/1024x1024.jpeg.eabcdefg1234567654321eabcdefg",
131 | "attachments": [
132 | {
133 | "type": "image",
134 | "url": "https://i.groupme.com/123456789"
135 | },
136 | {
137 | "type": "image",
138 | "url": "https://i.groupme.com/123456789"
139 | }
140 | ]
141 | }
142 | },
143 | "id": 123456789,
144 | "parent_id": 987654321,
145 | "topic": "Test Topic",
146 | "description": "This is a testing topic",
147 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.iabcdefg1234567654321iabcdefg",
148 | "creator_user_id": 123123123,
149 | "created_at": 1715574084,
150 | "updated_at": 1715574721,
151 | "muted_until": null,
152 | "like_icon": null,
153 | "unread_count": null,
154 | "last_read_message_id": null,
155 | "last_read_at": null
156 | }
157 | ```
158 |
159 | ***
160 |
161 | ## Create
162 |
163 | Create a topic. You must be an admin in the group to make this call.
164 |
165 | ```json linenums="1" title="HTTP Request"
166 | POST /groups/:group_id/subgroups
167 | {
168 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.679caf2a3dc04bd884137065e567047f",
169 | "description": "this is a description",
170 | "group_type": "announcement",
171 | "topic": "test topic"
172 | }
173 | ```
174 |
175 | **Parameters**
176 |
177 | * *group_id* (required)
178 |
179 | string - the ID of the parent group to get the subgroup for
180 |
181 | * *avatar_url*
182 |
183 | string - an Image URL for the topic processed by GroupMe's Image Service
184 |
185 | * *description*
186 |
187 | string - the description for the topic
188 |
189 | * *group_type*
190 |
191 | string - can be either `"private"` (anyone can post to this topic) or `"announcement"` (only admins can post to this topic)
192 |
193 | * *topic*
194 |
195 | string - the name of the new topic
196 |
197 | ```json linenums="1" title="HTTP Response"
198 | Status: 201 Accepted
199 | {
200 | "id": 107877040,
201 | "topic": "test topic",
202 | "type": "announcement",
203 | "description": "this is a description",
204 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.679caf2a3dc04bd884137065e567047f",
205 | "created_at": 1748457450,
206 | "updated_at": 1748457450,
207 | "parent_id": 107876923,
208 | "like_icon": null
209 | }
210 | ```
211 |
212 | ***
213 |
214 | ## Update
215 |
216 | Update a topic's details
217 |
218 | ```json linenums="1" title="HTTP Request"
219 | PUT /groups/:group_id/subgroups/:subgroup_id
220 | {
221 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.679caf2a3dc04bd884137065e567047f",
222 | "description": "this is a new description",
223 | "group_type": "private",
224 | "topic": "new name",
225 | "like_icon": {
226 | "pack_id": 1,
227 | "pack_index": 49,
228 | "type": "emoji"
229 | }
230 | }
231 | ```
232 |
233 | **Parameters**
234 |
235 | * *group_id* (required)
236 |
237 | string - the ID of the parent group to get the subgroup for
238 |
239 | * *subgroup_id* (required)
240 |
241 | string - the ID of the topic you want to update
242 |
243 | * *avatar_url*
244 |
245 | string - an Image URL for the topic processed by GroupMe's Image Service
246 |
247 | * *description*
248 |
249 | string - the description for the topic
250 |
251 | * *group_type*
252 |
253 | string - can be either `"private"` (anyone can post to this topic) or `"announcement"` (only admins can post to this topic)
254 |
255 | * *topic*
256 |
257 | string - the name of the new topic
258 |
259 | * *like icon*
260 |
261 | object - The GroupMe powerup emoji to set as the group's like icon. See the [emoji documentation](../common/emoji.md) for more information on what these values mean.
262 |
263 | ```json linenums="1" title="HTTP Response"
264 | {
265 | "id": 107877040,
266 | "topic": "new name",
267 | "type": "private",
268 | "description": "this is a new description",
269 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.742b941f7eea46998438cee6838268ec",
270 | "created_at": 1748457450,
271 | "updated_at": 1748457988,
272 | "parent_id": 107876923,
273 | "like_icon": {
274 | "pack_id": 1,
275 | "pack_index": 49,
276 | "type": "emoji"
277 | }
278 | }
279 | ```
280 |
281 | ***
282 |
283 | ## Delete
284 |
285 | Delete a topic
286 |
287 | ```json linenums="1" title="HTTP Request"
288 | DELETE /groups/:group_id/subgroups/:subgroup_id
289 | ```
290 |
291 | **Parameters**
292 |
293 | * *group_id* (required)
294 |
295 | string - the ID of the parent group to get the subgroup for
296 |
297 | * *subgroup_id* (required)
298 |
299 | string - the ID of the topic you want to update
300 |
301 | ```json linenums="1" title="HTTP Response"
302 | Status: 200 OK
303 | ```
304 |
305 | ## Mute/Unmute a specific Topic
306 |
307 | Silence general notifications for the main chat in a group, as well as all of the subtopics. This does not silence @mentions, replies, or reaction notifications for your own messages.
308 |
309 | Both calls return your member object for the group.
310 |
311 | ```json linenums="1" title="HTTP Request (To mute)"
312 | POST /groups/:group_id/subgroups/:subgroup_id/mute
313 | {
314 | "duration": 60
315 | }
316 | ```
317 |
318 | **Parameters**
319 |
320 | * *duration* (required)
321 |
322 | string - The length of time (in minutes) you want notifications to be silent for. To silence notifications until you enable them again, use `null`.
323 |
324 | ```json linenums="1" title="HTTP Request (To unmute)"
325 | POST /groups/:group_id/subgroups/:subgroup_id/unmute
326 | ```
327 |
328 | ```json linenums="1" title="HTTP Response (For both muting and unmuting)"
329 | Status: 200 OK
330 | {
331 | "membership": {
332 | "id": "1080225494",
333 | "user_id": "93645911",
334 | "country_code": "1",
335 | "phone_number": "3192414622",
336 | "email": "stanger.isaac@gmail.com",
337 | "avatar_url": "https://i.groupme.com/200x200.jpeg.94e0ac5891aa4e6f8ad4bbf961defe4d",
338 | "nickname": "Isaac",
339 | "creator": true,
340 | "muted": false,
341 | "snoozed": false,
342 | "has_sound_enabled": true,
343 | "pending": false,
344 | "muted_until": null,
345 | "muted_children": {
346 | "107933452": 253402300800
347 | }
348 | }
349 | }
350 | ```
351 |
352 | ***
353 |
--------------------------------------------------------------------------------
/docs/api/common/attachments.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Message Attachments"
3 | description: "Learn how to interact with GroupMe's message attachments via the API."
4 | ---
5 |
6 | # Attachments
7 |
8 | When sending messages in groups or DM channels, users (or bots) can add any number of attachments, as noted in the relevant sections of [Group Messages](../groups/messages.md), [Direct Messages](../dms/index.md), and [Bots](../bots/index.md). In each of these instances, you add an attachment by including a corresponding object in the "attachments" array. This section will list all the different types of attachments possible (That we know of and can find documentation for) and how to send them.
9 |
10 | All parameters are required unless otherwise specified.
11 |
12 | ***
13 |
14 | ## Images
15 |
16 | ```json linenums="1" title="Object Structure"
17 | {
18 | "type": "image",
19 | "url": "https://i.groupme.com/123456789"
20 | }
21 | ```
22 |
23 | **Parameters**
24 |
25 | * *type*
26 |
27 | string - must be "image" for an image attachment
28 |
29 | * *url*
30 |
31 | string - the URL of the image to send. This image must first be processed by GroupMe's [Image Service](../uploads/images.md)
32 |
33 | ***
34 |
35 | ## Video
36 |
37 | ```json linenums="1" title="Object Structure"
38 | {
39 | "type": "video",
40 | "url": "https://v.groupme.com/123456/2021-12-11T16:33:43Z/9999a999.1280x720r90.mp4",
41 | "preview_url": "https://v.groupme.com/123456/2021-12-11T16:33:43Z/9999a999.1280x720r90.jpg"
42 | }
43 | ```
44 | **Parameters**
45 |
46 | - *type*
47 |
48 | string - must be "video" for a video attachment
49 |
50 | - *url*
51 |
52 | string - URL to video file, needs to be a `v.groupme.com` link.
53 |
54 | - *preview_url*
55 |
56 | string - URL to video file thumbnail, again it does need to `v.groupme.com` link. Note that thumbnail images produced by the video service are not `i.groupme.com` links.
57 |
58 | ***
59 |
60 | ## File
61 |
62 | ```json linenums="1" title="Object Structure"
63 | {
64 | "type":"file",
65 | "file_id":"abcdabcd-dead-beef-2222-111122223333"
66 | }
67 | ```
68 | **Parameters**
69 |
70 | - *type*
71 |
72 | string - must be "file" for a file attachment
73 |
74 | - *url*
75 |
76 | file_id - valid file id from the [file service](../uploads/files.md)
77 |
78 | ***
79 |
80 | ## Location
81 |
82 | ```json linenums="1" title="Object Structure"
83 | {
84 | "type": "location",
85 | "name": "Heaven?",
86 | "lat": "64.148430",
87 | "lng": "-21.9355508"
88 | }
89 | ```
90 |
91 | **Parameters**
92 |
93 | * *type*
94 |
95 | string - must be "location" for a location attachment
96 |
97 | * *name*
98 |
99 | string - the name of the location you're sending
100 |
101 | * *lat*
102 |
103 | string - the latitude of the location
104 |
105 | * *lng*
106 |
107 | string - the longitude of the location.
108 |
109 | ***
110 |
111 | ## Emoji
112 |
113 | Only for GroupMe-specific emoji. Standard Unicode emoji (e.g. 💩) do not use this feature.
114 |
115 | ```json linenums="1" title="Object Structure"
116 | {
117 | "type": "emoji",
118 | "placeholder": "�",
119 | "charmap": [[1, 62]]
120 | }
121 | ```
122 |
123 | **Parameters**
124 |
125 | * *type*
126 |
127 | string - must be "emoji" for an emoji attachment
128 |
129 | * *placeholder*
130 |
131 | string - a placeholder character in the text which is meant to be replaced by the actual emoji.
132 |
133 | * This can technically be more than one character, but different platforms are inconsistent about how they display the resulting message - some will delete the entire placeholder string, others will only delete the first character
134 |
135 | * *charmap*
136 |
137 | array - an array of arrays of two integers. That is, an array of the form `[[1, 62], [2, 0]]`.
138 |
139 | * Each number pair represents one emoji, in the order the placeholder character appears.
140 |
141 | * The first number is the number of the emoji pack the emoji is found in (starting at one), and the second number is the position of the emoji in the array (starting at zero)
142 |
143 | * The first placeholder will be replaced by the emoji indicated by the first charmap, the second placeholder will be replaced by the second, and so on.
144 |
145 | * If there are more placeholder characters than there are charmaps, platforms are inconsistent on how they handle it. Some will render the placeholder, some will render a random emoji.
146 |
147 | * If there are fewer placeholder characters than there are charmaps, the extra charmaps will simply be discarded.
148 |
149 | ***
150 |
151 | ## Replies
152 |
153 | Designates a message as a reply to a previous message
154 |
155 | ```json linenums="1" title="Object Structure"
156 | {
157 | "type": "reply",
158 | "reply_id": "123456789",
159 | "base_reply_id": "123456789"
160 | }
161 | ```
162 |
163 | **Parameters**
164 |
165 | * *type*
166 |
167 | string - must be "reply" for a reply attachment
168 |
169 | * *reply_id*
170 |
171 | string - the ID of the message you're responding to. Must be greater than or equal to base_reply_id.
172 |
173 | * This can be omitted, but will result in some clients being inconsistent of if they recognize it as a reply or not.
174 |
175 | * If this is included, and reply_id and base_reply_id differ, this takes precedence
176 |
177 | * *base_reply_id*
178 |
179 | string - the ID of the message you're responding to.
180 |
181 | ***
182 |
183 | ## Mentions
184 |
185 | This is how you @mention someone.
186 |
187 | ```json linenums="1" title="Object Structure"
188 | {
189 | "type": "mentions",
190 | "user_ids": ["123456789", "1234567890"],
191 | "loci": [[0, 6], [8, 6]]
192 | }
193 | ```
194 |
195 | **Parameters**
196 |
197 | * *type*
198 |
199 | string - must be "mentions" for a mention attachment
200 |
201 | * *user_ids*
202 |
203 | array - an array of the user IDs being mentioned.
204 |
205 | * *loci*
206 |
207 | array - an array of arrays of two integers. That is, an array of the form `[[0, 6], [8, 6]]`
208 |
209 | * Each number pair represents a mention to the person specified in the associated `user_ids` element
210 |
211 | * The first number represents the position in the text where the mention begins, and the second represents how long it is.
212 |
213 | So, for example, in the message "Hi @Lowes", the first number would be 3, and the second number would be 6.
214 |
215 | Interestingly, because of this system, you don't have to actually type someone's name, or even type @, for someone to be mentioned. There's also no limit on how many people you can @mention at once. My bot, Lowes, has a function which simply says "@all" and everyone in the chat gets the notification that they've been mentioned.
216 |
217 | ***
218 |
219 | ## Read Only Attachments
220 |
221 | > [!note]
222 | > These attachment types cannot be sent in a message's attachments array and can only be observed through reading messages that have been sent in groups or direct messages. Most are added by the backend automatically.
223 |
224 | ***
225 |
226 | ## Poll
227 |
228 | This is a read-only attachment type, as it is not sent in one of your messages. Rather, when you create a poll, a message with this attachment is sent for you.
229 |
230 | Read more about polls [here](../groups/polls.md)
231 |
232 | ```json linenums="1" title="Object Structure"
233 | {
234 | "type": "poll",
235 | "poll_id": "1747858596203713"
236 | }
237 | ```
238 |
239 | **Parameters**
240 |
241 | * *type*
242 |
243 | string - must be "poll" for a poll attachment
244 |
245 | * *pool_id*
246 |
247 | string - the ID of the poll attached to the message
248 |
249 | ***
250 |
251 | ## Calendar Event
252 |
253 | This is a read-only attachment type, as it is not sent in one of your messages. Rather, when you create an event, a message with this attachment is sent for you.
254 |
255 | Read more about calendar events [here](../conversations/calendar.md)
256 |
257 | ```json linenums="1" title="Object Structure"
258 | {
259 | "type": "event"
260 | "event_id": "912fea48717643eda831e72306557100",
261 | "view": "full",
262 | }
263 | ```
264 |
265 | * *type*
266 |
267 | string - must be "event" for a callendar event attachment
268 |
269 | * *event_id*
270 |
271 | string - the ID of the event attached to the message
272 |
273 | * *view*
274 |
275 | string - describes the way the event should be rendered in chat. This value has not been observed to be anything other than `"full"` so far.
276 |
277 | ## Copilot
278 |
279 | This attachment type is read-only and used exclusively by Copilot in its messages to attach extra information about the Copilot interaction and the user that requested it.
280 |
281 | ```json linenums="1" title="Object Structure"
282 | {
283 | "type": "copilot"
284 | "message_id": "u6Us5bXBSQERTNfc6vWGB",
285 | "part_id": "0",
286 | "prompt_sender": "93645911",
287 | }
288 | ```
289 |
290 | **Parameters**
291 |
292 | * *type*
293 |
294 | string - must be "copilot" for a Copilot attachment
295 |
296 | * *message_id*
297 |
298 | string - Copilot specific internal message ID, not the same as the GroupMe message ID
299 |
300 | * *part_id*
301 |
302 | string - the index of the message in the Coplilot response. Copilot can respond to a single query using multiple messages, this allows you to order them if there are more than one.
303 |
304 | * *prompt_sender*
305 |
306 | string - the GroupMe user ID of the user who initiated the Copilot interaction
307 |
308 | ***
309 |
310 | ## Partial Image
311 |
312 | This attachment type is read-only and used exclusively by Copilot in its messages to attach partial images as they are being generated. Copilot will edit its messages containing in-progress images as they finish. When an image is complete, its partial image attachement is swapped for a regular image attachment containing a CDN URL.
313 |
314 | ```json linenums="1" title="Object Structure"
315 | {
316 | "type": "partial_image",
317 | "id": "1",
318 | "content": "9j6zLfSlAXAAAAAAAAADLVanVtYgAAAB5qdW1kYzJwYQARABCAAACqADibcQNjMnBhAAAAMq9qdW1..."
319 | }
320 | ```
321 |
322 | **Parameters**
323 |
324 | * *type*
325 |
326 | string - must be "partial_image" for a partial image attachment.
327 |
328 | * *id*
329 |
330 | string - This number increments for every edit copilot makes to the image.
331 |
332 | * *content* (optional)
333 |
334 | string - binary image data of the partial image. If this is missing, GroupMe will automatically supply a blank shimmering placeholder image to tell the client that an image is being generated but has not yet reached a step that can be rendered.
335 |
336 | ***
--------------------------------------------------------------------------------
/docs/api/groups/polls.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Polls"
3 | description: "Learn how to interact with GroupMe's polling system via the API."
4 | ---
5 |
6 | # Polls
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## Create a poll
33 |
34 | Creates a poll, which is immediately sent to the group.
35 |
36 | ```json linenums="1" title="HTTP Request"
37 | POST /poll/:group_id
38 | {
39 | "subject": "Is Dasani the objective best brand of bottled water?",
40 | "options": [
41 | {"title": "Yes"},
42 | {"title": "Absolutely"}
43 | ],
44 | "expiration": 1614042900,
45 | "type": "multi",
46 | "visibility": "public"
47 | }
48 | ```
49 |
50 | **Parameters**
51 |
52 | * *subject* (required)
53 |
54 | string - the title of the poll
55 |
56 | * *options* (required)
57 |
58 | array - contains objects of the form `{"title": option}`, where `option` is the title of that choice
59 |
60 | * *expiration* (required)
61 |
62 | integer - Time at which the poll expires, in SECONDS (not milliseconds) since January 1, 1970.
63 |
64 | * *type* (required)
65 |
66 | string - can either be "single" or "multi". Determines if users can respond with more than one choice or only one.
67 |
68 | * *visibility* (required)
69 |
70 | string - can either be "anonymous" or "public". If "public", others will be able to see who voted for which options.
71 |
72 |
73 |
74 | ```json linenums="1" title="HTTP Response"
75 | Status: 201 Created
76 | {
77 | "poll": {
78 | "data": {
79 | "id": "1234567890",
80 | "subject": "Is Dasani the objective best brand of bottled water?",
81 | "owner_id": "123456789",
82 | "conversation_id": "14538582",
83 | "created_at": 1613956027,
84 | "expiration": 1614042900,
85 | "status": "active",
86 | "options": [
87 | {
88 | "id": "1",
89 | "title": "Yes"
90 | },
91 | {
92 | "id": "2",
93 | "title": "Absolutely"
94 | }
95 | ],
96 | "last_modified": 1613956027,
97 | "type": "multi",
98 | "visibility": "public"
99 | }
100 | },
101 | "message": {
102 | "attachments": [
103 | {
104 | "poll_id": "1613956027340750",
105 | "type": "poll"
106 | }
107 | ],
108 | "avatar_url": "https://i.groupme.com/1100x1148.jpeg.705e8e84384c4249bb956f230e43d67d",
109 | "created_at": 1613956027,
110 | "event": {
111 | "data": {
112 | "conversation": {
113 | "id": "123456789"
114 | },
115 | "poll": {
116 | "id": "123456789",
117 | "subject": "Is this a test?"
118 | },
119 | "user": {
120 | "id": "123456789",
121 | "nickname": "Dasani Lover"
122 | }
123 | },
124 | "type": "poll.created"
125 | },
126 | "favorited_by": [],
127 | "group_id": "123456789",
128 | "id": "1234567890",
129 | "name": "Daniel Royer",
130 | "sender_id": "123456789",
131 | "sender_type": "user",
132 | "source_guid": "77a6d4981c554c71ac40ae30ba15e8a6",
133 | "system": false,
134 | "text": "Created new poll 'Is Dasani the objective best brand of bottled water?': https://s.groupme.com/1shwZm",
135 | "user_id": "123456789"
136 | }
137 | }
138 | ```
139 |
140 | ***
141 |
142 | ## Viewing Results
143 |
144 | Shows the responses to a poll
145 |
146 | ```json linenums="1" title="HTTP Request"
147 | GET /poll/:group_id/:poll_id
148 | ```
149 |
150 | ```json linenums="1" title="HTTP Response"
151 | Status: 201 Created
152 | {
153 | "poll": {
154 | "data": {
155 | "id": "123456789",
156 | "subject": "Is Dasani the objective best brand of bottled water?",
157 | "owner_id": "123456789",
158 | "conversation_id": "123456789",
159 | "created_at": 1613956027,
160 | "expiration": 1614042900,
161 | "status": "active",
162 | "options": [
163 | {
164 | "id": "1",
165 | "title": "Yes",
166 | "votes": 1,
167 | "voter_ids": [
168 | "123456789"
169 | ]
170 | },
171 | {
172 | "id": "2",
173 | "title": "Absolutely",
174 | "votes": 2,
175 | "voter_ids": [
176 | "123456789",
177 | "1234567890"
178 | ]
179 | }
180 | ],
181 | "last_modified": 1613957145,
182 | "type": "multi",
183 | "visibility": "public"
184 | },
185 | "user_votes": [
186 | "2",
187 | "1"
188 | ]
189 | }
190 | }
191 | ```
192 |
193 | In an anonymous poll, the "voter_ids" field does not exist.
194 |
195 | ***
196 |
197 | ## Voting in a poll
198 |
199 | Vote in a poll, or change your vote if you already voted.
200 |
201 | ```json linenums="1" title="HTTP Request (Single-Response Polls)"
202 | POST /poll/:group_id/:poll_id/:option_id
203 | ```
204 |
205 | ```json linenums="1" title="HTTP Request (Multi-Response Polls)"
206 | POST /poll/:group_id/:poll_id/
207 | {
208 | "votes": ["1", "2"]
209 | }
210 | ```
211 |
212 | **Parameters**
213 |
214 | * *votes*
215 | array - an array of the IDs of the options you want to vote for
216 |
217 | ```json linenums="1" title="HTTP Response"
218 | Status: 200 OK
219 | {
220 | "poll": {
221 | "data": {
222 | "id": "123456789",
223 | "subject": "Is Dasani the objective best brand of bottled water?",
224 | "owner_id": "123456789",
225 | "conversation_id": "123456789",
226 | "created_at": 1613956027,
227 | "expiration": 1614042900,
228 | "status": "active",
229 | "options": [
230 | {
231 | "id": "1",
232 | "title": "Yes",
233 | "votes": 1,
234 | "voter_ids": [
235 | "123456789"
236 | ]
237 | },
238 | {
239 | "id": "2",
240 | "title": "Absolutely",
241 | "votes": 2,
242 | "voter_ids": [
243 | "123456789",
244 | "1234567890"
245 | ]
246 | }
247 | ],
248 | "last_modified": 1613957145,
249 | "type": "multi",
250 | "visibility": "public"
251 | },
252 | "user_votes": [
253 | "2",
254 | "1"
255 | ]
256 | }
257 | }
258 | ```
259 |
260 | In an anonymous poll, the "voter_ids" field does not exist.
261 |
262 | If the user has not voted, the "user_votes" field will not exist.
263 |
264 | ***
265 |
266 | ## End poll
267 |
268 | End a poll right now
269 |
270 | ```json linenums="1" title="HTTP Request"
271 | POST /poll/:group_id/:poll_id/end
272 | ```
273 |
274 | ```json linenums="1" title="HTTP Response"
275 | Status: 200 OK
276 | {
277 | "poll": {
278 | "data": {
279 | "id": "123456789",
280 | "subject": "Is Dasani the objective best brand of bottled water?",
281 | "owner_id": "123456789",
282 | "conversation_id": "123456789",
283 | "created_at": 1613956027,
284 | "expiration": 1614042900,
285 | "status": "active",
286 | "options": [
287 | {
288 | "id": "1",
289 | "title": "Yes",
290 | "votes": 1,
291 | "voter_ids": [
292 | "123456789"
293 | ]
294 | },
295 | {
296 | "id": "2",
297 | "title": "Absolutely",
298 | "votes": 2,
299 | "voter_ids": [
300 | "123456789",
301 | "1234567890"
302 | ]
303 | }
304 | ],
305 | "last_modified": 1613957145,
306 | "type": "multi",
307 | "visibility": "public"
308 | },
309 | "user_votes": [
310 | "2",
311 | "1"
312 | ]
313 | }
314 | }
315 | ```
316 |
317 | ***
318 |
319 | ## List polls
320 |
321 | List all the polls in this chat
322 |
323 | ```json linenums="1" title="HTTP Request"
324 | GET /poll/:group_id
325 | ```
326 |
327 | ```json linenums="1" title="HTTP Response"
328 | Status: 200 OK
329 | {
330 | "polls": [
331 | {
332 | "data": {
333 | "id": "123456789",
334 | "subject": "Is this also a test?",
335 | "owner_id": "123456789",
336 | "conversation_id": "123456789",
337 | "created_at": 1613957003,
338 | "expiration": 1614043800,
339 | "status": "active",
340 | "options": [
341 | {
342 | "id": "1",
343 | "title": "Sure",
344 | "votes": 2
345 | },
346 | {
347 | "id": "2",
348 | "title": "I guess"
349 | }
350 | ],
351 | "last_modified": 1613958713,
352 | "type": "single",
353 | "visibility": "anonymous"
354 | },
355 | "user_vote": "1",
356 | "user_votes": [
357 | "1"
358 | ]
359 | },
360 | {
361 | "data": {
362 | "id": "123456789",
363 | "subject": "Is this a test?",
364 | "owner_id": "123456789",
365 | "conversation_id": "123456789",
366 | "created_at": 1613956027,
367 | "expiration": 1613958805,
368 | "status": "past",
369 | "options": [
370 | {
371 | "id": "1",
372 | "title": "Yes",
373 | "votes": 1,
374 | "voter_ids": [
375 | "123456789"
376 | ]
377 | },
378 | {
379 | "id": "2",
380 | "title": "Yes but the second option",
381 | "votes": 1,
382 | "voter_ids": [
383 | "1234567890"
384 | ]
385 | }
386 | ],
387 | "last_modified": 1613958805,
388 | "type": "multi",
389 | "visibility": "public"
390 | },
391 | "user_votes": [
392 | "1"
393 | ]
394 | }
395 | ],
396 | "continuation_token": null
397 | }
398 | ```
399 |
--------------------------------------------------------------------------------
/docs/api/groups/members.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Group Memberships"
3 | description: "Learn how to interact with GroupMe's member objects via the API."
4 | ---
5 |
6 | # Members
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## Index Members
33 |
34 | Fetch a group's current or former member list.
35 |
36 | This call is limited to admins and owners in the group. Any other caller will receive a `401 Unauthorized` response.
37 |
38 | ```json linenums="1" title="HTTP Request"
39 | GET /groups/:group_id/members
40 | ```
41 |
42 | **Parameters**
43 |
44 | * *filter* (required)
45 |
46 | string - to fetch either `active` (current memberships) or `inactive` (former memberships).
47 |
48 | ```json linenums="1" title="HTTP Response"
49 | Status: 200 OK
50 | {
51 | "memberships":[
52 | {
53 | "id": "24681012",
54 | "user_id": "11223344",
55 | "name": "Alureon",
56 | "nickname": "Alu",
57 | "image_url": "https://i.groupme.com/1a3c5e7g",
58 | "state": "active",
59 | "roles": ["owner","admin"]
60 | },
61 | {
62 | "id": "1357911131",
63 | "user_id": "55667788",
64 | "name": "Franco H",
65 | "nickname": "Fran",
66 | "image_url": "https://i.groupme.com/2b4d6f8h",
67 | "state": "active",
68 | "roles": ["user"]
69 | }
70 | ]
71 | }
72 | ```
73 |
74 | ## Add Members
75 | Add members to a group.
76 |
77 | Multiple members can be added in a single request, and results are fetched with a separate call (since memberships are processed asynchronously). The response includes a results_id that's used in the results request.
78 |
79 | In order to correlate request params with resulting memberships, GUIDs can be added to the members parameters. These GUIDs will be reflected in the membership JSON objects.
80 |
81 | !!! warning "Breaking Change: Device Verification"
82 |
83 | This endpoint now requires platform-level device verification (e.g., Apple App Attest or Google Play Integrity). Requests must include valid attestation headers (`X-verify-token`), or they will be rejected.
84 |
85 | This change was introduced by GroupMe to mitigate automated abuse. Unfortunately, it also blocks all third-party clients, including many valuable community projects. As such, this endpoint is no longer accessible to third-party applications and is retained here for historical reference only.
86 |
87 | ```json linenums="1" title="HTTP Request"
88 | POST /groups/:group_id/members/add
89 | {
90 | "members": [
91 | {
92 | "nickname": "Mom",
93 | "user_id": "1234567890",
94 | "guid": "GUID-1"
95 | },
96 | {
97 | "nickname": "Dad",
98 | "phone_number": "+1 2123001234",
99 | "guid": "GUID-2"
100 | },
101 | {
102 | "nickname": "Jane",
103 | "email": "jane@example.com",
104 | "guid": "GUID-3"
105 | }
106 | ]
107 | }
108 | ```
109 |
110 | **Parameters**
111 | * *members*
112 |
113 | * *array* - nickname is required. You must use one of the following identifiers: user_id, phone_number, or email. The array should contain objects with the following properties:
114 |
115 | * *nickname* (required)
116 |
117 | string - The name the user will use
118 |
119 | * *user_id*
120 |
121 | string - The user ID of the user to add
122 |
123 | * *phone_number*
124 |
125 | string - The phone number of the user to add
126 |
127 | * *email*
128 |
129 | string - The phone number of the user to add
130 |
131 | * *guid*
132 |
133 | string - If used, the GUID of the associated "results" object will match the value given
134 |
135 | ```json linenums="1" title="HTTP Response"
136 | Status: 202 Accepted
137 | {
138 | "results_id": "GUID"
139 | }
140 | ```
141 |
142 | ***
143 |
144 | ## Fetch "Add" Results
145 |
146 |
147 | Get the membership results from an add call.
148 |
149 | Successfully created memberships will be returned, including any GUIDs that were sent up in the add request. If GUIDs were absent, they are filled in automatically. Failed memberships and invites are omitted.
150 |
151 | Keep in mind that results are temporary -- they will only be available for 1 hour after the add request.
152 |
153 | ```json linenums="1" title="HTTP Request"
154 | GET /groups/:group_id/members/results/:results_id
155 | ```
156 |
157 | **Parameters**
158 |
159 | * *results_id* (required)
160 |
161 | string - This is the guid that's returned from an add request.
162 |
163 | ```json linenums="1" title="HTTP Response"
164 | Status: 200 OK
165 | {
166 | "members": [
167 | {
168 | "id": "1000",
169 | "user_id": "10000",
170 | "nickname": "John",
171 | "muted": false,
172 | "image_url": "https://i.groupme.com/AVATAR",
173 | "autokicked": false,
174 | "app_installed": true,
175 | "guid": "GUID-1"
176 | },
177 | {
178 | "id": "2000",
179 | "user_id": "20000",
180 | "nickname": "Anne",
181 | "muted": false,
182 | "image_url": "https://i.groupme.com/AVATAR",
183 | "autokicked": false,
184 | "app_installed": true,
185 | "guid": "GUID-2"
186 | }
187 | ]
188 | }
189 | ```
190 | ```json linenums="1" title="HTTP Response"
191 | Status: 503 Service Unavailable
192 | Results aren't ready. Try again in a little bit.
193 | ```
194 | ```json linenums="1" title="HTTP Response"
195 | Status: 404 Not Found
196 | Results are no longer available. Don't try again.
197 | ```
198 |
199 | ***
200 |
201 | ## Remove Member
202 |
203 |
204 | Remove a member (or yourself) from a group.
205 |
206 | Note: The creator of the group cannot be removed or exit.
207 |
208 | ```json linenums="1" title="HTTP Request"
209 | POST /groups/:group_id/members/:membership_id/remove
210 | ```
211 |
212 | **Parameters**
213 |
214 | * *membership_id* (required)
215 |
216 | string - Please note that this isn't the same as the user ID. In the members key in the group JSON, this is the id value, not the user_id.
217 |
218 | ```json linenums="1" title="HTTP Response"
219 | Status: 200 OK
220 | ```
221 |
222 | ***
223 |
224 | ## Index Pending Join Requests
225 |
226 | Some groups have "Request to join" enabled, and thus require their applications approved by an admin.
227 |
228 | This request can be sent by any member of the group, not just admins. However, in order to approve or deny requests, you must have permission to manage the group.
229 |
230 | ```json linenums="1" title="HTTP Request"
231 | GET /groups/:group_id/pending_memberships
232 | ```
233 |
234 | ```json linenums="1" title="HTTP Response"
235 | Status: 200 OK
236 | [
237 | {
238 | "id": "1075936468",
239 | "user_id": "43303468",
240 | "nickname": "bob",
241 | "image_url": "https://i.groupme.com/2320x3088.jpeg.df62e30722404d21acee182c1a3eb633",
242 | "reason": {
243 | "type": "join_reason/membership_join_reason",
244 | "question": {
245 | "type": "join_reason/questions/text",
246 | "text": "Why do you want to join this group?"
247 | },
248 | "answer": {
249 | "type": "join_reason/answers/text",
250 | "response": "Because it looks awesome!"
251 | },
252 | "method": "discoverable"
253 | },
254 | "timestamp": 1747219206,
255 | "state": "requested_pending"
256 | },
257 | {
258 | "id": "1075937258",
259 | "user_id": "43303469",
260 | "nickname": "allice",
261 | "image_url": "https://i.groupme.com/2320x3088.jpeg.df62e307b2402321acee182c1a3eb633",
262 | "reason": {
263 | "type": "join_reason/membership_join_reason",
264 | "question": {
265 | "type": "join_reason/questions/text",
266 | "text": "Why do you want to join this group?"
267 | },
268 | "answer": {
269 | "type": "join_reason/answers/text",
270 | "response": "Because I love GroupMe!"
271 | },
272 | "method": "discoverable"
273 | },
274 | "timestamp": 1756219206,
275 | "state": "requested_pending"
276 | }
277 | ]
278 | ```
279 |
280 | ***
281 |
282 | ## Accept/Deny a Pending Join Request
283 |
284 | This request is exclusive to members with permission to manage the group, non Admin/Owners will receive a 401: Unauthorized response.
285 |
286 | ```json linenums="1" title="HTTP Request"
287 | POST /groups/:group_id/members/:membership_id/approval
288 | {
289 | "approval": true
290 | }
291 | ```
292 | **Parameters**
293 |
294 | * *membership_id* (required)
295 |
296 | string - The *group specific* ID of the membership you wish to handle. Please note that this isn't the same as the user ID. In the members key in the group JSON, this is the id value, not the user_id.
297 |
298 | * *approval* (required)
299 |
300 | boolean - `true` to approve, `false` to deny.
301 |
302 | Note: if you deny the membership, `state` will be "denied" instead of "active"
303 |
304 | ```json linenums="1" title="HTTP Response"
305 | Status: 200 OK
306 | {
307 | "membership_id": 1075929653,
308 | "state": "active"
309 | }
310 | ```
311 | ```json linenums="1" title="HTTP Response"
312 | Status: 401 Unauthorized
313 | You are neither the Owner nor an Admin in this group
314 | ```
315 |
316 | ***
317 |
318 | ## Ban Member (v2)
319 |
320 | Prevent a member from rejoining a group after they leave.
321 |
322 | Current members of the group cannot be banned from rejoining as they have not left.
323 |
324 | Note: This request is relative to `https://v2.groupme.com`, NOT `https://api.groupme.com/v3`.
325 |
326 | ```json linenums="1" title="HTTP Request"
327 | POST /groups/:group_id/memberships/:membership_id/destroy
328 | ```
329 |
330 | **Parameters**
331 |
332 | * *membership_id* (required)
333 |
334 | string - Please note that this isn't the same as the user ID. In the members key in the group JSON, this is the id value, not the user_id.
335 |
336 | ```json linenums="1" title="HTTP Response"
337 | Status: 200 OK
338 | ```
339 |
340 | ***
341 |
342 | ## Change nickname
343 |
344 |
345 | Update your nickname in a group. The nickname must be between 1 and 50 characters.
346 |
347 | ```json linenums="1" title="HTTP Request"
348 | POST /groups/:group_id/memberships/update
349 | {
350 | "membership": {
351 | "nickname": "NEW NICKNAME"
352 | }
353 | }
354 | ```
355 |
356 | ```json linenums="1" title="HTTP Response"
357 | Status: 200 OK
358 | {
359 | "id": "MEMBERSHIP ID",
360 | "user_id": "USER ID",
361 | "nickname": "NEW NICKNAME",
362 | "muted": false,
363 | "image_url": "AVATAR URL",
364 | "autokicked": false,
365 | "app_installed": true
366 | }
367 | ```
368 |
--------------------------------------------------------------------------------
/docs/api/groups/messages.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Messages"
3 | description: "Learn how to interact with GroupMe's group message objects via the API."
4 | ---
5 |
6 | # Group Messages
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## Index
33 |
34 |
35 | Retrieve messages for a group.
36 |
37 | By default, messages are returned in groups of 20, ordered by created_at descending. This can be raised or lowered by passing a limit parameter, up to a maximum of 100 messages.
38 |
39 | Messages can be scanned by providing a message ID as either the before_id, since_id, or after_id parameter. If before_id is provided, then messages immediately preceding the given message will be returned, in descending order. This can be used to continually page back through a group's messages.
40 |
41 | The after_id parameter will return messages that immediately follow a given message, this time in ascending order (which makes it easy to pick off the last result for continued pagination).
42 |
43 | Finally, the since_id parameter also returns messages created after the given message, but it retrieves the most recent messages. For example, if more than twenty messages are created after the since_id message, using this parameter will omit the messages that immediately follow the given message. This is a bit counterintuitive, so take care.
44 |
45 | If no messages are found (e.g. when filtering with before_id) we return code 304.
46 |
47 | Note that for historical reasons, likes are returned as an array of user ids in the favorited_by key.
48 |
49 | ```json linenums="1" title="HTTP Request"
50 | GET /groups/:group_id/messages
51 | ```
52 |
53 | **Parameters**
54 |
55 | * *before_id*
56 |
57 | string - Returns messages created before the given message ID
58 |
59 | * *since_id*
60 |
61 | string - Returns most recent messages created after the given message ID
62 |
63 | * *after_id*
64 |
65 | string - Returns messages created immediately after the given message ID
66 |
67 | * *limit*
68 |
69 | integer - Number of messages returned. Default is 20. Max is 100.
70 |
71 | * *acceptFiles*
72 |
73 | boolean - A flag to tell the image service your client is capable of receiving non-image files. Setting this value to `0` or omitting it entirely will NOT omit messages with file attachments in the response; these messages will still be included, but the text property will be overwritten with `Please upgrade to download this file.` The file attachment will still be fully intact, however. In practice this means that if you care about the text in the message with an attachment, you should set `acceptFiles=1`.
74 |
75 |
76 | ```json linenums="1" title="HTTP Response"
77 | Status: 200 OK
78 | {
79 | "count": 123,
80 | "messages": [
81 | {
82 | "id": "1234567890",
83 | "source_guid": "GUID",
84 | "created_at": 1302623328,
85 | "user_id": "1234567890",
86 | "group_id": "1234567890",
87 | "name": "John",
88 | "avatar_url": "https://i.groupme.com/123456789",
89 | "text": "Hello world ",
90 | "system": true,
91 | "pinned_by": "",
92 | "pinned_at": null,
93 | "favorited_by": [
94 | "101",
95 | "66",
96 | "1234567890"
97 | ],
98 | "attachments": [
99 | {
100 | "type": "image",
101 | "url": "https://i.groupme.com/123456789"
102 | },
103 | {
104 | "type": "location",
105 | "lat": "40.738206",
106 | "lng": "-73.993285",
107 | "name": "GroupMe HQ"
108 | },
109 | {
110 | "type": "emoji",
111 | "placeholder": "",
112 | "charmap": [
113 | [1, 42],
114 | [2, 34]
115 | ]
116 | }
117 | ]
118 | }
119 | ]
120 | }
121 | ```
122 |
123 | ***
124 |
125 | ## Show a Specific Message
126 |
127 | Fetches an individual message object by its ID
128 |
129 | > [!important]
130 | > This request is relative to `https://api.groupme.com/v4/`, not `https://api.groupme.com/v3/`.
131 |
132 | ```json linenums="1" title="HTTP Request"
133 | GET https://api.groupme.com/v4/groups/:group_id/messages/:message_id
134 | ```
135 |
136 | **Parameters**
137 |
138 | * *message_id*
139 |
140 | string - The ID of the message you'd like to fetch
141 |
142 | ```json linenums="1" title="HTTP Response"
143 | Status: 200 OK
144 | {
145 | "message": {
146 | "attachments": [],
147 | "avatar_url": "https://i.groupme.com/572x525.png.e40a56a9f4c141868c56c2fc52bb8b4a",
148 | "created_at": 1743898778,
149 | "favorited_by": [],
150 | "group_id": "28330125",
151 | "id": "1743898778900707",
152 | "name": "Bob",
153 | "sender_id": "3274635",
154 | "sender_type": "user",
155 | "source_guid": "android-cbe3d2b5-d245-4b89-8555-838852949490",
156 | "system": false,
157 | "text": "hi there!",
158 | "user_id": "3274635",
159 | "platform": "gm",
160 | "pinned_at": null,
161 | "pinned_by": ""
162 | }
163 | }
164 | ```
165 |
166 | ## Send Message
167 |
168 |
169 | Send a message to a group
170 |
171 | If you want to attach an image, you must first process it through GroupMe's image service (More on that in the [attachments documentation](../common/attachments.md))
172 |
173 | Attachments of type emoji rely on data from [GroupMe Emoji powerups](../common/emoji.md).
174 |
175 | Clients use a placeholder character in the message text and specify a replacement charmap to substitute emoji characters
176 |
177 | The character map is an array of arrays containing rune data ([[{pack_id,offset}],...]).
178 |
179 | The placeholder should be a high-point/invisible UTF-8 character.
180 |
181 | ```json linenums="1" title="HTTP Request"
182 | POST /groups/:group_id/messages
183 | {
184 | "message": {
185 | "source_guid": "GUID",
186 | "text": "Hello world ",
187 | "attachments": [
188 | {
189 | "type": "image",
190 | "url": "https://i.groupme.com/123456789"
191 | },
192 | {
193 | "type": "location",
194 | "lat": "40.738206",
195 | "lng": "-73.993285",
196 | "name": "GroupMe HQ"
197 | },
198 | {
199 | "type": "emoji",
200 | "placeholder": "",
201 | "charmap": [
202 | [1, 42],
203 | [2, 34]
204 | ]
205 | }
206 | ]
207 | }
208 | }
209 | ```
210 |
211 | **Parameters**
212 | * *source_guid* (required)
213 |
214 | string - Client-side IDs for messages. This can be used by clients to set their own identifiers on messages, but the server also scans these for de-duplication. That is, if two messages are sent with the same source_guid within one minute of each other, the second message will fail with a 409 Conflict response. So it's important to set this to a unique value for each message.
215 |
216 | * *text* (required)
217 |
218 | string - This can be omitted if at least one attachment is present. The maximum length is 1,000 characters.
219 |
220 | * *attachments*
221 |
222 | array - A polymorphic list of attachments (locations, images, replies, etc). You may have more than one of any type of attachment, provided clients can display it.
223 |
224 | For more information on types of attachments and how to send them, check out the [attachments documentation](../common/attachments.md)
225 |
226 | ```json linenums="1" title="HTTP Response"
227 | Status: 201 Created
228 | {
229 | "message": {
230 | "id": "1234567890",
231 | "source_guid": "GUID",
232 | "created_at": 1302623328,
233 | "user_id": "1234567890",
234 | "group_id": "1234567890",
235 | "name": "John",
236 | "avatar_url": "https://i.groupme.com/123456789",
237 | "text": "Hello world ",
238 | "system": false,
239 | "pinned_by": "",
240 | "pinned_at": null,
241 | "favorited_by": [
242 | "101",
243 | "66",
244 | "1234567890"
245 | ],
246 | "attachments": [
247 | {
248 | "type": "image",
249 | "url": "https://i.groupme.com/123456789"
250 | },
251 | {
252 | "type": "location",
253 | "lat": "40.738206",
254 | "lng": "-73.993285",
255 | "name": "GroupMe HQ"
256 | },
257 | {
258 | "type": "emoji",
259 | "placeholder": "",
260 | "charmap": [
261 | [1, 42],
262 | [2, 34]
263 | ]
264 | }
265 | ]
266 | }
267 | }
268 | ```
269 |
270 | ***
271 |
272 | ## Edit Message
273 |
274 | Edit a message you've already sent
275 |
276 | If you want to attach an image, you must first process it through GroupMe's image service (More on that in the [attachments documentation](../common/attachments.md))
277 |
278 | Attachments of type emoji rely on data from [GroupMe Emoji powerups](../common/emoji.md).
279 |
280 | > [!important]
281 | > This request is relative to `https://api.groupme.com/v4/`, not `https://api.groupme.com/v3/`.
282 |
283 | ```json linenums="1" title="HTTP Request"
284 | PUT https://api.groupme.com/v4/groups/:group_id/messages/:message_id
285 | {
286 | "text": "Hello World!",
287 | "attachments": [
288 | {
289 | "type": "image",
290 | "url": "https://i.groupme.com/123456789"
291 | },
292 | {
293 | "type": "location",
294 | "lat": "40.738206",
295 | "lng": "-73.993285",
296 | "name": "GroupMe HQ"
297 | },
298 | ]
299 | }
300 | ```
301 |
302 | **Parameters**
303 | * *text* (required)
304 |
305 | string - This can be omitted if at least one attachment is present. The maximum length is 1,000 characters.
306 |
307 | * *attachments*
308 |
309 | array - A polymorphic list of attachments (locations, images, replies, etc). You may have more than one of any type of attachment, provided clients can display it.
310 |
311 | For more information on types of attachments and how to send them, check out the [attachments documentation](../common/attachments.md)
312 |
313 | ```json linenums="1" title="HTTP Response"
314 | Status: 200 OK
315 | {
316 | "message": {
317 | "id": "1234567890",
318 | "source_guid": "GUID",
319 | "created_at": 1302623328,
320 | "updated_at": 1747065854,
321 | "user_id": "1234567890",
322 | "group_id": "1234567890",
323 | "name": "John",
324 | "avatar_url": "https://i.groupme.com/123456789",
325 | "text": "Hello World!",
326 | "system": false,
327 | "pinned_by": "",
328 | "pinned_at": null,
329 | "favorited_by": [
330 | "101",
331 | "66",
332 | "1234567890"
333 | ],
334 | "attachments": [
335 | {
336 | "type": "image",
337 | "url": "https://i.groupme.com/123456789"
338 | },
339 | {
340 | "type": "location",
341 | "lat": "40.738206",
342 | "lng": "-73.993285",
343 | "name": "GroupMe HQ"
344 | },
345 | ]
346 | }
347 | }
348 | ```
349 |
350 | ***
351 |
352 |
353 | ## Delete a message
354 |
355 | ```json linenums="1" title="HTTP Request"
356 | DELETE /conversations/:group_id/messages/:message_id
357 | ```
358 |
359 | ```json linenums="1" title="HTTP Response"
360 | Status: 204 Deleted
361 | ```
362 |
--------------------------------------------------------------------------------
/docs/api/dms/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Direct Messages"
3 | description: "Learn how to interact with GroupMe's direct message channels via the API."
4 | ---
5 |
6 | # Direct Messages
7 |
8 | Unless otherwise stated, endpoints are relative to https://api.groupme.com/v3/ and must include the token of the user making the call - so, for example, if an endpoint is `GET /groups`, the request you make should be using the URL `https://api.groupme.com/v3/groups?token=aSDFghJkl`, where `aSDFghJkl` is replaced with the user's token.
9 |
10 | URLs which include a variable, such as `GET /groups/:id`, have their variables marked with a colon. So a request to that endpoint would look like `https://api.groupme.com/v3/groups/1234567?token=aSDFghJkl`, where `1234567` is replaced with the group's ID, and `aSDFghJkl` is replaced with the user's token.
11 |
12 | Finally, all responses are wrapped in a response envelope of the following form:
13 |
14 | ```json linenums="1"
15 | {
16 | "response": {
17 | "id": "12345",
18 | "name": "Family"
19 | ...
20 | },
21 | "meta": {
22 | "code": 200,
23 | "errors": []
24 | }
25 | }
26 | ```
27 |
28 | If the request succeeds, `meta.errors` will be null, and if the request fails, `response` will be null.
29 |
30 | ***
31 |
32 | ## List Existing DM Channels
33 |
34 |
35 | Returns a paginated list of direct message chats, or conversations, sorted by updated_at descending.
36 |
37 | ```json linenums="1" title="HTTP Request"
38 | GET /chats
39 | ```
40 |
41 | **Parameters**
42 |
43 | * *page*
44 |
45 | integer - Page number (Starts at 1, Defaults to 1)
46 |
47 | * *per_page*
48 |
49 | integer — Number of chats per page (Defaults to 20)
50 |
51 | ```json linenums="1" title="HTTP Response"
52 | Status: 200 OK
53 | [
54 | {
55 | "created_at": 1352299338,
56 | "updated_at": 1352299338,
57 | "last_message": {
58 | "attachments": [
59 |
60 | ],
61 | "avatar_url": "https://i.groupme.com/200x200.jpeg.abcdef",
62 | "conversation_id": "12345+67890",
63 | "created_at": 1352299338,
64 | "favorited_by": [
65 |
66 | ],
67 | "id": "1234567890",
68 | "name": "John Doe",
69 | "recipient_id": "67890",
70 | "sender_id": "12345",
71 | "sender_type": "user",
72 | "source_guid": "GUID",
73 | "text": "Hello world",
74 | "user_id": "12345"
75 | },
76 | "messages_count": 10,
77 | "other_user": {
78 | "avatar_url": "https://i.groupme.com/200x200.jpeg.abcdef",
79 | "id": 12345,
80 | "name": "John Doe"
81 | }
82 | }
83 | ]
84 | ```
85 |
86 | ***
87 |
88 | ## Show Specific DM Channel
89 |
90 | Directly fetch details about a specific DM channel using its compound `chat_id`.
91 |
92 | ```json linenums="1" title="HTTP Request"
93 | GET /chats/:chat_id
94 | ```
95 |
96 | **Parameters**
97 |
98 | * *chat_id*
99 |
100 | string - this is the compound ID of the chat, consisting of two seperate user IDs. It should look something like `93645911+118825642`.
101 |
102 | ```json linenums="1" title="HTTP Response"
103 | Status: 200 OK
104 | {
105 | "created_at": 1705616604,
106 | "last_message": {
107 | "attachments": [],
108 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.a13c40722d454d0e9c59d2cedb119056",
109 | "conversation_id": "56366372+93645911",
110 | "created_at": 1747778589,
111 | "favorited_by": [],
112 | "id": "174777858990222590",
113 | "name": "Bob",
114 | "recipient_id": "93645911",
115 | "sender_id": "56366372",
116 | "sender_type": "user",
117 | "source_guid": "FB9B32E6-45B8-4A63-8FAA-4A60005A0A60",
118 | "text": "I think it’s a good idea!",
119 | "user_id": "56366372",
120 | "pinned_at": null,
121 | "pinned_by": ""
122 | },
123 | "messages_count": 742,
124 | "other_user": {
125 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.a13c409e2d454320e9c59d2cedb119056",
126 | "id": "56366372",
127 | "name": "Bob"
128 | },
129 | "updated_at": 1747778589,
130 | "message_deletion_period": 2147483647,
131 | "message_deletion_mode": ["sender"],
132 | "requires_approval": false,
133 | "unread_count": null,
134 | "last_read_message_id": null,
135 | "last_read_at": null,
136 | "message_edit_period": 15
137 | }
138 | ```
139 |
140 | ***
141 |
142 | ## Delete DM Channel
143 |
144 | Clears chat history and removes an existing DM channel from your chats list
145 |
146 | ```json linenums="1" title="HTTP Request"
147 | DELETE /chats/:chat_id
148 | ```
149 |
150 | **Parameters**
151 |
152 | * *chat_id*
153 |
154 | string - this is the compound ID of the chat, consisting of two seperate user IDs. It should look something like `93645911+118825642`.
155 |
156 | ```json linenums="1" title="HTTP Response"
157 | Status: 200 OK
158 | ```
159 |
160 | ***
161 |
162 | ## Index Messages
163 |
164 | Fetch direct messages between two users.
165 |
166 | DMs are returned in groups of 20, ordered by created_at descending.
167 |
168 | If no messages are found (e.g. when filtering with since_id) we return code 304.
169 |
170 | Note that for historical reasons, likes are returned as an array of user ids in the favorited_by key.
171 |
172 | ```json linenums="1" title="HTTP Request"
173 | GET /direct_messages
174 | ```
175 |
176 | **Parameters**
177 |
178 | * *other_user_id* (required)
179 |
180 | string — The other participant in the conversation.
181 |
182 | * *before_id*
183 |
184 | string — Returns messages created before the given message ID
185 |
186 | * *since_id*
187 |
188 | string — Returns messages created after the given message ID
189 |
190 | * *after_id*
191 |
192 | string - Returns messages created immediately after the given message ID
193 |
194 | * *limit*
195 |
196 | integer - Number of messages returned. Default is 20. Max is 100.
197 |
198 | ```json linenums="1" title="HTTP Response"
199 | Status: 200 OK
200 | {
201 | "count": 123,
202 | "direct_messages": [
203 | {
204 | "attachments": [],
205 | "avatar_url": "https://i.groupme.com/1024x1024.jpeg.a13c409e2d454d082c59d2cedb119056",
206 | "conversation_id": "56366372+93645911",
207 | "created_at": 1747778589,
208 | "favorited_by": [],
209 | "id": "174777858990222590",
210 | "name": "Bob",
211 | "recipient_id": "93645911",
212 | "sender_id": "56366372",
213 | "sender_type": "user",
214 | "source_guid": "FB9B32E6-45B8-4A63-8FAA-4A60005A0A60",
215 | "text": "I think it’s a good idea!",
216 | "user_id": "56366372",
217 | "pinned_at": null,
218 | "pinned_by": ""
219 | }
220 | ],
221 | "read_receipt": {
222 | "id": "",
223 | "chat_id": "56366372+93645911",
224 | "message_id": "174777858990222590",
225 | "user_id": "93645911",
226 | "read_at": 1747779017
227 | }
228 | }
229 | ```
230 |
231 | > [!note]
232 | > If the server has no `read_receipt` parameter to share, it will be completely absent from the response.
233 |
234 | ***
235 |
236 | ## Send DM
237 |
238 | Send a DM to another user
239 |
240 | If you want to attach an image, you must first process it through the image service (More on that in the [attachments documentation](../common/attachments.md)).
241 |
242 | Attachments of type emoji rely on data from emoji PowerUps.
243 |
244 | Clients use a placeholder character in the message text and specify a replacement charmap to substitute emoji characters
245 |
246 | The character map is an array of arrays containing rune data ([[{pack_id,offset}],...]).
247 |
248 | The placeholder should be a high-point/invisible UTF-8 character.
249 |
250 | ```json linenums="1" title="HTTP Request"
251 | POST /direct_messages
252 | {
253 | "direct_message": {
254 | "source_guid": "GUID",
255 | "recipient_id": "20",
256 | "text": "Hello world ",
257 | "attachments": [
258 | {
259 | "type": "image",
260 | "url": "https://i.groupme.com/123456789"
261 | },
262 | {
263 | "type": "image",
264 | "url": "https://i.groupme.com/123456789"
265 | },
266 | {
267 | "type": "location",
268 | "lat": "40.738206",
269 | "lng": "-73.993285",
270 | "name": "GroupMe HQ"
271 | },
272 | {
273 | "type": "emoji",
274 | "placeholder": "",
275 | "charmap": [
276 | [1, 42],
277 | [2, 34]
278 | ]
279 | }
280 | ]
281 | }
282 | }
283 | ```
284 | **Parameters**
285 | * *source_guid* (required)
286 |
287 | string - Client-side IDs for messages. This can be used by clients to set their own identifiers on messages, but the server also scans these for de-duplication. That is, if two messages are sent with the same source_guid within one minute of each other, the second message will fail with a 409 Conflict response. So it's important to set this to a unique value for each message.
288 |
289 | * *recipient_id* (required)
290 |
291 | string - the GroupMe user ID of the recipient of this message.
292 |
293 | * *text* (required)
294 |
295 | string - This can be omitted if at least one attachment is present. The maximum length is 1,000 characters.
296 |
297 | * *attachments*
298 |
299 | array - A polymorphic list of attachments (locations, images, etc). You may have more than one of any type of attachment, provided clients can display it.
300 |
301 | For more information on types of attachments and how to send them, check out the [attachments documentation](../common/attachments.md)
302 |
303 | ```json linenums="1" title="HTTP Response"
304 | Status: 201 Created
305 | {
306 | "message": {
307 | "id": "1234567890",
308 | "source_guid": "GUID",
309 | "recipient_id": "20",
310 | "user_id": "1234567890",
311 | "created_at": 1302623328,
312 | "name": "John",
313 | "avatar_url": "https://i.groupme.com/123456789",
314 | "text": "Hello world ",
315 | "pinned_by": "",
316 | "pinned_at": null,
317 | "favorited_by": [
318 | "101"
319 | ],
320 | "attachments": [
321 | {
322 | "type": "image",
323 | "url": "https://i.groupme.com/123456789"
324 | },
325 | {
326 | "type": "location",
327 | "lat": "40.738206",
328 | "lng": "-73.993285",
329 | "name": "GroupMe HQ"
330 | },
331 | {
332 | "type": "emoji",
333 | "placeholder": "",
334 | "charmap": [
335 | [1, 42],
336 | [2, 34]
337 | ]
338 | }
339 | ]
340 | }
341 | }
342 | ```
343 | ```json linenums="1" title="HTTP Response"
344 | Status: 403 Forbidden
345 | User has been auto-banned for sending too many messages.
346 | ```
347 | ```json linenums="1" title="HTTP Response"
348 | Status: 400 Bad Request
349 | There's a problem with the parameters. Check errors.
350 | ```
351 |
352 | ***
353 |
354 | ## Delete a message
355 |
356 | ```json linenums="1" title="HTTP Request"
357 | DELETE /conversations/:group_id/messages/:message_id
358 | ```
359 |
360 | ```json linenums="1" title="HTTP Response"
361 | Status: 204 Deleted
362 | ```
363 |
364 | ***
365 |
366 | ## Send a Read Receipt
367 |
368 | You can only mark new messages as read, attempting to read a message sent before one you have already read will update the timestamp on the latest read message.
369 |
370 | > [!important]
371 | > This request is relative to `https://v2.groupme.com/`, not `https://api.groupme.com/v3/`.
372 |
373 | ```json linenums="1" title="HTTP Request"
374 | POST https://v2.groupme.com/read_receipts
375 | {
376 | "read_receipt": {
377 | "message_id": "174769395496126372",
378 | "chat_id": "74938777+93645911"
379 | },
380 | }
381 | ```
382 |
383 | **Parameters**
384 |
385 | * *message_id*
386 |
387 | String - The ID of the message you'd like to mark read.
388 |
389 | * *chat_id*
390 |
391 | String - The ID of the direct message channel the message can be found in.
392 |
393 | ```json linenums="1" title="HTTP Response"
394 | Status: 200 OK
395 | {
396 | "read_receipt": {
397 | "id": "174769438312353599",
398 | "chat_id": "74938777+93645911",
399 | "message_id": "174769438312353599",
400 | "user_id": "93645911",
401 | "read_at": 1747694449
402 | }
403 | }
404 | ```
405 |
--------------------------------------------------------------------------------