PulseRoster API

A REST API over your community roster. Read and write members, departments, ranks, duty time, leave, discipline, certifications and documents from your own code.

Available on the Professional plan. Create a key in Community settings → API keys. Machine-readable spec: /api/v1/openapi.json.

Authentication

Send your key as a bearer token. The X-API-Key header works too, for HTTP clients that make setting Authorization awkward.

curl https://roster.mypulsetech.com/api/v1/whoami \
  -H "Authorization: Bearer prk_a1b2c3d4_<secret>"

GET /api/v1/whoami is the first call to make: it confirms the key is live and shows which community and scopes it carries.

Responses

Every response uses the same envelope, success or failure, so a client can branch on success.

{
  "success": true,
  "data": [ ... ],
  "meta": {
    "request_id": "a1b2c3d4",
    "pagination": { "page": 1, "limit": 25, "total": 84, "total_pages": 4, "has_more": true }
  }
}

{
  "success": false,
  "error": { "code": "insufficient_scope", "message": "This key lacks the required scope: roster:write" },
  "meta": { "request_id": "e5f6a7b8" }
}

meta.request_id is also returned as the X-Request-Id header and written to our logs. Quote it in a support message and we can find the exact request.

Rate limits

120 requests per minute and 25,000 per day, per key. A key can be given a lower per-minute limit in settings.

  • X-RateLimit-Limit: the ceiling for the window closest to exhaustion.
  • X-RateLimit-Remaining: requests left. Present on successes too, so you can back off before you are throttled.
  • X-RateLimit-Reset: unix seconds when the window rolls over.
  • Retry-After: seconds to wait. Sent only with a 429.

Query conventions

Every list endpoint supports the same parameters. Which fields are filterable, sortable or searchable is listed per resource below.

  • ?page=2&limit=50: pagination. Limit caps at 100.
  • ?sort=-createdAt: sort, - for descending.
  • ?q=smith: case-insensitive search across the resource's search fields.
  • ?status=ACTIVE&status=ON_LOA: repeat an enum filter to mean "either".
  • ?createdAt.after=2026-01-01T00:00:00Z: date range on any date-time field. .before too.
  • ?include=member,rank: expand related records.

Scopes

A key carries an explicit scope list. A :write scope implies its :read counterpart. You can only grant scopes you hold the matching staff permission for, so a key is never more powerful than the person who made it.

Roster

roster:readMembers, characters, department and subdivision assignments, roster slots, callsigns.
roster:writeCreate, update and remove members; change ranks, assignments and slots.

Departments

departments:readDepartments, subdivisions and ranks.
departments:writeCreate, update and delete departments, subdivisions and ranks.

Duty & activity

timelogs:readDuty sessions, time logs, activity logs and activity requirement status.
timelogs:writeOpen and close duty sessions, create, edit and delete time logs.

Leave of absence

loa:readLOA requests across the community.
loa:writeCreate, approve, deny, extend and cancel LOA requests.

Certifications

certifications:readCertification definitions and member certifications.
certifications:writeManage certification definitions; award and revoke member certifications.

Disciplinary

disciplinary:readDisciplinary records, appeals, probations, commendations and infraction categories.
disciplinary:writeIssue, edit and delete disciplinary records; handle appeals and probations.

Documents

documents:readDocuments, folders, versions, required reading and acknowledgment status.
documents:writeCreate, edit, publish and delete documents and folders.

Applications

applications:readApplication forms, submissions and answers.
applications:writeReview submissions, change status, and manage application forms.

Community

community:readCommunity profile, roles, staff roles, custom field definitions and code tables.
community:writeUpdate community settings, roles, custom field definitions and code tables.
audit:readThe community audit log.

Errors

invalid_api_key401: missing, malformed, unknown, revoked or expired key.
insufficient_scope403: the key is valid but lacks the scope this endpoint needs.
ip_not_allowed403: the key has an IP allowlist and you are not on it.
plan_required403: the community is not on an active Professional plan.
not_found404: no such endpoint, or no such record in this community.
method_not_allowed405: that verb is not supported for this resource.
conflict409: a uniqueness constraint, or the record is still referenced.
unprocessable422: well-formed, but a referenced record does not exist.
validation_error400: a field or query parameter failed validation. See error.details.
bad_request400: malformed JSON or no writable fields supplied.
rate_limited429: slow down. Check Retry-After.
internal500: our fault. Quote meta.request_id to support.

Endpoints

22 resources. All paths are relative to https://roster.mypulsetech.com.

/api/v1/departments

A department within the community, e.g. a police or fire department.

GET/api/v1/departmentsdepartments:read
GET/api/v1/departments/{id}departments:read
POST/api/v1/departmentsdepartments:write
PATCH/api/v1/departments/{id}departments:write
DELETE/api/v1/departments/{id}departments:write
Filters:
type, isActive, slug, publicRosterEnabled, rosterMode
Sortable:
name, sortOrder, createdAt, publicRosterOrder
Searchable:
name, abbreviation
Expandable:
ranks, subdivisions

/api/v1/subdivisions

A unit within a department, e.g. Traffic, K9, SWAT.

