KnowBe4 — Integration Setup
Privacy note, up front: CMDash never stores a per-employee identifier from KnowBe4. Recipient and enrollment rows are read into memory for a single sync, reduced to counts and percentages, and discarded. KnowBe4 remains the only system of record for "who clicked which simulation"; CMDash holds only the resulting aggregates.
Prerequisites
- A KnowBe4 subscription that includes the Reporting API (Platinum/Diamond tiers, or an add-on on lower tiers)
- KnowBe4 account-admin access, to generate the Reporting API key
- At least one completed (Closed) phishing security test — an account with only in-flight tests produces no observations
- CMDash Essentials tier or above
Step 1: Generate a Reporting API Key
- Log in to the KnowBe4 console.
- Go to Account Settings → Account Integrations → API (labelled Reporting API / API Keys depending on console version).
- Enable the Reporting API if it is not already enabled, and click to generate a key.
- Copy the key immediately — it is shown only once.
- Note your console's region (the hostname you log in to, e.g.
training.knowbe4.com,eu.knowbe4.com) — you need the matching API host in Step 2.
Minimum privileges. KnowBe4's Reporting API key is a single account-wide read-only credential — there is no scope selector, and it grants no write access to campaigns, users, or training. The adapter reads only phishing security tests, their recipients, training campaigns, enrollments, and (optionally) groups and group members; it never creates or modifies anything in KnowBe4. If your KnowBe4 edition offers a choice between the Reporting API and the User Event API, only the Reporting key is needed.
Step 2: Connect in CMDash
- Navigate to Integrations in your CMDash dashboard.
- Find KnowBe4 and click Connect.
- Enter:
- API Key — the Reporting API key from Step 1 (stored as this connection's access token, sent as
Authorization: Bearer <key>) - Configuration (JSON):
{ "baseUrl": "https://us.api.knowbe4.com", "businessUnitGroupMap": { "Engineering": "<cmdash-business-unit-id>", "Finance": "<cmdash-business-unit-id>" } }
- API Key — the Reporting API key from Step 1 (stored as this connection's access token, sent as
- Click Connect.
Base URL format. KnowBe4 serves the Reporting API from a region-specific host, and a key issued in one region returns 401/404 against another — this is the most common setup failure for this integration.
- The default when
baseUrlis omitted is the US host,https://us.api.knowbe4.com. - Use your account's region host instead if you are not on US: the pattern is
https://<region>.api.knowbe4.com, matching the region of the console you log in to. Confirm the exact host in KnowBe4's API documentation for your account before saving. - Scheme and host only — CMDash appends
/v1/...itself. A trailing slash is stripped for you. - The host must be publicly resolvable; CMDash validates it on save and rejects private/reserved addresses and
.internal/.local/localhosthosts.
businessUnitGroupMap (optional) maps a KnowBe4 group name to a CMDash business-unit id, and is the only way this integration can produce a per-business-unit breakdown. Names are matched exactly as KnowBe4 reports them, so copy them from Users → Groups rather than retyping. Omit the key entirely and every observation is emitted tenant-wide.
What Syncs
| Data Type | KnowBe4 Reporting API | Sync Frequency |
|---|---|---|
| Phishing fail rate (last 5 closed tests) | GET /v1/phishing/security_tests?page=N&per_page=200 | Every 4 hours by default (configurable per connection) |
| Repeat-clicker ratio, per-BU click slice | GET /v1/phishing/security_tests/{id}/recipients | Every 4 hours by default (configurable per connection) |
| Training completion | GET /v1/training/campaigns?page=N&per_page=200 | Every 4 hours by default (configurable per connection) |
| Time-to-complete, per-BU training slice | GET /v1/training/enrollments?page=N&per_page=200 | Every 4 hours by default (configurable per connection) |
Group membership (only when businessUnitGroupMap is set) | GET /v1/groups, GET /v1/groups/{id}/members | Every 4 hours by default (configurable per connection) |
CMDash bounds itself to the 5 most recently started tests with status Closed each sync. From those it emits, as HUMAN_RISK maturity observations at the OBSERVED tier:
- Phishing fail rate, one observation per test, taken directly from KnowBe4's own
phish_prone_percentage— no per-recipient arithmetic involved. - Repeat-clicker ratio — of the recipients who appear in at least two tests in that window, the fraction who clicked in at least two. This is the one metric with no vendor-side equivalent, so it is derived from recipient rows in memory. If nobody has been exposed twice yet, the observation is omitted rather than reported as 0%.
- Training completion, one observation per campaign, from KnowBe4's own
completion_percentage. - Average time to complete, derived from enrollments that have a completion date and a recorded
time_spent. It is disclosure-only context and does not independently move the maturity score. - Per-business-unit phishing fail rate, repeat-clicker ratio, and training completion, when
businessUnitGroupMapresolves membership for at least one person.
Every observation carries the control reference NIST CSF 2 PR.AT-01. Per-BU aggregates are stored honestly at ingest and suppressed at display time by a minimum-group-size (k-anonymity) floor, so a small unit's numbers are collected but not shown.
Troubleshooting
The connection detail page shows the health check's message. The health check calls GET {baseUrl}/v1/account, so it fails on exactly the credential and region problems a sync would hit:
HTTP 401: The key is wrong or was regenerated in KnowBe4 — or, very commonly, the key belongs to a different region thanbaseUrlpoints at.HTTP 403: The Reporting API is not enabled for the account, or the subscription tier does not include it.HTTP 404:baseUrlincludes a path (it should be scheme + host only) or points at the console host rather than the API host.HTTP 429: KnowBe4's own rate limit. CMDash paces itself at 240 requests/minute for this provider, but a very large account plus other API consumers can still hit KnowBe4's daily/short-window caps.URL host not allowed: …/… resolves to a private/reserved address:baseUrlis not a public host.Request to <host> timed out: No response within 15 seconds.KnowBe4 circuit breaker open — skipping this cycle: 5 failures inside 60 seconds tripped the per-tenant breaker; it fails fast for a 30-second cool-off, then probes.- Sync succeeds but no phishing observations appear: There are no tests with status
Closed. Tests still running, or in any other state, are ignored by design. - No repeat-clicker observation: Either the recipient fetch failed (logged as
KnowBe4 recipients fetch failed for pst <id> …, which deliberately does not fail the sync) or no recipient has appeared in two of the last five closed tests yet. - No per-business-unit breakdown:
businessUnitGroupMapis absent, the KnowBe4 group names in it do not match exactly, or the group/member fetch failed (logged asKnowBe4 group membership fetch failed …). Org-wide observations are unaffected in all three cases. - No time-to-complete figure: Enrollments returned no rows with both a completion date and a
time_spentvalue, or the enrollment fetch failed (logged, non-fatal). - A business unit's numbers are missing from the UI even though it syncs: The k-anonymity floor is suppressing a group that is too small to show without identifying individuals.
Rate Limits & Sync Behavior
| Setting | Value |
|---|---|
| Sync frequency | Every 4 hours by default (syncInterval, in seconds, is configurable per connection; default 14400) |
| Test window | The 5 most recent closed phishing tests per sync |
| Pagination cap | 200 rows per request, 5,000 rows per dataset, per sync; reaching it, or failing to read recipients, enrollments or group membership (a token without the broader report scope), marks the sync Partial with a warning |
| Response size cap | 5 MiB per response, enforced by CMDash's SSRF-safe HTTP client |
| Request timeout | 15 seconds per request |
| Rate limit handling | Every request is paced through CMDash's shared token-bucket limiter at 240 requests/minute per tenant for this provider (KnowBe4's documented ~4 requests/second soft limit) |
| Circuit breaker | 5 failures in 60 seconds trips the breaker OPEN for 30 seconds, then a single probe decides whether it closes |
| Observation freshness | HUMAN_RISK observations decay on a 30-day half-life — a program that stops running simulations loses credit gradually |
| Failed sync retry | Job queue retries 3 times with exponential backoff (starting at 30s) — see docs/developers/dead-letter.md |
Credential Rotation
KnowBe4 Reporting API keys do not expire on a fixed schedule. CMDash stores the key as a non-expiring credential and cannot show an expiry warning banner for it; rotate on your own cadence (quarterly is a reasonable default).
To rotate:
- In KnowBe4: Account Settings → Account Integrations → API, generate a new key.
- In CMDash: Settings → Integrations → KnowBe4 → Edit.
- Enter the new API Key and Save.
- Revoke the old key in KnowBe4 once the connection reports healthy.
Multiple Instances / Multi-Site
Each connection reads one KnowBe4 account in one region. Separate accounts (e.g. a subsidiary with its own KnowBe4 subscription) each need their own CMDash connection, with their own key, baseUrl, and businessUnitGroupMap.
To add another account:
- Generate a Reporting API key in the additional KnowBe4 account, following Step 1.
- In CMDash: Settings → Integrations → Add Integration → KnowBe4.
- Enter the key and a
baseUrlfor that account's region. - Give the connection a descriptive name (e.g.
KnowBe4 - Corp,KnowBe4 - EU Subsidiary). - Click Connect.
Observations from all connections roll up into the same human-risk maturity dimension.
Compliance Framework Coverage
The KnowBe4 integration provides evidence for the following controls. Coverage is automatically mapped when data is synced.
| Framework | Control | Description |
|---|---|---|
| NIST CSF 2 | PR.AT-01 | Personnel are provided with awareness and training — every KnowBe4 observation is stamped with this control reference directly |
| NIST CSF 2 | PR.AT-02 | Role-based training for individuals in specialised roles — fed indirectly, at half weight, through the human-risk maturity dimension |