# 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](https://docs.kapiworks.com/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](https://docs.kapiworks.com/guides/sandbox) 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.

**curl**

```bash
# 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'
```

**Node**

```js
// Node 24, as an ES module (first-call.mjs). KAPI_WORKS_API_KEY comes from your secret manager.
const response = await fetch("https://api.kapiworks.com/v1/training-records?limit=200", {
  headers: { Authorization: `Bearer ${process.env.KAPI_WORKS_API_KEY}`, Accept: "application/json" },
  redirect: "error",
  signal: AbortSignal.timeout(30_000),
});
const requestId = response.headers.get("x-request-id");
const body = await response.json();
if (!response.ok) throw new Error(`HTTP ${response.status} ${body.code}; request ID ${requestId}`);
console.log(JSON.stringify(body, null, 2));
```

**Python**

```python
# Python 3, standard library only. KAPI_WORKS_API_KEY comes from your secret manager.
import json, os, urllib.error, urllib.request

class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, *args):  # do not resend the key to another address
        return None

request = urllib.request.Request(
    "https://api.kapiworks.com/v1/training-records?limit=200",
    headers={"Authorization": "Bearer " + os.environ["KAPI_WORKS_API_KEY"], "Accept": "application/json"},
)
try:
    with urllib.request.build_opener(NoRedirect).open(request, timeout=30) as response:
        print(json.dumps(json.load(response), indent=2))
except urllib.error.HTTPError as error:
    problem = json.load(error)
    raise SystemExit(f"HTTP {error.code} {problem['code']}; request ID {error.headers['X-Request-Id']}")
```

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

```json
{
  "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](https://docs.kapiworks.com/guides/sync) covers paging, snapshots and retries.

## Next steps

* [Authentication](https://docs.kapiworks.com/authentication): how the API checks a key, and what it refuses.
* [The five registers](https://docs.kapiworks.com/guides/registers): what each endpoint returns, at a moment or over a period.
* [Errors](https://docs.kapiworks.com/errors): every error code, with what to do.
