kapi WorksDocs

Quickstart

Create a key, keep it on your server, and read your first page of training records.

You need a Kapi Works company and an owner or admin of that company. They create the key in Works Studio; your server makes the calls.

1. Create a key

In Works Studio, a company owner or admin opens Settings → API keys and selects Create key. Creating a key needs two-step sign-in on their account and a sign-in within the last 15 minutes.

They give the key:

  • a name they will recognise later, such as the name of the receiving system;
  • the receiving company, its country and the purpose of the integration;
  • the scopes the integration needs, and no more;
  • an expiry, at most 365 days away;
  • optionally, the networks it may be used from. Leave this empty to allow any network.

Create a separate key for each receiving integration and each company. A key covers one company, across all of its learning spaces.

2. Store the key on your server

Works Studio shows the key once. Copy it into your server's secret manager and give it to your code as KAPI_WORKS_API_KEY. Keep it out of source code, browser code, command arguments and logs.

Production keys start with kw_live_; keys from Kapi's other environments start with kw_test_. The prefix names the environment, and sandbox keys on the live system also start with kw_live_. Each key works only on the deployment that issued it.

3. Make the first call

Read the first page of training records. The key goes in the Authorization header and nowhere else: a key sent in a web address is revoked at once.

# Supply the key from your server's secret manager. Do not log it.
# curl reads the header from stdin, keeping the key out of its arguments.
printf 'header = "Authorization: Bearer %s"\n' "$KAPI_WORKS_API_KEY" |
  curl --config - --silent --show-error --fail-with-body \
  'https://api.kapiworks.com/v1/training-records?limit=200'

The answer is one page of records, the snapshot they come from, and a cursor for the next page:

200 response
{
  "data": [
    {
      "deployment_id": "00000000-0000-4000-8000-000000000001",
      "organization_id": "4f6b2a52-1c1e-4f7a-9d3e-2b7c8a9e0d11",
      "learning_space_id": "8b2d6c3e-5f41-4a8e-9c27-1d0e3f4a5b62",
      "record_id": "a7e3c1b9-2d48-4f6a-8e15-9b0c7d6e5f43",
      "learner_profile_id": "c3a1e7f2-8d64-4b1f-a9e2-6f5d4c3b2a10",
      "person_name": "Amira Okafor",
      "work_email": "amira.okafor@example.com",
      "course_id": "d2f8b4a6-7c1e-4e93-b5a0-3c9e8f7d6a21",
      "course_release_id": "e5c2a9d7-4b3f-4c18-9a6e-2f1d0c9b8a77",
      "renews_certificate_id": null,
      "sequence": 1271,
      "valid_from": "2026-09-12T13:41:09Z",
      "recorded_at": "2026-09-12T13:41:12Z",
      "cause": "change",
      "logic_version": 4,
      "requirement": "required",
      "assigned_at": "2026-09-01T08:00:00Z",
      "due_at": "2026-09-30T22:59:59Z",
      "due_time_zone": "Europe/London",
      "due_basis": "share_deadline",
      "completed_at": "2026-09-12T13:41:09Z",
      "completion_receipt_id": "9c8b7a6f-5e4d-4c3b-8a29-1f0e9d8c7b66",
      "ended_at": null,
      "ended_reason": null,
      "waived_at": null,
      "waiver_actor_id": null,
      "waiver_reason_code": null,
      "outcome": "completed_on_time",
      "days_late": null,
      "reminders_sent": 1,
      "share_ids": [
        "b6d4f2a8-9e1c-4a35-8f72-0e9d8c7b6a54"
      ],
      "doors": [
        "employee"
      ],
      "certificate_id": "f1b9d3e5-6a2c-4d70-8b4e-5c3a2f1e0d98",
      "obligation_change_id": null
    }
  ],
  "total": 1,
  "snapshot": {
    "organization_id": "4f6b2a52-1c1e-4f7a-9d3e-2b7c8a9e0d11",
    "deployment_id": "00000000-0000-4000-8000-000000000001",
    "as_at": "2026-10-09T09:00:00Z",
    "from": null,
    "known_at": "2026-10-09T08:59:41Z",
    "sequence": 1284,
    "capture_sequence": 352,
    "chain_hash": "3f9a1c7e5b2d8f4a6c0e9b1d3f5a7c9e2b4d6f8a0c1e3b5d7f9a2c4e6b8d0f1a",
    "logic": {
      "version": 4,
      "date": "2026-10-06"
    },
    "history_complete_from": "2026-10-06T05:00:00Z",
    "available": true
  },
  "next_cursor": null
}

The rows above are invented. A real response holds the company's records, which are personal data, so store and log them with care.

4. Read every page

While next_cursor is not null, call the same endpoint again with that cursor. Only a null next_cursor means the pull is complete, because a page can hold fewer rows than you asked for. Keeping data in sync covers paging, snapshots and retries.

Next steps

On this page