API Reference

The Groupi REST API v2. All endpoints require an API key via the x-api-key header.

Versions & Migration

v2 is recommended for new integrations. It returns resources directly and uses 204 No Content after successful deletes.

v2: https://api.groupi.gg/api/v2
v1: https://api.groupi.gg/api/v1

The v1 contract remains available for existing integrations. Its successful responses use { "success": true, "data": ... } envelopes, and deletes return a JSON confirmation with status 200.

Migrating from v1

  1. Change the base path from /api/v1 to /api/v2.
  2. Read successful response fields from the top level instead of from data.
  3. Treat a 204 response to DELETE as success without parsing a response body.
  4. Handle validation failures as VALIDATION_ERROR in the standard error object.

Authentication

Include your API key in the x-api-key header:

curl -H "x-api-key: grp_your_key_here" \
  https://api.groupi.gg/api/v2/events

Create API keys in your account settings.

Events

GET/events

Get all events the authenticated user is a member of.

Response

[{
  "id": "k170...",
  "title": "Team Offsite",
  "description": "Annual event",
  "location": "Mountain View",
  "chosenDateTime": 1704067200000,
  "memberCount": 12,
  "userRole": "ORGANIZER",
  "userRsvpStatus": "YES"
}]

Example

curl -H "x-api-key: grp_xxx" \
  https://api.groupi.gg/api/v2/events
POST/events

Create a new event. Use potentialDateTimeOptions for explicit ISO dates, or gdl for a Groupi Date Language expression (not both). GDL is parsed server-side with no AI dependency — see the GDL reference for more information. Invalid expressions return GDL_PARSE_ERROR.

Request Body

{
  "title": "Team Dinner",            // required, 1-200 chars
  "description": "Quarterly meetup",  // optional, max 5000
  "location": "Downtown",            // optional, max 500
  "gdl": "[Tu,Th]@18-20",            // optional GDL expression
  "potentialDateTimeOptions": [{      // optional (mutually exclusive with gdl)
    "start": "2024-02-15T18:00:00Z",
    "end": "2024-02-15T21:00:00Z"
  }],
  "chosenDateTime": "...",            // optional, for single-date events
  "chosenEndDateTime": "...",         // optional
  "reminderOffset": "1_DAY"           // optional
}

Response

{
  "eventId": "k170...",
  "membershipId": "k171..."
}

Example

curl -X POST -H "x-api-key: grp_xxx" \
  -H "Content-Type: application/json" \
  -d '{"title":"Dinner","gdl":"Fr@19"}' \
  https://api.groupi.gg/api/v2/events
GET/events/{eventId}

Get full details of a specific event. Requires membership.

Response

{
  "id": "k170...",
  "title": "Team Offsite",
  "description": "...",
  "location": "...",
  "timezone": "America/New_York",
  "chosenDateTime": null,
  "reminderOffset": "1_DAY",
  "creator": {
    "id": "k172...",
    "user": { "name": "Alice", "username": "alice" }
  }
}
PATCH/events/{eventId}

Update event details. Requires MODERATOR role or higher.

Request Body

{
  "title": "New Title",          // optional
  "description": "Updated",     // optional
  "location": "New Location",   // optional
  "reminderOffset": "2_HOURS"   // optional, null to remove
}
DELETE/events/{eventId}

Permanently delete an event and all associated data. Requires ORGANIZER role.

Posts

GET/events/{eventId}/posts

Get all posts for an event. Requires membership.

POST/events/{eventId}/posts

Create a new post in an event. Requires membership.

Request Body

{
  "title": "Meeting Notes",   // required
  "content": "Discussed..."   // required
}

Response

{ "postId": "k173..." }
GET/posts/{postId}

Get a post with its replies.

PATCH/posts/{postId}

Update a post. Requires author or MODERATOR role.

Request Body

{ "title": "...", "content": "..." }
DELETE/posts/{postId}

Delete a post and its replies. Requires author or MODERATOR role.

Replies

GET/posts/{postId}/replies

List replies

POST/posts/{postId}/replies

Create reply

Request Body

{ "text": "Sounds good!" }
PATCH/replies/{replyId}

Update reply

Request Body

{ "text": "Updated text" }
DELETE/replies/{replyId}

Delete reply

Members

GET/events/{eventId}/members

