Build custom integrations, bots, and tools with the FiveRoster REST API. Manage rosters, track shifts, and automate player management from your own applications.
Base URL
https://fiveroster.com/api/v1
All API v1 endpoints require an API key passed via the X-API-KEY header.
X-API-KEY: your_api_key_here
Free for All Users — Every FiveRoster user can generate API keys and use the full API, regardless of subscription tier.
All v1 endpoints are rate-limited to 50 requests per minute per IP address. Exceeding this limit will return a 429 Too Many Requests response.
All errors follow a standard format:
{
"error": {
"code": "error_code_string",
"message": "Human-readable description of the error"
}
}
| Status | Code | Description |
|---|---|---|
| 400 | missing_* | A required parameter is missing |
| 401 | missing_api_key | No API key was provided |
| 401 | invalid_api_key | The API key does not exist |
| 401 | expired_api_key | The API key has expired |
| 403 | premium_required | The API key creator no longer has premium |
| 404 | roster_not_found | Roster not found for this guild |
| 404 | rank_not_found | Rank not found for this roster |
| 404 | player_not_found | Player not found on this roster |
| 404 | custom_field_not_found | Custom field not found for this roster |
| 429 | — | Rate limit exceeded |
| 500 | — | Internal server error |
/api/v1/rosters
Retrieve all rosters for your guild.
Response
200 OK{
"data": [
{
"roster_uuid": "abc-123-def",
"name": "Los Santos Police Department",
"guild_id": "123456789"
}
]
}
/api/v1/rosters/{roster_uuid}
Retrieve detailed information about a single roster, including its ranks, flags and custom field names.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
Response
200 OK{
"data": {
"roster_uuid": "abc-123-def",
"name": "Los Santos Police Department",
"guild_id": "123456789",
"ranks": [...],
"flags": [...],
"custom_fields": [
{ "id": 1, "name": "Badge Number", "position": 1, "is_dynamic": true, "dynamic_source": "callsign" },
{ "id": 2, "name": "Assigned Vehicle", "position": 2, "is_dynamic": false, "dynamic_source": null }
]
}
}
/api/v1/rosters/{roster_uuid}/players
Retrieve all players on a roster, sorted by rank position. Every player includes a custom_fields array holding the roster's custom fields and that player's value for each (null when unset).
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
Response
200 OK{
"data": [
{
"player_id": "123456789012345678",
"player_name": "John Doe",
"rank": "Officer",
"callsign": "A-01",
"status": "Active",
"flags": [1, 3],
"strikes": [],
"notes": null,
"custom_fields": [
{ "id": 1, "name": "Badge Number", "value": "101" },
{ "id": 2, "name": "Assigned Vehicle", "value": "Unit 14" }
],
"promoted_at": "2025-06-15T12:00:00Z"
}
]
}
/api/v1/rosters/{roster_uuid}/ranks
Retrieve all ranks for a roster, ordered by position. Ranks where is_section is true are section headers and cannot be used as promotion/demotion targets.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
Response
200 OK{
"ranks": [
{
"rank_uuid": "rank-uuid-1",
"name": "Chief of Police",
"position": 1,
"is_section": false,
"shortcode": "COP"
}
]
}
/api/v1/rosters/{roster_uuid}/ranks/{rank_uuid}/enroll
Add a new player to a roster at a specific rank.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
| rank_uuid | string | URL | The UUID of the target rank |
| member_id | string | Body | Discord user ID of the player to enroll |
Request Body
{ "member_id": "123456789012345678" }
Response
200 OK{
"message": "Player promoted successfully with dynamic callsign.",
"callsign": "A-01",
"new_rank_name": "Officer"
}
/api/v1/rosters/{roster_uuid}/players/{member_id}
Remove a player from a roster entirely. This will remove their Discord roles and reset their nickname if applicable.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
| member_id | string | URL | Discord user ID of the player |
Response
200 OK{ "message": "Player cleared from roster successfully" }
/api/v1/rosters/{roster_uuid}/members/{member_id}/promote
Promote a player to a higher rank. This will update their Discord roles, assign a new callsign, and post an announcement to configured channels.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
| member_id | string | URL | Discord user ID of the player |
| rank_uuid | string | Body | The UUID of the target rank (must be higher than current) |
| clear_player_data | boolean | Body | Optional. Clears strikes, flags, and notes. Default: false |
Request Body
{ "rank_uuid": "target-rank-uuid", "clear_player_data": false }
Response
200 OK{
"message": "Player promoted successfully with dynamic callsign.",
"callsign": "A-03",
"new_rank_name": "Sergeant"
}
/api/v1/rosters/{roster_uuid}/members/{member_id}/demote
Demote a player to a lower rank. This will update their Discord roles, assign a new callsign, and post an announcement to configured demotion channels.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
| member_id | string | URL | Discord user ID of the player |
| rank_uuid | string | Body | The UUID of the target rank (must be lower than current) |
| clear_player_data | boolean | Body | Optional. Clears strikes, flags, and notes. Default: false |
Request Body
{ "rank_uuid": "target-rank-uuid", "clear_player_data": false }
Response
200 OK{
"message": "Player demoted successfully with dynamic callsign.",
"callsign": "C-12",
"new_rank_name": "Officer"
}
/api/v1/rosters/{roster_uuid}/flags
Retrieve all flags (divisions) for a roster.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
Response
200 OK{
"flags": [
{ "id": 1, "name": "Patrol Division", "position": 1 },
{ "id": 2, "name": "Detective Bureau", "position": 2 }
]
}
/api/v1/rosters/{roster_uuid}/members/{member_id}/flags
Retrieve all flags assigned to a specific member on a roster.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
| member_id | string | URL | Discord user ID of the player |
Response
200 OK{
"member_id": "123456789012345678",
"flags": [
{ "id": 1, "name": "Patrol Division", "position": 1 }
]
}
/api/v1/rosters/{roster_uuid}/members/{member_id}/flags
Add a flag (division) to a member.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
| member_id | string | URL | Discord user ID of the player |
| flag_id | integer | Body | The ID of the flag to assign |
Request Body
{ "flag_id": 1 }
Response
200 OK{
"message": "Flag added to player successfully",
"member_id": "123456789012345678",
"flag": { "id": 1, "name": "Patrol Division" }
}
/api/v1/rosters/{roster_uuid}/members/{member_id}/flags/{flag_id}
Remove a flag (division) from a member.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
| member_id | string | URL | Discord user ID of the player |
| flag_id | integer | URL | The ID of the flag to remove |
Response
200 OK{
"message": "Flag removed from player successfully",
"member_id": "123456789012345678",
"flag": { "id": 1, "name": "Patrol Division" }
}
Custom fields are the extra columns you add to a roster (badge number, vehicle, phone number, and so on). Field names are also returned by Get Specific Roster, and each player's values by Get Roster Players.
/api/v1/rosters/{roster_uuid}/custom-fields
Retrieve every custom field configured on a roster, ordered by column position. Use the returned ids when updating a player's values. Fields where is_dynamic is true are calculated by FiveRoster (for example a badge number derived from the callsign) and cannot be set manually.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
Response
200 OK{
"custom_fields": [
{
"id": 1,
"name": "Badge Number",
"position": 1,
"is_dynamic": true,
"dynamic_source": "callsign"
},
{
"id": 2,
"name": "Assigned Vehicle",
"position": 2,
"is_dynamic": false,
"dynamic_source": null
}
]
}
/api/v1/rosters/{roster_uuid}/members/{member_id}/custom-fields
Set custom field values for a single player. Only the fields you send are changed; any field you leave out keeps its current value. POST is accepted as well as PUT. Dynamic fields are managed automatically and are returned under skipped instead of being overwritten.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
| member_id | string | URL | Discord user ID of the player |
| custom_fields | array | Body | Array of { "id": <custom field id>, "value": <string|null> } objects. Send null to clear a value. Max 255 characters per value. |
Request Body
{ "custom_fields": [{ "id": 2, "value": "Unit 14" }, { "id": 3, "value": null }] }
Response
200 OK{
"message": "Custom fields updated successfully",
"member_id": "123456789012345678",
"custom_fields": [
{ "id": 2, "name": "Assigned Vehicle", "value": "Unit 14" },
{ "id": 3, "name": "Phone Number", "value": null }
],
"skipped": []
}
All shift endpoints support authentication via either X-API-KEY header or X-BOT-TOKEN header. When using X-BOT-TOKEN, a guild_id parameter is required.
/api/v1/shifts/start
Start a new shift for a player.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | Body | UUID of the roster |
| player_id | string | Body | Discord user ID |
| flag_id | integer | Body | Optional. Division/flag ID to track shift under |
Request Body
{ "roster_uuid": "abc-123-def", "player_id": "123456789012345678", "flag_id": 1 }
Response
201 Created{
"message": "Shift started successfully",
"shift": {
"id": 42,
"roster_uuid": "abc-123-def",
"roster_name": "Los Santos Police Department",
"player_id": "123456789012345678",
"flag_id": 1,
"flag_name": "Patrol Division",
"started_at": "2025-06-15T18:30:00+00:00"
}
}
/api/v1/shifts/end
End the currently active shift for a player.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| player_id | string | Body | Discord user ID |
Request Body
{ "player_id": "123456789012345678" }
Response
200 OK{
"message": "Shift ended successfully",
"shift": {
"id": 42,
"started_at": "2025-06-15T18:30:00+00:00",
"ended_at": "2025-06-15T20:45:00+00:00",
"duration_seconds": 8100,
"formatted_duration": "2h 15m"
}
}
/api/v1/shifts/pause
Put an active shift on a break. The shift stays open and keeps its start time and division, but the clock stops and the break is deducted from the hours the shift records. Paused time never counts toward quotas, leaderboards or analytics.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| player_id | string | Body | Discord user ID |
Request Body
{ "player_id": "123456789012345678" }
Response
200 OK{
"message": "Shift paused successfully",
"shift": {
"id": 42,
"roster_uuid": "abc-123-def",
"roster_name": "Los Santos Police Department",
"player_id": "123456789012345678",
"flag_id": 1,
"started_at": "2025-06-15T18:30:00+00:00",
"is_paused": true,
"paused_at": "2025-06-15T19:30:00+00:00",
"paused_seconds": 0,
"pause_count": 1,
"duration_seconds": 3600,
"formatted_duration": "1h 0m"
}
}
404 with no_active_shift when the player has no open shift, and 409 with already_paused when it is already on a break. The 409 is safe to ignore: the running break is left alone, so a repeated request never restarts it or loses banked time, and the current state comes back alongside the error.
/api/v1/shifts/resume
Put a paused shift back on the clock. The break is banked and the shift carries on from where it stopped. Ending a paused shift needs no special handling, as End Shift closes the open break itself.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| player_id | string | Body | Discord user ID |
Request Body
{ "player_id": "123456789012345678" }
Response
200 OK{
"message": "Shift resumed successfully",
"shift": {
"id": 42,
"roster_uuid": "abc-123-def",
"roster_name": "Los Santos Police Department",
"player_id": "123456789012345678",
"flag_id": 1,
"started_at": "2025-06-15T18:30:00+00:00",
"is_paused": false,
"paused_at": null,
"paused_seconds": 900,
"pause_count": 1,
"duration_seconds": 3600,
"formatted_duration": "1h 0m"
}
}
/api/v1/shifts/status?player_id={player_id}
Check if a player is currently on shift. A paused shift still reports on_shift: true, since a break is not a clock-out. Read is_paused to tell the two apart.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| player_id | string | Query | Discord user ID |
Response
200 OK{
"on_shift": true,
"shift": {
"id": 42,
"roster_uuid": "abc-123-def",
"roster_name": "Los Santos Police Department",
"started_at": "2025-06-15T18:30:00+00:00",
"is_paused": false,
"paused_at": null,
"paused_seconds": 0,
"pause_count": 0,
"duration_seconds": 3600,
"formatted_duration": "1h 0m"
}
}
/api/v1/shifts/active
Retrieve all currently active shifts for your guild. Optionally filter by roster or division. Members on a break are included, with is_paused set and a duration that has stopped counting.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | Query | Optional. Filter by roster |
| flag_id | integer | Query | Optional. Filter by division/flag |
Response
200 OK{
"count": 3,
"shifts": [
{
"player_id": "123456789012345678",
"roster_name": "Los Santos Police Department",
"flag_name": "Patrol Division",
"started_at": "2025-06-15T18:30:00+00:00",
"duration_seconds": 3600,
"formatted_duration": "1h 0m",
"is_paused": false,
"paused_seconds": 0
}
]
}
/api/v1/shifts/leaderboard
Get the top shift contributors for your guild.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| period | string | Query | Optional. week (default), month, or all-time |
| roster_uuid | string | Query | Optional. Filter by roster |
| flag_id | integer | Query | Optional. Filter by division/flag |
| limit | integer | Query | Optional. Number of results (default: 10, max: 25) |
Response
200 OK{
"period": "week",
"leaderboard": [
{
"rank": 1,
"player_id": "123456789012345678",
"total_hours": 10.0,
"formatted_time": "10h 0m",
"shift_count": 5
}
]
}
/api/v1/shifts/hours
Get shift hour statistics for a specific player. Set include_breakdown=true for division breakdown.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| player_id | string | Query | Discord user ID |
| roster_uuid | string | Query | Optional. Filter by roster |
| flag_id | integer | Query | Optional. Filter by division/flag |
| include_breakdown | boolean | Query | Optional. Include breakdown by division |
Response
200 OK{
"player_id": "123456789012345678",
"weekly": { "hours": 5.0, "formatted": "5h", "shift_count": 3 },
"monthly": { "hours": 20.0, "formatted": "20h", "shift_count": 12 },
"total": { "hours": 100.0, "formatted": "100h", "shift_count": 50 }
}
/api/v1/shifts/quota-progress
Get a player's progress toward active shift quotas.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| player_id | string | Query | Discord user ID |
| roster_uuid | string | Query | Optional. Filter by roster |
Response
200 OK{
"has_active_quotas": true,
"quotas": [
{
"quota_name": "Weekly Minimum",
"required_formatted": "4h",
"completed_formatted": "3h",
"percentage": 75.0,
"is_met": false,
"time_remaining": "2 days 4 hours"
}
]
}
/api/v1/rosters/{roster_uuid}/shifts/analytics
Get comprehensive shift analytics for a specific roster.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | URL | The UUID of the roster |
Response
200 OK{
"active_shifts_count": 3,
"weekly_hours": 45.5,
"monthly_hours": 180.0,
"average_shift_hours": 2.3,
"daily_breakdown": [
{ "date": "2025-06-09", "day": "Mon", "hours": 8.5 }
],
"top_contributors": [
{ "player_id": "123456789012345678", "total_hours": 10.5 }
]
}
/api/v1/shifts/division-stats?roster_uuid={roster_uuid}
Get shift statistics broken down by division/flag for a roster.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_uuid | string | Query | UUID of the roster |
Response
200 OK{
"roster_name": "Los Santos Police Department",
"divisions": [
{
"flag_name": "Patrol Division",
"active_count": 3,
"weekly_formatted": "45h 30m",
"member_count": 12
}
]
}
/api/v1/shifts/guild-rosters
Retrieve all rosters in your guild that have shift tracking enabled.
Response
200 OK{
"rosters": [
{ "roster_uuid": "abc-123-def", "name": "Los Santos Police Department" }
]
}
/api/v1/shifts/rosters?player_id={player_id}
Retrieve rosters where a specific player is enrolled and shift tracking is enabled.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| player_id | string | Query | Discord user ID |
Response
200 OK{
"rosters": [
{ "roster_uuid": "abc-123-def", "name": "Los Santos Police Department" }
]
}
/api/v1/shifts/player-flags?player_id={player_id}&roster_uuid={roster_uuid}
Retrieve a player's assigned flags/divisions for a roster. Useful for determining which division to start a shift under.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| player_id | string | Query | Discord user ID |
| roster_uuid | string | Query | UUID of the roster |
Response
200 OK{
"flags": [
{ "id": 1, "name": "Patrol Division" },
{ "id": 2, "name": "Detective Bureau" }
]
}
/api/v1/rosters/{roster_id}/loa
Get all LOA requests for a roster. Optionally filter by status.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
| status | string | query | Filter by status: pending, approved, denied, cancelled, expired |
Response
200{
"data": [
{
"id": 1,
"player_id": "123456789",
"player_name": "Senior Officer | C-12 - Ryneide",
"start_date": "2026-04-13",
"end_date": "2026-04-17",
"reason": "Vacation",
"status": "approved",
"approved_by": "987654321",
"approved_at": "2026-04-12T18:30:00Z"
}
]
}
/api/v1/rosters/{roster_id}/loa
Submit a new LOA request for a player. The request will be created with pending status.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
Request Body
{
"player_id": "123456789",
"start_date": "2026-04-13",
"end_date": "2026-04-17",
"reason": "Vacation"
}
Response
201{
"success": true,
"message": "LOA request created.",
"data": {
"id": 1,
"status": "pending"
}
}
/api/v1/rosters/{roster_id}/loa/{loa_id}/approve
Approve a pending LOA request. This will apply the configured status or flag to the player.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
| loa_id | integer | path | The LOA request ID |
Response
200{
"success": true,
"message": "LOA request approved."
}
/api/v1/rosters/{roster_id}/loa/{loa_id}/deny
Deny a pending LOA request with an optional reason. The player will be notified via DM.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
| loa_id | integer | path | The LOA request ID |
Request Body
{
"denial_reason": "Insufficient notice period"
}
Response
200{
"success": true,
"message": "LOA request denied."
}
/api/v1/rosters/{roster_id}/loa/{loa_id}/cancel
Cancel a pending or approved LOA. If the LOA was approved, the player's status/flag will be reverted.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
| loa_id | integer | path | The LOA request ID |
Response
200{
"success": true,
"message": "LOA request cancelled."
}
/api/v1/rosters/{roster_id}/members/{member_id}/strikes
Get all strikes for a specific player on a roster.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
| member_id | string | path | The Discord user ID of the player |
Response
200{
"data": [
{
"id": 1,
"player_id": "123456789",
"strike_info": "Failure to follow SOP",
"issued_by": "987654321",
"expires_at": null,
"created_at": "2026-04-10T14:00:00Z"
}
],
"total": 1,
"max_strikes": 4
}
/api/v1/rosters/{roster_id}/members/{member_id}/strikes
Add a strike to a player. If the player reaches a strike threshold with automated actions configured, those actions will be triggered.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
| member_id | string | path | The Discord user ID of the player |
Request Body
{
"reason": "Failure to follow SOP",
"expires_at": "2026-05-10"
}
Response
201{
"success": true,
"message": "Strike added.",
"data": {
"id": 2,
"total_strikes": 2,
"max_strikes": 4
}
}
/api/v1/rosters/{roster_id}/strikes/{strike_id}
Remove a specific strike from a player.
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
| strike_id | integer | path | The strike ID to remove |
Response
200{
"success": true,
"message": "Strike removed."
}
/api/v1/rosters/{roster_id}/strike-actions
Get the configured automated strike actions for a roster (thresholds and action types).
Parameters
| Name | Type | In | Description |
|---|---|---|---|
| roster_id | string | path | The roster UUID |
Response
200{
"data": [
{
"threshold": 3,
"action_type": "demote"
},
{
"threshold": 5,
"action_type": "remove"
}
]
}