Skip to main content

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; a generic_api_key-kind credential's only works against /integrations/token. Crossing them fails with the same generic 401 as 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.missing in 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 matching fivem.* scope added in the web app. A 403 on any other FiveM Bridge route isn't a scope problem — look at the reason field in the response body instead (see below), that route uses the discriminated 200 pattern, not a 403.

"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 scope can be DEPARTMENT, meaning it's invisible outside the issuing department, by design.
  • Name matching is exact on firstName/lastName (narrowed further by dateOfBirth where 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.