Skip to main content

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 (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:

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

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: 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.

Other optional settings

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.