Cadence developers

Connect your on-duty system to Cadence.

A small REST API: pull approved off-duty time out of Cadence, and push on-duty shifts and approved leave in so scheduling knows who is already committed.

Overview

Every request is scoped to one agency — the agency that owns the API key you send. All timestamps are ISO 8601 in UTC. All responses are JSON.

Export off-duty time

Approved (or any status) time entries with officer, shift, and job detail, ready for payroll or your records system.

Ingest on-duty time

Send regular-duty shifts and approved leave so Cadence can see conflicts before an officer takes an off-duty job.

EndpointMethodPurpose
/api/public/v1/export/off-duty-timeGETRead off-duty time entries for a date range.
/api/public/v1/ingest/on-dutyPOSTCreate or update on-duty shift and leave records.

Base URL: https://cadencepublicsafety.com

Get a token

  1. Sign in as an agency admin.
  2. Go to Settings → Integrations.
  3. Give the key a name that says where it will be used, then create it.
  4. Copy the key immediately — it starts with cdk_ and is shown only once. Cadence stores a hash, not the key itself.
  5. Revoke a key at any time from the same page; revoked keys stop working instantly.

Treat a key like a password. Use a separate key per integration so you can revoke one without breaking the others.

Authentication

Send the key as a bearer token on every request.

Authorization: Bearer cdk_your_key_here

A missing key returns 401 with { "error": "Missing API key" }. An unknown or revoked key returns 401 with { "error": "Invalid or revoked API key" }.

Export off-duty time

GET /api/public/v1/export/off-duty-time

Query parameters

ParameterTypeDescription
fromrequired, YYYY-MM-DDStart of the range, matched against check-in time.
torequired, YYYY-MM-DDEnd of the range, matched against check-in time.
statusoptionalOne of approved, submitted, open, rejected, or all. Defaults to approved.

Example request

curl -H "Authorization: Bearer cdk_your_key_here" \
  "https://cadencepublicsafety.com/api/public/v1/export/off-duty-time?from=2026-01-01&to=2026-01-31&status=approved"

Example response

{
  "agency_id": "0b1e...",
  "from": "2026-01-01",
  "to": "2026-01-31",
  "status": "approved",
  "count": 1,
  "records": [
    {
      "id": "e2b0...",
      "officer": { "id": "9a1c...", "name": "J. Rivera", "email": "j.rivera@agency.gov" },
      "shift": {
        "id": "5f3d...",
        "starts_at": "2026-01-14T22:00:00.000Z",
        "ends_at": "2026-01-15T04:00:00.000Z"
      },
      "job": {
        "id": "77c9...",
        "title": "Stadium gate security",
        "event_name": "Winter Classic",
        "location_address": "500 Main St",
        "business_name": "Riverside Arena"
      },
      "check_in_at": "2026-01-14T21:56:00.000Z",
      "check_out_at": "2026-01-15T04:03:00.000Z",
      "hours": 6.1,
      "notes": null,
      "status": "approved",
      "reviewed_at": "2026-01-16T15:02:00.000Z"
    }
  ]
}

Record fields

FieldTypeDescription
iduuidTime entry identifier — stable, use it to dedupe.
officerobjectOfficer id, name, and email.
shiftobjectScheduled shift id and start/end times.
jobobjectJob id, title, event name, address, and requesting business name.
check_in_attimestampWhen the officer checked in.
check_out_attimestamp | nullWhen the officer checked out.
hoursnumber | nullRecorded hours for the entry.
notesstring | nullOfficer notes on the entry.
statusstringopen, submitted, approved, or rejected.
reviewed_attimestamp | nullWhen an admin reviewed it.

Records are returned newest check-in first.

Ingest on-duty time

POST /api/public/v1/ingest/on-duty

Send a records array — up to 500 per request. Each record is matched to an officer by email and must belong to an active member of your agency. Sending the same external_id again updates the existing record instead of creating a duplicate, so you can safely re-send.

Record fields

FieldTypeDescription
officer_emailrequired, stringEmail of an active officer in your agency. Case-insensitive.
external_idrequired, stringYour system's id for this shift or leave. Used to update instead of duplicate.
typerequiredEither shift or leave.
starts_atrequired, ISO timestampStart of the block.
ends_atrequired, ISO timestampEnd of the block. Must be after starts_at.
sourceoptional, stringLabel for where the record came from. Defaults to api.

Example request

curl -X POST \
  -H "Authorization: Bearer cdk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "records": [
      {
        "officer_email": "j.rivera@agency.gov",
        "external_id": "rms-shift-88213",
        "type": "shift",
        "starts_at": "2026-01-14T06:00:00Z",
        "ends_at": "2026-01-14T16:00:00Z",
        "source": "rms"
      }
    ]
  }' \
  "https://cadencepublicsafety.com/api/public/v1/ingest/on-duty"

Example response

{
  "agency_id": "0b1e...",
  "received": 1,
  "accepted": 1,
  "results": [
    { "external_id": "rms-shift-88213", "status": "ok" }
  ]
}

The request returns 200 even when some records fail. Check each entry in results: a failed one has "status": "error" and a message, for example an officer email that is not an active member.

Errors

StatusMeaningCommon cause
400Bad requestMissing from/to, bad date, unknown status value, invalid JSON, no records array, or more than 500 records.
401UnauthorizedNo Authorization header, or the key is unknown or revoked.
500Server errorCadence could not read or write the records. Retry, then contact support.

Error bodies are always { "error": "message" }.

Limits and behavior

  • Requests only ever see and write data for the agency that owns the key.
  • Maximum 500 records per ingest request — split larger syncs into batches.
  • Timestamps must be ISO 8601; send UTC to avoid ambiguity.
  • Export filters on check-in time, not on shift start time.
  • Each key records a last-used time so you can spot stale integrations.
  • Ingest is idempotent per external id — nightly full re-syncs are safe.

Need something the API doesn't cover?

Tell us what your records or CAD system needs and we'll look at adding it.

Contact us