Skip to content

REST API

Reading and Writing Your Roster From Your Own Code

PulseRoster has a REST API. Anything you can see in the dashboard, a script can read, and most of it a script can change: members, departments, ranks, assignments, roster slots, duty time, leave, discipline, certifications and documents.

Typical uses are a FiveM resource that checks whether a player is on the roster before it gives them a vehicle, a Discord bot of your own, a spreadsheet that pulls activity hours every Monday, or a one-off script that fixes 300 badge numbers.

Available on the Professional plan.


Creating a Key

  1. Go to Settings > API Keys
  2. Click New key
  3. Give it a name you will recognise later, such as "Dispatch bot (production)"
  4. Tick the scopes it needs, and nothing more
  5. Optionally set an expiry date, an IP allowlist, or a lower rate limit
  6. Click Create key

The key is shown once. We store a hash of it, not the key itself, so it cannot be retrieved later. Copy it straight into wherever it is going to live. If you lose it, revoke it and make another.

Who Can Create Keys

Managing API keys is its own permission, Create, scope, and revoke API keys, separate from the one that governs Discord. Out of the box it is held by the community owner and anyone with the Full Admin staff role. You can grant it to any staff role in Settings > Staff Roles.

A Key Is Never More Powerful Than Its Creator

You can only put a scope on a key if you hold the matching permission yourself. Someone with the Roster Manager role can create a key that edits the roster, and cannot create one that touches disciplinary records, because they cannot touch those in the app either.

This is checked when the key is created, not just hidden in the form, so there is no way around it.

Treat a key like a password

Anyone holding the key can do everything its scopes allow, without logging in. Do not paste one into a public GitHub repository, a Discord channel, or a client-side script that players can read. If a key leaks, revoke it in Settings > API Keys and the next request using it fails immediately.


Authenticating

Send the key as a bearer token:

curl https://roster.mypulsetech.com/api/v1/whoami \
  -H "Authorization: Bearer prk_a1b2c3d4_your_secret_here"

If your HTTP client makes an Authorization header awkward, which some FiveM Lua libraries do, send it as X-API-Key instead. Both work.

GET /api/v1/whoami is the call to start with. It confirms the key is live and shows which community and scopes it carries.


Scopes

A key carries an explicit list of scopes. A write scope includes read, so roster:write can also read the roster. A read scope never implies write.

Scope group What it covers
roster Members, characters, assignments, roster slots, member notes
departments Departments, subdivisions, ranks
timelogs Duty sessions, time logs, activity
loa Leave of absence requests
certifications Certification definitions and member certifications
disciplinary Records, appeals, probations, commendations
documents Documents and folders
applications Application forms and submissions
community Community settings, announcements
audit Your audit log, read only

Each has a :read and a :write form, except audit, which is read only. Calling something your key lacks the scope for returns 403 insufficient_scope naming the scope you need.


Common Requests

List active members:

curl "https://roster.mypulsetech.com/api/v1/members?status=ACTIVE&limit=50" \
  -H "Authorization: Bearer $PULSEROSTER_KEY"

Find a member by their FiveM licence:

curl "https://roster.mypulsetech.com/api/v1/members?fivemLicense=license:abc123" \
  -H "Authorization: Bearer $PULSEROSTER_KEY"

Promote someone by changing the rank on their department assignment:

curl -X PATCH "https://roster.mypulsetech.com/api/v1/department-assignments/$ASSIGNMENT_ID" \
  -H "Authorization: Bearer $PULSEROSTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rankId": "the-new-rank-id"}'

Pull last month's duty sessions:

curl "https://roster.mypulsetech.com/api/v1/duty-sessions?startedAt.after=2026-08-01T00:00:00Z&startedAt.before=2026-09-01T00:00:00Z" \
  -H "Authorization: Bearer $PULSEROSTER_KEY"

How Every List Endpoint Works

Once you have called one, you can call all of them. The same parameters work everywhere.

Parameter Example Meaning
page, limit ?page=2&limit=50 Pagination. Limit caps at 100
sort ?sort=-joinDate Sort. A leading - means descending
q ?q=smith Search the resource's text fields
Any filter field ?status=ACTIVE Filter. Repeat it to mean "either"
field.after, field.before ?createdAt.after=2026-01-01T00:00:00Z Date range on any date field
include ?include=member,rank Expand related records inline

The reference page lists which fields are filterable, sortable and searchable for each resource.


Responses

Every response has the same shape, whether it worked or not, so your code can check success first.

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

A failure looks like this:

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

That request_id also comes back as the X-Request-Id header and is written to our logs. If something goes wrong, quote it to support and we can find the exact request.

Error Codes

Code Meaning
invalid_api_key Missing, wrong, revoked or expired key
insufficient_scope Valid key, but it lacks the scope this call needs
ip_not_allowed The key has an IP allowlist and you are not on it
plan_required The community is not on an active Professional plan
not_found No such endpoint, or no such record in your community
method_not_allowed That action is not available for this resource
conflict Something already exists, or the record is still in use
validation_error A field failed validation. error.details names which
rate_limited Too many requests. Check the Retry-After header
internal Our fault. Quote the request id

Rate Limits

120 requests per minute and 25,000 per day, per key. You can set a lower per-minute limit on an individual key when you want a new script kept on a short leash.

Every response tells you where you stand, not just the ones that get refused:

Header Meaning
X-RateLimit-Limit The ceiling for the current window
X-RateLimit-Remaining How many requests you have left
X-RateLimit-Reset When the window resets, as a unix timestamp
Retry-After Seconds to wait. Only sent with a 429

Watch X-RateLimit-Remaining and slow down before you are throttled rather than after.


What the API Records

Anything a key changes is written to your audit log, marked as having come through the API and attributed to the person whose key it is. You can see it in Settings > Audit Log, or read it through the API at /api/v1/audit-logs.

If an integration starts doing something you did not expect, the audit log is where you will see it.


Things to Know

Members cannot be created through the API. A member needs a user account behind it, and creating accounts is not something a key can do. Add people through an invite, an application, or Discord import, then manage them through the API afterwards.

Changing a rank through the API does not rename anyone in Discord. Discord nickname sync runs when you promote someone in the dashboard. A rank set through the API is correct on your roster, but the member's Discord nickname will not follow until the next dashboard change or a manual sync from Settings > Discord.

Some fields are read only. Anything the system works out for itself, such as disciplinary points, whether a record triggered a probation, or document visibility rules, can be read but not written. Those are computed by the app, and letting a key set them directly would produce records that claim something that never happened.

Member notes marked private are not exposed. A key has no identity of its own, so it cannot be "the author" of a private note.