Skip to content

API reference

Every endpoint with its permission, parameters and an example answer.

Generated from the API itself. To open an endpoint and send a real request with your key, use the interactive API reference. The same description is available for tools such as Postman, Insomnia and Bruno as an OpenAPI file.

GET /v1/me

Check your key. The key and, per experience, what it can do right now.

Any valid key.

Response 200

{
  "data": {
    "key": {
      "id": "12",
      "name": "Moderation bot",
      "hint": "…a1b2",
      "scopes": [
        "moderation.ban"
      ],
      "created_at": "2026-11-04T10:00:00.000Z",
      "expires_at": "2027-02-02T10:00:00.000Z"
    },
    "experiences": [
      {
        "id": "34",
        "name": "My Game",
        "permissions": [
          "moderation.ban"
        ]
      }
    ]
  }
}

GET /v1/experiences

List experiences. The experiences this key can act in, with its effective permissions in each.

Any valid key.

Response 200

{
  "data": [
    {
      "id": "34",
      "name": "My Game",
      "permissions": [
        "moderation.ban",
        "players.lookup"
      ]
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}

Get an experience. The experience and the key’s permissions in it.

Any valid key.

Response 200

{
  "data": {
    "id": "34",
    "name": "My Game",
    "universe_id": "4242",
    "verification_status": "verified",
    "icon_url": null,
    "permissions": [
      "moderation.ban"
    ]
  }
}

GET /v1/experiences/{experienceId}/players/{robloxUserId}

Look up a player. Profile, ban status, whether they are in a running server, and recent moderation records (when the key also has moderation.view_history).

Needs the players.lookup permission (Look up players).

Response 200

{
  "data": {
    "id": "156",
    "username": "Builderman",
    "avatar_url": null,
    "online": false,
    "server": null,
    "ban": {
      "known": true,
      "active": false,
      "expires_at": null,
      "display_reason": null
    },
    "recent_records": []
  }
}

GET /v1/experiences/{experienceId}/players/{robloxUserId}/stats

Read a player’s tracked stats. The current saved value of every stat tracked under Datastores. Needs an Open Cloud key with datastore read access on the experience.

Needs the stats.view permission (View player stats). The experience owner's plan must include datastores.

Response 200

{
  "data": {
    "player": {
      "id": "156",
      "username": "Builderman"
    },
    "online": false,
    "stats": [
      {
        "name": "gold",
        "label": "Gold",
        "data_store": "PlayerData",
        "state": "ok",
        "value": 50
      }
    ]
  }
}

GET /v1/experiences/{experienceId}/moderation/records

List moderation records. Bans, unbans, kicks, warnings and notes, newest first.

Needs the moderation.view_history permission (View moderation history). The experience owner's plan must include moderation.

Query parameters

NameTypeRequiredNotes
limitintegernoHow many items to return (1-100). min 1, max 100, default 25
cursorstringnoThe next_cursor of the previous page.
actionban, unban, kick, warn, noteno
statusactive, completed, queued, failedno"active" means bans in force now.
searchstringnoRoblox user ID or part of a username.

Response 200

{
  "data": [
    {
      "id": "9",
      "action": "ban",
      "status": "completed",
      "target": {
        "id": "156",
        "username": "Builderman"
      },
      "reason": "Exploiting",
      "display_reason": null,
      "duration_seconds": 86400,
      "expires_at": "2026-11-05T10:00:00.000Z",
      "issued_by": "ModName",
      "source": "api",
      "created_at": "2026-11-04T10:00:00.000Z"
    }
  ],
  "next_cursor": null
}

POST /v1/experiences/{experienceId}/moderation/bans

Ban a player. The result. A permanent ban may wait for a second person’s approval: status "pending_approval", HTTP 202.

Needs the moderation.ban permission (Ban players). The experience owner's plan must include moderation.

JSON body

NameTypeRequiredNotes
targetstringyesRoblox user ID or username.
reasonstringyesWhy. Shown to other staff and recorded in the history.
display_reasonstringnoShown to the player when they try to join.
duration_secondsinteger or nullnoOmit or null for a permanent ban.
exclude_alt_accountsbooleannoDo not ban the player’s alternate accounts.

Response 201

{
  "data": {
    "status": "completed",
    "message": "Builderman was banned.",
    "record_id": "9",
    "approval_id": null,
    "target": {
      "id": "156",
      "username": "Builderman"
    }
  }
}

POST /v1/experiences/{experienceId}/moderation/unbans

Lift a ban. The result.

Needs the moderation.unban permission (Unban players). The experience owner's plan must include moderation.

JSON body

NameTypeRequiredNotes
targetstringyesRoblox user ID or username.
reasonstringyesWhy. Shown to other staff and recorded in the history.

Response 201

{
  "data": {
    "status": "completed",
    "message": "Builderman was unbanned.",
    "record_id": "10",
    "approval_id": null,
    "target": {
      "id": "156",
      "username": "Builderman"
    }
  }
}

POST /v1/experiences/{experienceId}/moderation/kicks

Kick a player. The result. "queued" until a game server confirms the kick.

Needs the moderation.kick permission (Kick players). The experience owner's plan must include moderation.

JSON body

NameTypeRequiredNotes
targetstringyesRoblox user ID or username.
reasonstringyesWhy. Shown to other staff and recorded in the history.

Response 201

{
  "data": {
    "status": "queued",
    "message": "Kick queued for Builderman.",
    "record_id": "11",
    "approval_id": null,
    "target": {
      "id": "156",
      "username": "Builderman"
    }
  }
}

POST /v1/experiences/{experienceId}/moderation/warnings

Warn a player or add a note. The result.

Needs the moderation.warn permission (Create warnings and notes). The experience owner's plan must include moderation.

JSON body

NameTypeRequiredNotes
targetstringyesRoblox user ID or username.
reasonstringyesWhy. Shown to other staff and recorded in the history.
kindwarn, noteno"warn" counts towards escalation; "note" is only for staff. default "warn"

Response 201

{
  "data": {
    "status": "completed",
    "message": "Warning recorded for Builderman.",
    "record_id": "12",
    "approval_id": null,
    "target": {
      "id": "156",
      "username": "Builderman"
    }
  }
}

GET /v1/experiences/{experienceId}/servers

List running servers. Servers that reported in the last minutes, fullest first.

Needs the players.lookup permission (Look up players).

Response 200

{
  "data": [
    {
      "job_id": "abc-123",
      "place_id": "5000",
      "player_count": 12,
      "player_ids": [
        "156"
      ],
      "started_at": "2026-11-04T09:00:00.000Z",
      "last_seen_at": "2026-11-04T10:00:00.000Z"
    }
  ],
  "next_cursor": null
}

POST /v1/experiences/{experienceId}/announcements

Announce in every server. Whether it was sent now or queued for the next presence report.

Needs the game.announce permission (Send in-game announcements). The experience owner's plan must include in_game_integration.

JSON body

NameTypeRequiredNotes
textstringyesShown in every running server, filtered by Roblox.

Response 202

{
  "data": {
    "status": "sent",
    "text": "Server restarts in 5 minutes",
    "message": "Announcement sent to all servers via MessagingService."
  }
}

GET /v1/experiences/{experienceId}/staff

List staff. Staff by public handle (never emails or account names).

Needs the staff.view permission (View staff).

Response 200

{
  "data": [
    {
      "id": "5",
      "username": "ModName",
      "roblox_user_id": "1000",
      "discord_user_id": "700",
      "is_owner": false,
      "status": "active",
      "roles": [
        "Moderator"
      ],
      "joined_at": "2026-10-01T10:00:00.000Z",
      "expires_at": null,
      "last_active_at": "2026-11-04T09:00:00.000Z"
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/roles

List roles. Roles with their permissions and member counts.

Needs the staff.view permission (View staff).

Response 200

{
  "data": [
    {
      "id": "3",
      "name": "Moderator",
      "description": "Standard moderation.",
      "position": 400,
      "is_owner_role": false,
      "permissions": [
        "moderation.ban"
      ],
      "member_count": 4
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/analytics

Player and visit analytics. Live numbers and a time series, limited to the plan’s analytics history.

Needs the analytics.view permission (View analytics). The experience owner's plan must include analytics.

Query parameters

NameTypeRequiredNotes
range24h, 7d, 30d, 90d, 180d, 365dnoCapped by the plan’s analytics history. default "7d"

Response 200

{
  "data": {
    "range_days": 7,
    "limited_by_plan": false,
    "bucket": "day",
    "live": {
      "playing": 120,
      "visits": 100000,
      "favorites": 900
    },
    "live_servers": {
      "servers": 8,
      "players": 118
    },
    "current": {
      "avg_playing": 90,
      "peak_playing": 150,
      "visits_gained": 4000,
      "samples": 2000
    },
    "previous": {
      "avg_playing": 80,
      "peak_playing": 140,
      "visits_gained": 3500,
      "samples": 2000
    },
    "series": [
      {
        "time": "2026-11-04T00:00:00.000Z",
        "avg_playing": 90,
        "peak_playing": 150,
        "visits": 100000,
        "favorites": 900
      }
    ]
  }
}

GET /v1/experiences/{experienceId}/audit-logs

Read the audit log. Everything staff and integrations did, newest first, as far back as the plan keeps.

Needs the audit.view permission (View audit logs). The experience owner's plan must include audit_log.

Query parameters

NameTypeRequiredNotes
limitintegernoHow many items to return (1-100). min 1, max 100, default 25
cursorstringnoThe next_cursor of the previous page.
actionstringnoAction prefix, such as "moderation." or "moderation.ban".
sourceweb, discord, in_game, admin, system, game_server, stripe, apino
fromstringnoISO-8601, such as 2026-11-01T00:00:00Z.
tostringno

Response 200

{
  "data": [
    {
      "id": "77",
      "action": "moderation.ban",
      "result": "success",
      "source": "api",
      "actor": "api:Moderation bot",
      "target": {
        "type": "roblox_user",
        "id": "156"
      },
      "metadata": {
        "reason": "Exploiting"
      },
      "created_at": "2026-11-04T10:00:00.000Z"
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/tickets

List tickets. Tickets in the categories the key’s role may see, most recently active first, 30 per page.

Needs the tickets.view permission (View tickets).

Query parameters

NameTypeRequiredNotes
statusopen, closed, allnodefault "open"
category_idintegernomax 9007199254740991
awaiting_stafftrue, falsenoOnly tickets waiting for a staff reply.
cursorstringnoThe next_cursor of the previous page.

Response 200

{
  "data": [
    {
      "id": "8",
      "number": 12,
      "category_id": "2",
      "subject": "Cheater in server",
      "status": "open",
      "awaiting": "staff",
      "player": {
        "roblox_user_id": "156",
        "username": "Builderman",
        "roblox_verified": true,
        "discord_user_id": null,
        "discord_username": null
      },
      "reported_player": {
        "roblox_user_id": "999",
        "username": "Exploiter"
      },
      "assigned_to": null,
      "created_at": "2026-11-04T09:00:00.000Z",
      "updated_at": "2026-11-04T09:05:00.000Z"
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/tickets/{ticketId}

Get a ticket with its messages. The ticket, the player’s answers and every message, including staff-only notes (internal: true).

Needs the tickets.view permission (View tickets).

Response 200

{
  "data": {
    "id": "8",
    "number": 12,
    "subject": "Cheater in server",
    "status": "open",
    "awaiting": "staff",
    "answers": [
      {
        "label": "What happened?",
        "value": "Flying"
      }
    ],
    "messages": [
      {
        "id": "30",
        "author_type": "player",
        "author": "Builderman",
        "body": "Someone is flying",
        "internal": false,
        "created_at": "2026-11-04T09:00:00.000Z"
      }
    ]
  }
}

POST /v1/experiences/{experienceId}/tickets/{ticketId}/replies

Reply to a ticket. Confirmation. The reply reaches the player like a dashboard reply; internal: true keeps it among staff.

Needs the tickets.respond permission (Reply to and close tickets).

JSON body

NameTypeRequiredNotes
bodystringyes
internalbooleannotrue: a note only staff can see, not sent to the player. default false

Response 201

{
  "data": {
    "ticket_id": "8",
    "internal": false
  }
}

POST /v1/experiences/{experienceId}/tickets/{ticketId}/status

Close or reopen a ticket. The new status.

Needs the tickets.respond permission (Reply to and close tickets).

JSON body

NameTypeRequiredNotes
statusopen, closedyes
reasonstringno

Response 200

{
  "data": {
    "ticket_id": "8",
    "status": "closed"
  }
}

GET /v1/experiences/{experienceId}/appeals

List ban appeals. Up to the 50 most recent pending or decided appeals, with the ban they concern.

Needs the moderation.appeals permission (Review ban appeals).

Query parameters

NameTypeRequiredNotes
statuspending, decidednodefault "pending"

Response 200

{
  "data": [
    {
      "id": "4",
      "status": "pending",
      "player": {
        "roblox_user_id": "156",
        "username": "Builderman",
        "discord_user_id": null
      },
      "message": "I was falsely banned.",
      "ban": null,
      "decided_by": null,
      "decision_note": null,
      "decided_at": null,
      "created_at": "2026-11-04T09:00:00.000Z"
    }
  ],
  "next_cursor": null
}

POST /v1/experiences/{experienceId}/appeals/{appealId}/decision

Decide an appeal. The decision. Accepting also lifts the ban, which takes the moderation.unban permission as well, and fails (leaving the appeal pending) if Roblox refuses.

Needs the moderation.appeals permission (Review ban appeals). The experience owner's plan must include moderation.

JSON body

NameTypeRequiredNotes
acceptbooleanyestrue lifts the ban.
notestringno

Response 201

{
  "data": {
    "appeal_id": "4",
    "decision": "accepted",
    "message": "Appeal accepted: Builderman was unbanned."
  }
}

GET /v1/experiences/{experienceId}/watchlist

List the watchlist. Watched players and whether they are in a server now.

Needs the players.lookup permission (Look up players).

Response 200

{
  "data": [
    {
      "roblox_user_id": "156",
      "username": "Builderman",
      "reason": "Alt of a banned player",
      "added_by": "ModName",
      "online": false,
      "created_at": "2026-11-04T09:00:00.000Z"
    }
  ],
  "next_cursor": null
}

POST /v1/experiences/{experienceId}/watchlist

Watch a player. The entry. Staff are alerted when the player joins a server.

Needs the moderation.watchlist permission (Manage the watchlist).

JSON body

NameTypeRequiredNotes
targetstringyesRoblox user ID or username.
reasonstringno

Response 201

{
  "data": {
    "roblox_user_id": "156",
    "username": "Builderman",
    "reason": "Alt of a banned player",
    "added_by": "api:Moderation bot",
    "online": false,
    "created_at": "2026-11-04T09:00:00.000Z"
  }
}

DELETE /v1/experiences/{experienceId}/watchlist/{robloxUserId}

Stop watching a player. Confirmation.

Needs the moderation.watchlist permission (Manage the watchlist).

Response 200

{
  "data": {
    "roblox_user_id": "156",
    "removed": true
  }
}

GET /v1/experiences/{experienceId}/forms

List forms. Forms with their response counts (null for forms the key’s role may not read).

Needs the forms.view permission (View form responses).

Response 200

{
  "data": [
    {
      "id": "2",
      "slug": "staff-application",
      "title": "Staff application",
      "description": null,
      "open": true,
      "responses": 14,
      "last_response_at": "2026-11-03T18:00:00.000Z",
      "updated_at": "2026-10-20T12:00:00.000Z"
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/forms/{formId}/responses

List form responses. Responses, newest first, 20 per page.

Needs the forms.view permission (View form responses).

Query parameters

NameTypeRequiredNotes
qstringnoRoblox user ID, Discord ID or part of a username.
cursorstringnoThe next_cursor of the previous page.

Response 200

{
  "data": [
    {
      "id": "31",
      "player": {
        "roblox_user_id": "156",
        "username": "Builderman",
        "roblox_verified": true,
        "discord_user_id": null
      },
      "answers": [
        {
          "label": "Why do you want to join?",
          "value": "I like helping."
        }
      ],
      "created_at": "2026-11-03T18:00:00.000Z"
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/staff-records

List staff records. Warnings, commendations, notes and more kept about staff the key’s role is above. Needs the Staff records module.

Needs the records.view permission (View staff records of staff below you). The experience owner's plan must include staff_management.

Query parameters

NameTypeRequiredNotes
kindstringno
qstringno
member_idintegernoA staff member id from /staff. max 9007199254740991
include_voidedtrue, falseno
limitintegernomin 1, max 200, default 100

Response 200

{
  "data": [
    {
      "id": "5",
      "member": {
        "id": "7",
        "name": "ModName"
      },
      "kind": "warning",
      "status": "active",
      "title": "Late to shift",
      "reason": null,
      "from_role": null,
      "to_role": null,
      "expires_at": null,
      "until_at": null,
      "voided_at": null,
      "issued_by": "AdminName",
      "source": "web",
      "created_at": "2026-11-01T10:00:00.000Z"
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/orders

List orders. Customer orders, newest first. Needs the Orders module and both permissions.

Needs the orders.view and orders.log permissions (See all orders and order statistics). The experience owner's plan must include staff_management.

Query parameters

NameTypeRequiredNotes
statusopen, preparing, ready, completed, canceledno
qstringno
member_idintegernomax 9007199254740991
limitintegernomin 1, max 300, default 100

Response 200

{
  "data": [
    {
      "id": "40",
      "number": 118,
      "status": "open",
      "customer": {
        "roblox_user_id": "156",
        "name": "Builderman"
      },
      "items": [
        {
          "name": "Burger",
          "qty": 2,
          "price": 50
        }
      ],
      "total": 100,
      "note": null,
      "cashier": {
        "id": "7",
        "name": "ModName"
      },
      "assigned_to": null,
      "source": "dashboard",
      "created_at": "2026-11-04T10:00:00.000Z",
      "completed_at": null
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/activity

Staff activity. In-game time and actions per staff member, as member ids and roles (look names up in /staff). Needs the Activity module and both permissions.

Needs the activity.view_all and staff.view permissions (See everyone's activity and the leaderboard). The experience owner's plan must include staff_management.

Query parameters

NameTypeRequiredNotes
range7, 30, 90, weeknoDays, or "week" for the current week in the experience’s calendar. default "30"
sorttime, actionsnodefault "time"

Response 200

{
  "data": {
    "from": "2026-10-06",
    "median_in_game_seconds": 7200,
    "median_actions": 12,
    "in_game_time_available": true,
    "board": [
      {
        "rank": 1,
        "member_id": "7",
        "role": "Moderator",
        "in_game_seconds": 18000,
        "actions": 40,
        "measured": true
      }
    ],
    "trend": [
      {
        "week": "2026-10-26",
        "in_game_seconds": 30000,
        "actions": 90
      }
    ]
  }
}

GET /v1/experiences/{experienceId}/webhooks

List webhooks. The experience’s webhooks (never their secrets).

Needs the webhooks.manage permission (Manage outgoing webhooks).

Response 200

{
  "data": [
    {
      "id": "3",
      "label": "Ops channel",
      "url": "https://example.com/rowarden",
      "events": [
        "moderation"
      ],
      "status": "active",
      "payload_version": "v1",
      "consecutive_failures": 0,
      "last_error": null,
      "last_success_at": "2026-11-04T10:00:00.000Z",
      "created_at": "2026-11-01T10:00:00.000Z"
    }
  ],
  "next_cursor": null
}

POST /v1/experiences/{experienceId}/webhooks

Add a webhook. The webhook, with its signing secret in secret. The secret is shown this once; it is never kept for an Idempotency-Key replay.

Needs the webhooks.manage permission (Manage outgoing webhooks).

JSON body

NameTypeRequiredNotes
labelstringyes
urlstringyeshttps only, on a public address.
eventsarraynoGroups or single events to send. Empty or omitted: everything. default []
payload_versionv1, legacyno"v1" is the documented body; "legacy" the original one. default "v1"

Response 201

{
  "data": {
    "id": "3",
    "label": "Ops channel",
    "url": "https://example.com/rowarden",
    "events": [
      "moderation"
    ],
    "status": "active",
    "payload_version": "v1",
    "consecutive_failures": 0,
    "last_error": null,
    "last_success_at": "2026-11-04T10:00:00.000Z",
    "created_at": "2026-11-01T10:00:00.000Z",
    "secret": "whsec_…"
  }
}

PATCH /v1/experiences/{experienceId}/webhooks/{webhookId}

Change a webhook. The webhook after the change. The address cannot be changed.

Needs the webhooks.manage permission (Manage outgoing webhooks).

JSON body

NameTypeRequiredNotes
labelstringno
eventsarraynoGroups or single events to send. Empty or omitted: everything.
activebooleannofalse pauses it; true resumes one that was paused or switched off.
payload_versionv1, legacyno"v1" is the documented body; "legacy" the original one.

Response 200

{
  "data": {
    "id": "3",
    "label": "Ops channel",
    "url": "https://example.com/rowarden",
    "events": [
      "moderation"
    ],
    "status": "active",
    "payload_version": "v1",
    "consecutive_failures": 0,
    "last_error": null,
    "last_success_at": "2026-11-04T10:00:00.000Z",
    "created_at": "2026-11-01T10:00:00.000Z"
  }
}

DELETE /v1/experiences/{experienceId}/webhooks/{webhookId}

Delete a webhook. Confirmation. Its delivery log is deleted too.

Needs the webhooks.manage permission (Manage outgoing webhooks).

Response 200

{
  "data": {
    "id": "3",
    "deleted": true
  }
}

POST /v1/experiences/{experienceId}/webhooks/{webhookId}/rotate-secret

Replace the signing secret. The new secret, once. Both secrets sign for 24 hours.

Needs the webhooks.manage permission (Manage outgoing webhooks).

Response 201

{
  "data": {
    "id": "3",
    "secret": "whsec_…"
  }
}

POST /v1/experiences/{experienceId}/webhooks/{webhookId}/test

Send a test event. Confirmation. A ping event is delivered within half a minute.

Needs the webhooks.manage permission (Manage outgoing webhooks).

Response 202

{
  "data": {
    "id": "3",
    "queued": true
  }
}

GET /v1/experiences/{experienceId}/webhooks/{webhookId}/deliveries

List recent deliveries. Deliveries of the last 14 days, newest first.

Needs the webhooks.manage permission (Manage outgoing webhooks).

Query parameters

NameTypeRequiredNotes
limitintegernomin 1, max 100, default 50

Response 200

{
  "data": [
    {
      "id": "4812",
      "event": "moderation.ban",
      "status": "delivered",
      "attempts": 1,
      "last_http_status": 200,
      "last_error": null,
      "created_at": "2026-11-04T10:00:00.000Z",
      "delivered_at": "2026-11-04T10:00:01.000Z"
    }
  ],
  "next_cursor": null
}

POST /v1/experiences/{experienceId}/webhooks/{webhookId}/deliveries/{deliveryId}/retry

Retry a failed delivery. Confirmation. Only failed deliveries can be retried.

Needs the webhooks.manage permission (Manage outgoing webhooks).

Response 202

{
  "data": {
    "id": "4812",
    "queued": true
  }
}

GET /v1/experiences/{experienceId}/sessions

List sessions and shifts. Upcoming sessions, then recent past ones, with how many booked and attended. Needs the Sessions module.

Needs the staff.view permission (View staff). The experience owner's plan must include staff_management.

Response 200

{
  "data": [
    {
      "id": "6",
      "kind": "training",
      "title": "Moderator training",
      "description": null,
      "status": "scheduled",
      "canceled_reason": null,
      "starts_at": "2026-11-08T18:00:00.000Z",
      "ends_at": "2026-11-08T19:00:00.000Z",
      "host": {
        "id": "7",
        "name": "ModName"
      },
      "capacity": 20,
      "target_attendees": null,
      "booked": 12,
      "attended": 0,
      "absent": 0,
      "series_id": null
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/time-off

Time off. Requests waiting for a decision (from staff below the key’s role), and staff on approved leave in the next 30 days. Needs the Time off module.

Needs the timeoff.review permission (Approve or reject time off of staff below you). The experience owner's plan must include staff_management.

Response 200

{
  "data": [
    {
      "id": "2",
      "member": {
        "id": "7",
        "name": "ModName"
      },
      "category": "Holiday",
      "status": "approved",
      "starts_on": "2026-11-10",
      "ends_on": "2026-11-17",
      "reason": null,
      "on_leave_now": false,
      "created_at": "2026-11-01T10:00:00.000Z"
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/assignments

Assignments and quotas. Every assignment with each member’s progress in the current period. Needs the Assignments module.

Needs the assignments.manage permission (Create assignments and quotas and tick off manual requirements). The experience owner's plan must include staff_management.

Response 200

{
  "data": [
    {
      "id": "3",
      "title": "Weekly quota",
      "description": null,
      "period": "weekly",
      "active": true,
      "due_at": null,
      "requirements": [
        {
          "id": "r1",
          "type": "ingame_time",
          "target": 7200,
          "label": null
        }
      ],
      "window": {
        "starts_on": "2026-11-02",
        "ends_on": "2026-11-08"
      },
      "progress": [
        {
          "member": {
            "id": "7",
            "name": "ModName"
          },
          "percent": 60,
          "completed": false,
          "excused": false,
          "behind": false
        }
      ],
      "history": [
        {
          "period_start": "2026-10-26",
          "members": 8,
          "completed": 6,
          "excused": 1
        }
      ]
    }
  ],
  "next_cursor": null
}

GET /v1/experiences/{experienceId}/promotions

Promotion recommendations. Open recommendations with their votes, then recently decided ones. Needs the Promotions module.

Needs the promotions.vote permission (Vote on promotion recommendations (for staff below you)). The experience owner's plan must include staff_management.

Response 200

{
  "data": [
    {
      "id": "9",
      "status": "open",
      "candidate": {
        "id": "7",
        "name": "ModName",
        "current_role": "Moderator"
      },
      "target_role": "Senior Moderator",
      "nominated_by": "AdminName",
      "justification": "Always on time.",
      "votes": [
        {
          "voter": "SeniorName",
          "justification": "Agreed.",
          "created_at": "2026-11-03T10:00:00.000Z"
        }
      ],
      "decided_by": null,
      "decision_note": null,
      "decided_at": null,
      "created_at": "2026-11-02T10:00:00.000Z"
    }
  ],
  "next_cursor": null
}