GET/api/v1/subdivisionsdepartments:read
GET/api/v1/subdivisions/{id}departments:read
POST/api/v1/subdivisionsdepartments:write
PATCH/api/v1/subdivisions/{id}departments:write
DELETE/api/v1/subdivisions/{id}departments:write
Filters:
departmentId, isActive, hasOwnRoster, publicRosterEnabled
Sortable:
name, sortOrder, createdAt
Searchable:
name, abbreviation

/api/v1/ranks

A rank within a department. `level` orders seniority, and higher is more senior.

GET/api/v1/ranksdepartments:read
GET/api/v1/ranks/{id}departments:read
POST/api/v1/ranksdepartments:write
PATCH/api/v1/ranks/{id}departments:write
DELETE/api/v1/ranks/{id}departments:write
Filters:
departmentId, isActive, isCommandStaff, isSupervisor, level
Sortable:
level, name, sortOrder, createdAt
Searchable:
name, abbreviation

/api/v1/members

A person in the community. Identity lives on the linked user; roster state lives here.

GET/api/v1/membersroster:read
GET/api/v1/members/{id}roster:read
PATCH/api/v1/members/{id}roster:write
DELETE/api/v1/members/{id}roster:write
Filters:
status, isCommunityStaff, userId, fivemLicense, steamId
Sortable:
joinDate, createdAt, lastName, displayName, status
Searchable:
displayName, firstName, lastName
Expandable:
characters, departmentAssignments

/api/v1/characters

An in-game character belonging to a member.

GET/api/v1/charactersroster:read
GET/api/v1/characters/{id}roster:read
POST/api/v1/charactersroster:write
PATCH/api/v1/characters/{id}roster:write
DELETE/api/v1/characters/{id}roster:write
Filters:
memberId, isPrimary
Sortable:
name, createdAt
Searchable:
name

/api/v1/department-assignments

Places a member in a department at a rank. The core roster row.

GET/api/v1/department-assignmentsroster:read
GET/api/v1/department-assignments/{id}roster:read
POST/api/v1/department-assignmentsroster:write
PATCH/api/v1/department-assignments/{id}roster:write
DELETE/api/v1/department-assignments/{id}roster:write
Filters:
memberId, departmentId, rankId, status, isPrimary, characterId
Sortable:
joinDate, createdAt, callsign, badgeNumber
Searchable:
callsign, badgeNumber, unitNumber
Expandable:
member, rank, department

/api/v1/subdivision-assignments

Places an existing department assignment into a subdivision.

GET/api/v1/subdivision-assignmentsroster:read
GET/api/v1/subdivision-assignments/{id}roster:read
POST/api/v1/subdivision-assignmentsroster:write
PATCH/api/v1/subdivision-assignments/{id}roster:write
DELETE/api/v1/subdivision-assignments/{id}roster:write
Filters:
departmentAssignmentId, subdivisionId
Sortable:
joinDate, createdAt

/api/v1/roster-slots

A named position in a slot-based or hybrid roster. A null assignedMemberId means vacant.

GET/api/v1/roster-slotsroster:read
GET/api/v1/roster-slots/{id}roster:read
POST/api/v1/roster-slotsroster:write
PATCH/api/v1/roster-slots/{id}roster:write
DELETE/api/v1/roster-slots/{id}roster:write
Filters:
departmentId, subdivisionId, rankId, assignedMemberId, isActive
Sortable:
sortOrder, badgeNumber, createdAt
Searchable:
badgeNumber, name

/api/v1/member-notes

A staff note attached to a member. Private notes are visible to their author in the UI; see the caveat in the docs.

GET/api/v1/member-notesroster:read
GET/api/v1/member-notes/{id}roster:read
POST/api/v1/member-notesroster:write
PATCH/api/v1/member-notes/{id}roster:write
DELETE/api/v1/member-notes/{id}roster:write
Filters:
memberId, createdById, isPinned, isPrivate, category
Sortable:
createdAt, updatedAt
Searchable:
content

/api/v1/duty-sessions

A shift. `endedAt` is null while the member is still on duty.

GET/api/v1/duty-sessionstimelogs:read
GET/api/v1/duty-sessions/{id}timelogs:read
POST/api/v1/duty-sessionstimelogs:write
PATCH/api/v1/duty-sessions/{id}timelogs:write
DELETE/api/v1/duty-sessions/{id}timelogs:write
Filters:
memberId, departmentAssignmentId, source, activityType
Sortable:
startedAt, endedAt, createdAt
Expandable:
member

/api/v1/activity-logs

Logged hours against a department assignment. Denied entries are excluded from all totals.

GET/api/v1/activity-logstimelogs:read
GET/api/v1/activity-logs/{id}timelogs:read
POST/api/v1/activity-logstimelogs:write
PATCH/api/v1/activity-logs/{id}timelogs:write
DELETE/api/v1/activity-logs/{id}timelogs:write
Filters:
departmentAssignmentId, activityType, verified, loggedById
Sortable:
date, hours, createdAt
Searchable:
description

/api/v1/leaves-of-absence

An LOA request. Status drives whether the member shows as on leave.

