> ## Documentation Index
> Fetch the complete documentation index at: https://support.configview.com/llms.txt
> Use this file to discover all available pages before exploring further.

# KnowBe4 setup

## Part 1: KnowBe4 Data Ingestion

Set up a KnowBe4 app so ConfigView can pull your security awareness training and
phishing simulation data into the dashboard.

> **The Reporting API is a paid add-on.** KnowBe4 makes it available only to
> **Platinum**, **Diamond**, **SAT Foundations**, and **SAT Advanced**
> subscriptions. On any other plan every call returns `403 Forbidden` and no
> configuration will help — check your subscription level first.

***

### Step 1: Create a Reporting API Key

1. Sign in to your KnowBe4 console and open the **Reporting API Management
   Console** directly:
   **[https://training.knowbe4.com/app/api\_tokens/reporting](https://training.knowbe4.com/app/api_tokens/reporting)**
   (in the console this is your account menu → **Account Settings → API →
   Reporting API**).
2. Turn on **Reporting API Access** if it isn't already enabled.
3. Click **Create New API Token**, give it a name you'll recognise, e.g. `ConfigView`.
4. **Copy the token now** — KnowBe4 shows the secret only once. If you lose it,
   delete the token and create a new one.

> **Reporting API keys and User Event API keys are different keys.** They are
> issued from separate pages and are not interchangeable. A User Event API key
> pasted into `KNOWBE4_API_TOKEN` returns `401 Unauthorized` on every call. The
> key you want comes from the **Reporting** page linked above.

#### Find your region

KnowBe4 runs five regional servers and **an API key only works against the
server its account lives on** — a US key returns `401` against the EU host. Match
the console you sign in to:

| You sign in at         | Region value     | API base URL                 |
| ---------------------- | ---------------- | ---------------------------- |
| `training.knowbe4.com` | `us` *(default)* | `https://us.api.knowbe4.com` |
| `eu.knowbe4.com`       | `eu`             | `https://eu.api.knowbe4.com` |
| `ca.knowbe4.com`       | `ca`             | `https://ca.api.knowbe4.com` |
| `uk.knowbe4.com`       | `uk`             | `https://uk.api.knowbe4.com` |
| `de.knowbe4.com`       | `de`             | `https://de.api.knowbe4.com` |

***

### Step 2: Add the Token to ConfigView

1. Go to your ConfigView dashboard: `https://{companyname}.configview.com/admin/secret/`
2. Click **Add Secret**
3. Create the following secret:
   * `KNOWBE4_API_TOKEN`: The Reporting API token you just created
4. If your account is **not** on the US server, also create:
   * `KNOWBE4_REGION`: one of `eu`, `ca`, `uk`, `de`
5. Click **Save**

***

### Step 3: Enable the KnowBe4 App in ConfigView

1. Go to: `https://{companyname}.configview.com/admin/cron/`
2. You should see **KnowBe4** in the list of available apps
3. Select the scripts you want to run.
4. Click **Save**

***

### Step 4: Verify

1. Go to: `https://{companyname}.configview.com/admin/cron/`
2. Click the **Health check** button on the KnowBe4 app header.
3. All checks should pass.

If a check fails, verify that your secret is saved correctly, that the key came
from the **Reporting** API page, and that `KNOWBE4_REGION` matches the console
you sign in to.

***

### Available Scripts

| Script                     | Table                              | Description                                                                                                                 |
| -------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Account                    | `knowbe4_account`                  | Subscription level, seat count, allowed domains, admins, and the current org risk score                                     |
| Account Risk Score History | `knowbe4_account_risk_history`     | The organisation's risk score over time, one row per date                                                                   |
| Users                      | `knowbe4_users`                    | Every user with phish-prone percentage, risk score, manager, department, and join / last-sign-in dates                      |
| Groups                     | `knowbe4_groups`                   | Console, smart, and provisioning-managed groups with member counts and risk scores                                          |
| Group Members              | `knowbe4_group_members`            | One row per group membership — the edge list joining users to groups                                                        |
| Phishing Campaigns         | `knowbe4_phishing_campaigns`       | Campaign configuration: targets, frequency, durations, difficulty filter, and last result                                   |
| Phishing Security Tests    | `knowbe4_phishing_security_tests`  | Each simulated phishing test with its template, landing page, and delivered / opened / clicked / reported counters          |
| Phishing Recipients        | `knowbe4_phishing_recipients`      | **Per-user** phishing outcomes — who clicked, entered data, enabled a macro, or reported the mail, with IP, browser, and OS |
| Training Store Purchases   | `knowbe4_training_store_purchases` | The purchased content library: modules, videos, games, and newsletters with duration and retirement status                  |
| Training Policies          | `knowbe4_training_policies`        | Uploaded policy documents, their minimum review time, and draft / published / archived status                               |
| Training Campaigns         | `knowbe4_training_campaigns`       | Training campaigns with targeted groups, assigned content, dates, and auto-enrolment settings                               |
| Training Enrollments       | `knowbe4_training_enrollments`     | One row per user per assigned module — status, time spent, and completion date                                              |

> **`knowbe4_phishing_recipients` is the table most reporting questions actually
> need.** "Who failed the last phishing test", "which department clicks most",
> and "who reported it" are all per-recipient questions; the counters on
> `knowbe4_phishing_security_tests` only give you the totals. It carries a
> derived `failed` column that is true when the user clicked, replied, entered
> data, opened an attachment, enabled a macro, or scanned a QR code — so you
> don't have to remember all six.

***

### Two percentage scales

KnowBe4 reports phish-prone percentages on **two different scales**, and mixing
them in one query produces answers that are wrong by a factor of 100:

| Column                                                   | Scale   | `0.3` means |
| -------------------------------------------------------- | ------- | ----------- |
| `knowbe4_users.phish_prone_percentage`                   | 0 – 100 | 0.3%        |
| `knowbe4_phishing_campaigns.last_phish_prone_percentage` | 0 – 1   | 30%         |
| `knowbe4_phishing_security_tests.phish_prone_percentage` | 0 – 1   | 30%         |

ConfigView stores each exactly as KnowBe4 returns it rather than silently
normalising, so the numbers always match what the KnowBe4 console shows.

***

### Three things that look like bugs but aren't

These were all confirmed against live account data, so don't "correct" them:

**`knowbe4_group_members` reports more members than `knowbe4_groups.member_count`.**
KnowBe4's `member_count` counts **active members only**, while the derived
membership table includes archived users too (they keep their group
memberships). On a live account every group reconciled exactly as
`member_count = active members`, with the difference being archived users. That
extra detail is the point — "who was in the Finance group before they left" is
an offboarding question you can't answer from `member_count`. To match the
KnowBe4 console, join to `knowbe4_users` and filter `status = 'active'`.

**A phishing test can have more recipient rows than its own `scheduled_count`.**
The counters on `knowbe4_phishing_security_tests` come from KnowBe4's campaign
summary and can lag the per-recipient detail — one live test reported
`scheduled_count = 105` while the recipients endpoint returned 153 people. Treat
`knowbe4_phishing_recipients` as authoritative for per-user questions and count
rows there rather than reading the summary counters.

**`phish_prone_percentage` can exceed 100.** KnowBe4 derives it from failures
against tests taken, and accounts with few tests can produce values above 100
(a live account had one archived user at 200). ConfigView stores what the API
returns rather than clamping it, so the value always matches the console. Filter
or clamp in the query if you're charting it.

***

### Configuring Phishing Recipients

Phishing Recipients is the only collector that makes one request **per phishing
security test**, so it is the only one that can grow without bound as your
account accumulates history. It reads a rolling window rather than everything.

The window defaults to a **year**, because `KNOWBE4_PST_MAX_TESTS` is the real
cost ceiling and a short window is actively misleading: if you run a phishing
test every few months, a 90-day window collects nothing, and an empty table
reads as "nobody was phished" rather than "nothing was collected".

Rows deduplicate on `(pst_id, recipient_id)` and the cleanup only touches the
tests a given run actually re-read, so **recipients of older tests are never
deleted** — the window bounds what is re-fetched, not what is kept. A window
wider than your schedule interval is deliberate: it backfills what a failed or
skipped run would have lost, and it lets late events land, such as a click or a
Phish Alert report that happens days after delivery.

| Variable                    | Default | Effect                                                     |
| --------------------------- | ------- | ---------------------------------------------------------- |
| `KNOWBE4_PST_LOOKBACK_DAYS` | `365`   | How far back each run re-reads phishing tests              |
| `KNOWBE4_PST_MAX_TESTS`     | `200`   | Cap on tests per run; the run warns loudly if it truncates |

***

### Other optional settings

| Variable                                | Default    | Effect                                                                                                           |
| --------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------- |
| `KNOWBE4_REGION`                        | `us`       | `us`, `eu`, `ca`, `uk`, or `de` — must match your console                                                        |
| `KNOWBE4_INCLUDE_ARCHIVED`              | `1`        | Also collect archived users and groups. Set to `0` to halve the request cost of Users, Groups, and Group Members |
| `KNOWBE4_TRAINING_CAMPAIGN_PERCENTAGES` | `0`        | See the warning below — leave this off                                                                           |
| `KNOWBE4_MAX_REQUESTS_PER_RUN`          | per script | Hard cap on requests one run may spend                                                                           |
| `KNOWBE4_PER_PAGE`                      | `500`      | Page size; 500 is KnowBe4's documented maximum                                                                   |

> **Leave `KNOWBE4_TRAINING_CAMPAIGN_PERCENTAGES` off.** KnowBe4 documents that
> requesting the `completion_percentage` field on the training campaigns endpoint
> **caps the response at ten records**. Turning it on would silently give you an
> inventory of ten campaigns that still looks like a successful run. With it off,
> ConfigView asks for the complete list and leaves `completion_percentage` NULL.

***

### About pagination

KnowBe4 is retiring the `page` parameter in **November 2026** in favour of
`cursor`, and the two cannot be combined — if both are sent, the cursor wins and
returns a differently-ordered window. ConfigView uses cursor pagination and
falls back to numbered pages only for endpoints that ignore the cursor, so
collection is unaffected by the cutover and no configuration change is needed.

***

### About the API limits

KnowBe4 enforces three separate ceilings on the Reporting API:

* **2,000 requests per day**, plus one per licensed seat on your account
* **4 requests per second**
* **50 requests per minute** of burst

The daily figure is the one that usually binds, and it is shared by every
KnowBe4 collector *and* by anything else using the same key. ConfigView paces
itself well inside the per-second and per-minute limits, and each script also
refuses to spend more than its own share of the daily budget — if it reaches
that cap it stops cleanly and commits what it has, rather than exhausting the
account's quota and starving the other collectors.

Two design choices exist to keep that budget small:

* **Group Members** derives membership from each user's group list instead of
  calling KnowBe4 once per group, so it costs a handful of requests instead of
  one per group.
* **Phishing Recipients** uses the rolling window described above.

If a health check reports `429`, the account's daily budget is already spent —
usually by another integration sharing the same key. Wait for the daily reset,
which is roughly 24 hours from the account's first API request of the day.

***

### Data Tables

Once the scripts run, the corresponding KnowBe4 tables will be created in your
database. All tables include a `run_at` column for historical tracking and a
`raw` JSON column with the full KnowBe4 object.

Two tables are exceptions to snapshot semantics:

* `knowbe4_account_risk_history` is **append-only**. Each row is a dated risk
  score and rows accumulate across runs, so query it by `score_date`, not by
  `run_at`.
* `knowbe4_phishing_recipients` keeps history outside the lookback window, so
  `run_at` records when each recipient row was last refreshed rather than
  marking a full snapshot. Query it by `pst_id` or the event timestamps.
