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
| Name | Type | Required | Notes |
|---|---|---|---|
limit | integer | no | How many items to return (1-100). min 1, max 100, default 25 |
cursor | string | no | The next_cursor of the previous page. |
action | ban, unban, kick, warn, note | no | |
status | active, completed, queued, failed | no | "active" means bans in force now. |
search | string | no | Roblox 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
| Name | Type | Required | Notes |
|---|---|---|---|
target | string | yes | Roblox user ID or username. |
reason | string | yes | Why. Shown to other staff and recorded in the history. |
display_reason | string | no | Shown to the player when they try to join. |
duration_seconds | integer or null | no | Omit or null for a permanent ban. |
exclude_alt_accounts | boolean | no | Do 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
| Name | Type | Required | Notes |
|---|---|---|---|
target | string | yes | Roblox user ID or username. |
reason | string | yes | Why. 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
| Name | Type | Required | Notes |
|---|---|---|---|
target | string | yes | Roblox user ID or username. |
reason | string | yes | Why. 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
| Name | Type | Required | Notes |
|---|---|---|---|
target | string | yes | Roblox user ID or username. |
reason | string | yes | Why. Shown to other staff and recorded in the history. |
kind | warn, note | no | "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
| Name | Type | Required | Notes |
|---|---|---|---|
text | string | yes | Shown 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
| Name | Type | Required | Notes |
|---|---|---|---|
range | 24h, 7d, 30d, 90d, 180d, 365d | no | Capped 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
| Name | Type | Required | Notes |
|---|---|---|---|
limit | integer | no | How many items to return (1-100). min 1, max 100, default 25 |
cursor | string | no | The next_cursor of the previous page. |
action | string | no | Action prefix, such as "moderation." or "moderation.ban". |
source | web, discord, in_game, admin, system, game_server, stripe, api | no | |
from | string | no | ISO-8601, such as 2026-11-01T00:00:00Z. |
to | string | no |
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
| Name | Type | Required | Notes |
|---|---|---|---|
status | open, closed, all | no | default "open" |
category_id | integer | no | max 9007199254740991 |
awaiting_staff | true, false | no | Only tickets waiting for a staff reply. |
cursor | string | no | The 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
| Name | Type | Required | Notes |
|---|---|---|---|
body | string | yes | |
internal | boolean | no | true: 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
| Name | Type | Required | Notes |
|---|---|---|---|
status | open, closed | yes | |
reason | string | no |
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
| Name | Type | Required | Notes |
|---|---|---|---|
status | pending, decided | no | default "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
| Name | Type | Required | Notes |
|---|---|---|---|
accept | boolean | yes | true lifts the ban. |
note | string | no |
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
| Name | Type | Required | Notes |
|---|---|---|---|
target | string | yes | Roblox user ID or username. |
reason | string | no |
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
| Name | Type | Required | Notes |
|---|---|---|---|
q | string | no | Roblox user ID, Discord ID or part of a username. |
cursor | string | no | The 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
| Name | Type | Required | Notes |
|---|---|---|---|
kind | string | no | |
q | string | no | |
member_id | integer | no | A staff member id from /staff. max 9007199254740991 |
include_voided | true, false | no | |
limit | integer | no | min 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
| Name | Type | Required | Notes |
|---|---|---|---|
status | open, preparing, ready, completed, canceled | no | |
q | string | no | |
member_id | integer | no | max 9007199254740991 |
limit | integer | no | min 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
| Name | Type | Required | Notes |
|---|---|---|---|
range | 7, 30, 90, week | no | Days, or "week" for the current week in the experience’s calendar. default "30" |
sort | time, actions | no | default "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
| Name | Type | Required | Notes |
|---|---|---|---|
label | string | yes | |
url | string | yes | https only, on a public address. |
events | array | no | Groups or single events to send. Empty or omitted: everything. default [] |
payload_version | v1, legacy | no | "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
| Name | Type | Required | Notes |
|---|---|---|---|
label | string | no | |
events | array | no | Groups or single events to send. Empty or omitted: everything. |
active | boolean | no | false pauses it; true resumes one that was paused or switched off. |
payload_version | v1, legacy | no | "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
| Name | Type | Required | Notes |
|---|---|---|---|
limit | integer | no | min 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
}