GET/api/v1/leaves-of-absenceloa:read
GET/api/v1/leaves-of-absence/{id}loa:read
POST/api/v1/leaves-of-absenceloa:write
PATCH/api/v1/leaves-of-absence/{id}loa:write
DELETE/api/v1/leaves-of-absence/{id}loa:write
Filters:
memberId, status, type, departmentAssignmentId
Sortable:
startDate, endDate, requestedAt, createdAt
Expandable:
member

/api/v1/disciplinary-records

A disciplinary action filed against a department assignment.

GET/api/v1/disciplinary-recordsdisciplinary:read
GET/api/v1/disciplinary-records/{id}disciplinary:read
POST/api/v1/disciplinary-recordsdisciplinary:write
PATCH/api/v1/disciplinary-records/{id}disciplinary:write
DELETE/api/v1/disciplinary-records/{id}disciplinary:write
Filters:
departmentAssignmentId, type, isActive, appealStatus, infractionCategoryId
Sortable:
issuedAt, expiresAt, createdAt
Searchable:
description

/api/v1/disciplinary-appeals

An appeal against a disciplinary record.

GET/api/v1/disciplinary-appealsdisciplinary:read
GET/api/v1/disciplinary-appeals/{id}disciplinary:read
POST/api/v1/disciplinary-appealsdisciplinary:write
PATCH/api/v1/disciplinary-appeals/{id}disciplinary:write
DELETE/api/v1/disciplinary-appeals/{id}disciplinary:write
Filters:
disciplinaryRecordId, appellantId, status, stage
Sortable:
submittedAt, reviewedAt, createdAt

/api/v1/probations

A probation period for a member, often triggered by a disciplinary record.

GET/api/v1/probationsdisciplinary:read
GET/api/v1/probations/{id}disciplinary:read
POST/api/v1/probationsdisciplinary:write
PATCH/api/v1/probations/{id}disciplinary:write
DELETE/api/v1/probations/{id}disciplinary:write
Filters:
memberId, status, departmentAssignmentId, disciplinaryRecordId
Sortable:
startDate, endDate, createdAt

/api/v1/commendations

An award or recognition given to a department assignment.

GET/api/v1/commendationsdisciplinary:read
GET/api/v1/commendations/{id}disciplinary:read
POST/api/v1/commendationsdisciplinary:write
PATCH/api/v1/commendations/{id}disciplinary:write
DELETE/api/v1/commendations/{id}disciplinary:write
Filters:
departmentAssignmentId, type, awardedById
Sortable:
awardedAt, createdAt
Searchable:
type, description

/api/v1/certifications

A certification definition. `departmentId` null means community-wide.

GET/api/v1/certificationscertifications:read
GET/api/v1/certifications/{id}certifications:read
POST/api/v1/certificationscertifications:write
PATCH/api/v1/certifications/{id}certifications:write
DELETE/api/v1/certifications/{id}certifications:write
Filters:
departmentId, isActive
Sortable:
name, createdAt
Searchable:
name, abbreviation

/api/v1/member-certifications

A certification awarded to a member.

GET/api/v1/member-certificationscertifications:read
GET/api/v1/member-certifications/{id}certifications:read
POST/api/v1/member-certificationscertifications:write
PATCH/api/v1/member-certifications/{id}certifications:write
DELETE/api/v1/member-certifications/{id}certifications:write
Filters:
memberId, certificationId, tier
Sortable:
awardedAt, expiresAt, createdAt
Expandable:
certification, member

/api/v1/documents

A document in the documentation CMS. `isDraft` false plus a publishedAt means it is live.

GET/api/v1/documentsdocuments:read
GET/api/v1/documents/{id}documents:read
POST/api/v1/documentsdocuments:write
PATCH/api/v1/documents/{id}documents:write
DELETE/api/v1/documents/{id}documents:write
Filters:
departmentId, folderId, type, isDraft, isActive, slug, requiresAcknowledgment
Sortable:
title, sortOrder, publishedAt, createdAt, updatedAt
Searchable:
title

/api/v1/document-folders

A folder in the documentation tree.

GET/api/v1/document-foldersdocuments:read
GET/api/v1/document-folders/{id}documents:read
POST/api/v1/document-foldersdocuments:write
PATCH/api/v1/document-folders/{id}documents:write
DELETE/api/v1/document-folders/{id}documents:write
Filters:
departmentId, parentFolderId, isActive, slug
Sortable:
name, sortOrder, createdAt
Searchable:
name

/api/v1/announcements

A community or department announcement.

GET/api/v1/announcementscommunity:read
GET/api/v1/announcements/{id}community:read
POST/api/v1/announcementscommunity:write
PATCH/api/v1/announcements/{id}community:write
DELETE/api/v1/announcements/{id}community:write
Filters:
departmentId, priority, isPinned, isActive
Sortable:
publishAt, createdAt, title
Searchable:
title, content

/api/v1/audit-logs

The community audit trail. `apiKeyId` is set when the change came through this API.

GET/api/v1/audit-logsaudit:read
GET/api/v1/audit-logs/{id}audit:read
Filters:
actorId, action, targetType, targetId, apiKeyId
Sortable:
createdAt