Integration Troubleshooting
Debugging help for anyone building or running an integration against the API — a custom FiveM bridge, a bot, or the official resources. If you're troubleshooting Kestrel as an end user (a player, dispatcher, or officer) rather than as an integrator, see FAQ & Troubleshooting instead.
"Every request gets a 401"
- Token expired. Access tokens last 12 minutes with no refresh —
re-exchange via
/token(or/integrations/fivem/token) rather than reusing an old one. If your integration runs long-lived, renew on a timer well before the 12-minute mark, don't wait for a 401 to react. - Wrong path for your credential kind. A
fivem-kind credential's secret only works against/integrations/fivem/token; ageneric_api_key-kind credential's only works against/integrations/token. Crossing them fails with the same generic401as a wrong secret — see Getting a Token. - Credential revoked. Check Admin → Integrations — a revoked credential's already-issued tokens keep working until they naturally expire, but a fresh exchange fails immediately.
Every failure here collapses to the same INTEGRATION_INVALID_CLIENT_CREDENTIALS
or INTEGRATION_TOKEN_INVALID — see
Errors & Status Codes — so you
can't distinguish "wrong secret" from "revoked" from the response alone.
Regenerate the credential in the web app if you're not sure which it is.
"I get 403 PERMISSION_DENIED"
- API credential: check
details.missingin the response body — it names the exact scope you're short. Go to Admin → Integrations, open the credential, and add it. See the scope catalog. - FiveM credential: only three routes enforce a scope at all
(
/citation,/arrest,/bolo— see Rate Limits & Security). If you're hitting a 403 on one of those three, the credential needs the matchingfivem.*scope added in the web app. A 403 on any other FiveM Bridge route isn't a scope problem — look at thereasonfield in the response body instead (see below), that route uses the discriminated200pattern, not a403.
"I get not_linked or not_on_duty and don't know which"
This is deliberate, not a bug in your integration — see
Getting a Token.
Every step in the on-duty gate (linked, right department, on duty)
collapses to the same generic reason so the API can't be used to enumerate
who's linked. To actually tell them apart, you need out-of-band
information your integration already has: ask the player to confirm
they've run /kestrellink, and separately confirm they're on duty in the
web app or via your own /duty sync call's own response.
"/bolo-check or a lookup returns no_match for something I know exists"
- Confirm you're querying the right department's data — a BOLO's
scopecan beDEPARTMENT, meaning it's invisible outside the issuing department, by design. - Name matching is exact on
firstName/lastName(narrowed further bydateOfBirthwhere accepted) — a typo, nickname, or different casing won't match. This isn't a fuzzy search. - For a vehicle, confirm the plate string matches exactly, including any spacing your server's plate generator uses.
"The ANPR/camera-network hit report keeps 403ing"
Almost always a missing records.vehicle.view scope on the API
credential doing the reporting — POST /records/bolos/:boloId/hits
independently verifies the reported plate against the BOLO's linked
vehicle, which requires that scope even though the route itself is gated
on records.bolo.manage. Grant both.
"My WebSocket connection drops after a few minutes"
The socket is authenticated with the same bearer token as everything else, and shares its 12-minute lifetime. Reconnect with a freshly exchanged token rather than treating the drop as an error — see the WebSocket section.
"I'm getting rate-limited and don't know why"
Limits are per-route, per-credential — see the table in
Rate Limits & Security. The
most common trip-up is polling /waypoint or sending /location faster
than the documented budget in a tight loop, or retrying a failed call
immediately instead of backing off. A 429 body carries no detail on how
long to wait (see Errors & Status Codes)
— use a fixed or exponential backoff rather than guessing.
Still stuck
Check Errors & Status Codes for
what the response shape itself is telling you, and
Setting Up Your Community if the problem is
really about credential setup or department mapping rather than the API
itself. If none of that explains it, that's worth reporting rather than
retrying blindly — include the exact error code and message from the
response body.