Get all members of an event with their roles and RSVP status.

Response

[{
  "id": "k174...",
  "role": "ATTENDEE",
  "rsvpStatus": "YES",
  "personId": "k175...",
  "user": { "name": "Bob", "username": "bob" }
}]
PATCH/events/{eventId}/members/{memberId}

Change a member's role. Requires MODERATOR or higher.

Request Body

{ "role": "MODERATOR" }  // ORGANIZER, MODERATOR, or ATTENDEE
DELETE/events/{eventId}/members/{memberId}

Remove a member from the event. Requires MODERATOR or higher.

POST/events/{eventId}/leave

Remove yourself from an event. Cannot leave as the last organizer.

Availability

GET/events/{eventId}/availability

Get the full availability voting grid showing all members' votes on all potential dates.

POST/events/{eventId}/availability

Submit or update your availability votes for potential dates.

Request Body

{
  "votes": [
    { "potentialDateTimeId": "k175...", "status": "YES" },
    { "potentialDateTimeId": "k176...", "status": "MAYBE" },
    { "potentialDateTimeId": "k177...", "status": "NO" }
  ]
}
GET/events/{eventId}/potential-dates

Get the list of potential date/time options for an event.

Friends

GET/friends

List friends

GET/friends/requests/incoming

Incoming friend requests

GET/friends/requests/outgoing

Outgoing friend requests

GET/friends/search?q={query}

Search for users by name or username.

GET/friends/status/{targetPersonId}

Get friendship status

Notifications

GET/notifications

Get notifications. Add ?unread=true to filter.

GET/notifications/count

Get unread count

Response

{ "count": 5 }
POST/notifications/{notificationId}/read

Mark as read

POST/notifications/read-all

Mark all as read

Profile

GET/profile

Get your profile

GET/profile/{username}

Get profile by username

PUT/profile

Update profile

Request Body

{ "bio": "Hello!", "pronouns": "they/them" }

Invites

GET/events/{eventId}/invites

List event invites

POST/events/{eventId}/invites

Create invite link

Response

{ "id": "...", "token": "abc123" }
DELETE/invites/{inviteId}

Delete invite

POST/invites/{token}/accept

Accept invite

Add-ons

GET/events/{eventId}/addons

List add-on configs

POST/events/{eventId}/addons/{addonType}/enable

Enable an add-on for an event. Requires MODERATOR or higher.

Request Body

{ "config": { ... } }  // addon-specific config
POST/events/{eventId}/addons/{addonType}/disable

Disable add-on

PATCH/events/{eventId}/addons/{addonType}/config

Update add-on config

Request Body

{ "config": { ... } }

Settings

GET/settings/privacy

Get privacy settings

PUT/settings/privacy

Update privacy settings

GET/settings/notifications

Get notification preferences

Themes

GET/themes

List custom themes

GET/themes/preferences

Get theme preferences

POST/themes

Create custom theme

PUT/themes/{themeId}

Update theme

DELETE/themes/{themeId}

Delete theme

Muting

GET/muting

List muted events and posts. Filter with ?type=events or ?type=posts.

POST/muting/events/{eventId}

Mute event

DELETE/muting/events/{eventId}

Unmute event

POST/muting/posts/{postId}

Mute post

DELETE/muting/posts/{postId}

Unmute post

Reports

POST/reports

Report a post, reply, or user for review.

Request Body

{
  "targetType": "post",        // "post", "reply", or "user"
  "targetId": "k178...",
  "reason": "Spam or abuse"
}

Admin

GET/admin/users

Admin only.

DELETE/admin/users/{userId}

Admin only. Deletes user and associated data.

PUT/admin/users/{userId}/role

Admin only.

GET/admin/events

Admin only.

Response Format (v2)

Success

Data is returned directly — no envelope. HTTP status code indicates success (200, 201, 204).

// GET /events/{eventId} → 200
{ "id": "k170...", "title": "Team Offsite", ... }

// DELETE /events/{eventId} → 204 No Content

Error

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Event not found"
  }
}

Common Error Codes

UNAUTHORIZEDMissing or invalid API keyFORBIDDENInsufficient permissionsNOT_FOUNDResource does not existVALIDATION_ERRORInvalid request bodyGDL_PARSE_ERRORInvalid GDL expression