Skip to content
Help Center
Products
Back to KC Safety Integration

KC Safety Public REST API Reference (v1)

Summary

The KC Safety Public REST API is a read-only HTTP API that lets an external system pull incident and reference data from your KC Safety account. This article is the reference: base URL, authentication, response envelope, rate limiting, errors, and every endpoint available today. For a step-by-step walkthrough of creating a key and making your first call, see How to Create API Keys and Read Data with the KC Safety Public REST API.

Getting started

About

The KC Safety Public REST API lets an external system — your own middleware, a data warehouse, a reporting tool, or another KnowledgeCity product — pull incident and reference data from your KC Safety account.

Requests are authenticated with an API key, scoped automatically to your account, and rate-limited according to your plan.

Read-only. The API exposes GET endpoints only. There is no create, update, or delete. To send data into KC Safety, use inbound email reporting (for incidents) or SCIM provisioning (for user accounts). To receive KC Safety events as they happen, use outbound event webhooks.

Before calling the API, create an API key: Admin Panel → Settings → Integrations → API Keys. This requires the Super Admin role and a plan that includes API access.

The reference below is organised into resource groups, each listing the endpoints it exposes.

GroupEndpoints
Departments1
Incident types1
Incidents2
Locations1
Users1

Base URL and versioning

EnvironmentBase URL
Productionhttps://safety-api.knowledgecity.com/api/v1

All public endpoints live under /api/v1/public/.

Versioning

  • Major version is set in the URL path, e.g. /api/v1/public/....
  • There is no minor-version request header. The API does not accept an x-api-version header, and there is nothing to pin — unlike the KC Integration API, v1 is the whole version contract.

