├── 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 |
66 | {%- if config.home_button1_name -%} 67 | 68 | {{ config.home_button1_name }} 69 | 70 | {%- endif -%} 71 | {%- if config.home_button2_name -%} 72 | 73 | {{ config.home_button2_name }} 74 | 75 | {%- endif -%} 76 |
77 |
78 |
79 | 80 |
81 |
82 |
83 |
84 | {% endblock %} -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | OpenGM Logo 5 | 6 |

7 |

GroupMe Community API Docs

8 |

9 | The complete, up-to-date, and community-driven documentation for the GroupMe API. 10 |

11 |
12 | 13 |
14 | 15 | Documentation License 16 | Code License 17 | Join the Developer Chat 18 |
19 | 20 |
21 | 22 |

View the Documentation Website

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 | OpenGM 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 | OpenGM 95 | -------------------------------------------------------------------------------- /src/overrides/assets/opengm_logo_colorized.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OpenGM 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 | --------------------------------------------------------------------------------