Use the API host (safety-api.…), not your KC Safety web address — the application origin does not proxy /api/* through to the backend. If you need a non-production base URL for testing, ask your account team.

Authentication

Every request must carry an API key as a bearer token:

Authorization: Bearer kcs_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • Keys are prefixed kcs_ followed by 40 hexadecimal characters. The header must begin exactly with Bearer kcs_ — there is no alternative header such as X-API-Key.
  • The key both authenticates the caller and resolves the account. Every response is automatically scoped to the key's account, so there is no account or tenant parameter to pass, and a key can never reach another account's data.
  • The raw key is displayed once, at creation. Only a hash is stored, so the value cannot be retrieved again. If a key is lost, revoke it and create a replacement.

Send requests over HTTPS only. Never embed a key in client-side code, a browser request, or a public repository.

Example request

curl -H "Authorization: Bearer kcs_xxxx" \
  "https://safety-api.knowledgecity.com/api/v1/public/incidents?page=1&limit=50"
{
  "error": false,
  "data": {
    "data": [
      {
        "id": "…",
        "incident_number": "INC-2026-0001",
        "description": "…",
        "status": "open",
        "severity_score": 12,
        "severity_likelihood": 3,
        "severity_impact": 4,
        "location": "…",
        "incident_date": "2026-08-01",
        "created_at": "2026-08-01T10:00:00.000Z",
        "updated_at": "2026-08-02T09:00:00.000Z"
      }
    ],
    "pagination": { "page": 1, "limit": 50, "total": 137, "pages": 3 }
  }
}

Pagination

Only Return incidents is paginated. It accepts page and limit query parameters and returns a pagination object alongside the rows.

ParameterTypeRequiredDescription
pageintegeroptional1-based page number. Defaults to 1.
limitintegeroptionalPage size. Defaults to 50. Maximum 200 — a higher value is reduced to 200 rather than rejected.

The pagination object reports page, limit, total (rows matching the request) and pages (total pages at the current limit).

Every other endpoint returns a complete, unpaginated array and accepts no query parameters.

Response envelope

Every successful response shares a top-level wrapper:

{ "error": false, "data": <payload> }

The shape of data varies by endpoint — it is not uniform, so handle each endpoint's shape explicitly:

Endpointdata contains
Return incidentsAn object: { "data": [ … ], "pagination": { … } } — note the nested data
Return incident by idA single incident object
Return incident types, users, locations, departmentsA bare array: [ … ], with no pagination

The Responses table on each endpoint below describes the full body, envelope included.

Rate limiting

Limits are enforced over a fixed one-hour window per account — the window opens with your first request and resets one hour later; it does not slide continuously. The allowance depends on your plan. Read the response headers rather than hard-coding a number or a reset time — your allowance can change with your plan, and X-RateLimit-Reset always carries the exact reset moment.

Present on every response:

HeaderMeaning
X-RateLimit-LimitYour hourly allowance.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetWhen the window resets, as an absolute Unix epoch time in seconds — not a countdown.

When the limit is exceeded, the API returns 429 with a Retry-After header (in seconds) and this body:

{
  "error": "RATE_LIMIT_EXCEEDED",
  "message": "API rate limit exceeded for this account (<limit> requests/hour). Retry after <n>s."
}

On a 429 response, X-RateLimit-Reset is not set — use Retry-After. On successful responses, Retry-After is not set.

Design your integration to read incrementally and cache what it can, rather than re-reading the full data set on a tight loop. If you need to react to changes promptly, use outbound event webhooks instead of polling — they deliver within seconds and consume no API quota.

Errors

Authentication and rate-limit errors return a string error code in error. Errors raised by an individual endpoint return error: true with a message. Both forms occur, so branch on the HTTP status rather than the body shape.

Statuserror valueWhen
401API_KEY_REQUIREDThe Authorization: Bearer kcs_… header is missing or malformed.
401API_KEY_INVALIDThe key is unknown, revoked, or expired.
403INSUFFICIENT_SCOPEThe key's scope does not permit the HTTP method used.
403FEATURE_NOT_AVAILABLEThe account's plan does not include API access.
404trueThe resource was not found, e.g. { "error": true, "message": "Incident not found" }.
429RATE_LIMIT_EXCEEDEDThe hourly limit was reached.
500AUTH_ERROR or trueUnexpected server error.

If your account is moved to a plan without API access, existing keys stop working and requests return 403 FEATURE_NOT_AVAILABLE, even though the keys still exist.

Scopes

Each key carries a scope: read, read-write, or admin. The scope governs which HTTP methods the key may use — read permits GET; read-write and admin additionally permit write verbs.

Because the Public API is read-only today, scope has no practical effect on the current surface: a read key can reach every endpoint. Scopes exist for forward compatibility with future write endpoints. Choose read unless you have a specific reason not to.

Managing API keys

Keys are managed by a tenant administrator at Admin Panel → Settings → Integrations → API Keys, which requires the Super Admin role and a plan that includes API access.

  • Create — name the key and optionally set a scope (default read) and an expiry date. The raw kcs_ value is shown once; copy it immediately.
  • List — shows each key's name, prefix, scope, active state, last use, and expiry. The raw value is never shown again.
  • Rename, enable or disable, change scope — all supported.
  • Revoke — immediate and permanent. Requests using a revoked key return 401 API_KEY_INVALID.

Issue a separate key per integration, so revoking one does not interrupt the others. Revoke any key that is unused, has been exposed, or belonged to a decommissioned system.

A quick reference is also available in the app at Admin Panel → Docs → API Keys.

API reference

Departments

GET /api/v1/public/departments

Return departments

Returns the active departments configured for your account, ordered by name.

Responses

200 — application/json body

FieldTypeRequiredDescription
errorbooleanrequiredAlways false on a successful response.
dataarrayrequiredArray of department objects.
↳ idstring (uuid)requiredDepartment identifier.
↳ namestringrequiredDepartment name.
↳ created_atstring (date-time)requiredWhen the department was created.
StatusDescription
401API_KEY_REQUIRED — the Authorization header is missing or malformed. API_KEY_INVALID — the key is unknown, revoked, or expired.
403INSUFFICIENT_SCOPE or FEATURE_NOT_AVAILABLE.
429RATE_LIMIT_EXCEEDED — hourly limit reached. See Retry-After.
500Server error.

Incident types

GET /api/v1/public/incident-types

Return incident types

Returns the active incident types configured for your account, ordered by name.

Responses

200 — application/json body

FieldTypeRequiredDescription
errorbooleanrequiredAlways false on a successful response.
dataarrayrequiredArray of incident-type objects.
↳ idstring (uuid)requiredIncident-type identifier.
↳ namestringrequiredIncident-type name.
↳ descriptionstringoptionalIncident-type description.
↳ created_atstring (date-time)requiredWhen the incident type was created.
StatusDescription
401API_KEY_REQUIRED — the Authorization header is missing or malformed. API_KEY_INVALID — the key is unknown, revoked, or expired.
403INSUFFICIENT_SCOPE or FEATURE_NOT_AVAILABLE.
429RATE_LIMIT_EXCEEDED — hourly limit reached. See Retry-After.
500Server error.

Incidents

GET /api/v1/public/incidents

Return incidents

Returns incidents for your account, newest first (ordered by creation date, descending). This is the only paginated endpoint. There are no filter or sort parameters.

Query parameters

NameTypeRequiredDescription
pageintegeroptional1-based page number. Defaults to 1.
limitintegeroptionalPage size. Defaults to 50. Maximum 200 — a higher value is reduced to 200.

Responses

200 — application/json body

FieldTypeRequiredDescription
errorbooleanrequiredAlways false on a successful response.
dataobjectrequiredPayload wrapper.
↳ dataarrayrequiredArray of incident objects.
↳↳ idstring (uuid)requiredIncident identifier.
↳↳ incident_numberstringrequiredHuman-readable reference, e.g. INC-2026-0001.
↳↳ descriptionstringoptionalIncident description.
↳↳ statusstringrequiredWorkflow status, e.g. open.
↳↳ severity_scoreintegeroptionalCalculated severity, severity_likelihood × severity_impact.
↳↳ severity_likelihoodintegeroptionalLikelihood rating.
↳↳ severity_impactintegeroptionalImpact rating.
↳↳ locationstringoptionalLocation name.
↳↳ incident_datestring (date)optionalDate the incident occurred.
↳↳ created_atstring (date-time)requiredWhen the incident was created.
↳↳ updated_atstring (date-time)requiredWhen the incident was last updated.
↳ paginationobjectrequiredPage counters.
↳↳ pageintegerrequiredCurrent page number.
↳↳ limitintegerrequiredPage size in effect.
↳↳ totalintegerrequiredTotal incidents in the account.
↳↳ pagesintegerrequiredTotal pages at the current limit.
StatusDescription
401API_KEY_REQUIRED — the Authorization header is missing or malformed. API_KEY_INVALID — the key is unknown, revoked, or expired.
403INSUFFICIENT_SCOPE or FEATURE_NOT_AVAILABLE.
429RATE_LIMIT_EXCEEDED — hourly limit reached. See Retry-After.
500Server error — { "error": true, "message": "Failed to fetch incidents" }.

GET /api/v1/public/incidents/{id}

Return incident by id

Returns a single incident by its identifier, scoped to your account. Returns the same fields as Return incidents, as a single object with no pagination wrapper.

Path parameters

NameTypeRequiredDescription
idstring (uuid)requiredIncident identifier.

Responses

200 — application/json body

FieldTypeRequiredDescription
errorbooleanrequiredAlways false on a successful response.
dataobjectrequiredThe incident object.
↳ idstring (uuid)requiredIncident identifier.
↳ incident_numberstringrequiredHuman-readable reference, e.g. INC-2026-0001.
↳ descriptionstringoptionalIncident description.
↳ statusstringrequiredWorkflow status, e.g. open.
↳ severity_scoreintegeroptionalCalculated severity, severity_likelihood × severity_impact.
↳ severity_likelihoodintegeroptionalLikelihood rating.
↳ severity_impactintegeroptionalImpact rating.
↳ locationstringoptionalLocation name.
↳ incident_datestring (date)optionalDate the incident occurred.
↳ created_atstring (date-time)requiredWhen the incident was created.
↳ updated_atstring (date-time)requiredWhen the incident was last updated.
StatusDescription
401API_KEY_REQUIRED — the Authorization header is missing or malformed. API_KEY_INVALID — the key is unknown, revoked, or expired.
403INSUFFICIENT_SCOPE or FEATURE_NOT_AVAILABLE.
404Not found — { "error": true, "message": "Incident not found" }. Also returned when the incident belongs to another account.
429RATE_LIMIT_EXCEEDED — hourly limit reached. See Retry-After.
500Server error — { "error": true, "message": "Failed to fetch incident" }.

Locations

GET /api/v1/public/locations

Return locations

Returns the active locations configured for your account, ordered by name.

Responses

200 — application/json body

FieldTypeRequiredDescription
errorbooleanrequiredAlways false on a successful response.
dataarrayrequiredArray of location objects.
↳ idstring (uuid)requiredLocation identifier.
↳ namestringrequiredLocation name.
↳ created_atstring (date-time)requiredWhen the location was created.
StatusDescription
401API_KEY_REQUIRED — the Authorization header is missing or malformed. API_KEY_INVALID — the key is unknown, revoked, or expired.
403INSUFFICIENT_SCOPE or FEATURE_NOT_AVAILABLE.
429RATE_LIMIT_EXCEEDED — hourly limit reached. See Retry-After.
500Server error.

Users

GET /api/v1/public/users

Return users

Returns active users in your account, ordered by name. KnowledgeCity support accounts are always excluded from this list.

Responses

200 — application/json body

FieldTypeRequiredDescription
errorbooleanrequiredAlways false on a successful response.
dataarrayrequiredArray of user objects.
↳ idstring (uuid)requiredUser identifier.
↳ namestringrequiredFull name.
↳ emailstring (email)requiredEmail address.
↳ rolestringrequiredRole in KC Safety.
↳ departmentstringoptionalDepartment name.
↳ job_titlestringoptionalJob title.
↳ created_atstring (date-time)requiredWhen the user was created.
StatusDescription
401API_KEY_REQUIRED — the Authorization header is missing or malformed. API_KEY_INVALID — the key is unknown, revoked, or expired.
403INSUFFICIENT_SCOPE or FEATURE_NOT_AVAILABLE.
429RATE_LIMIT_EXCEEDED — hourly limit reached. See Retry-After.
500Server error.

Not yet available

  • Write endpoints — none. No create, update, or delete through the API.
  • Filtering and sorting — the incidents list accepts only page and limit. There are no status, type, severity, or date-range filters, and no sort parameter.
  • Other resources — only the six endpoints above are exposed. Investigations, action items, and documents are not on the Public API.

Ask Casey about this article