From knowbe4
Provides patterns for KnowBe4 REST API including Bearer token authentication, multi-region base URLs (US, EU, CA, UK, DE), pagination, rate limiting, error handling, and common requests.
npx claudepluginhub wyre-technology/msp-claude-plugins --plugin knowbe4This skill uses the workspace's default tool permissions.
The KnowBe4 Reporting API provides read access to account data including users, groups, phishing campaigns, training campaigns, and risk scores. Authentication uses Bearer token (API key) in the Authorization header. The API is region-specific -- your base URL depends on where your KnowBe4 account is hosted.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Designs, implements, and audits WCAG 2.2 AA accessible UIs for Web (ARIA/HTML5), iOS (SwiftUI traits), and Android (Compose semantics). Audits code for compliance gaps.
The KnowBe4 Reporting API provides read access to account data including users, groups, phishing campaigns, training campaigns, and risk scores. Authentication uses Bearer token (API key) in the Authorization header. The API is region-specific -- your base URL depends on where your KnowBe4 account is hosted.
KnowBe4 uses a simple API key passed as a Bearer token:
GET /v1/users
Authorization: Bearer YOUR_API_KEY
Accept: application/json
Required Headers:
| Header | Value | Description |
|---|---|---|
Authorization | Bearer <API_KEY> | API authentication token |
Accept | application/json | Response format |
Content-Type | application/json | Request body format (for POST/PUT) |
export KNOWBE4_API_KEY="your-api-token-here"
export KNOWBE4_REGION="US" # US, EU, CA, UK, DE
KnowBe4 operates in multiple geographic regions. Your API base URL is determined by your account's region:
| Region | Base URL | Data Center |
|---|---|---|
| US | https://us.api.knowbe4.com | United States |
| EU | https://eu.api.knowbe4.com | European Union (Frankfurt) |
| CA | https://ca.api.knowbe4.com | Canada |
| UK | https://uk.api.knowbe4.com | United Kingdom |
| DE | https://de.api.knowbe4.com | Germany |
Your region is set when your KnowBe4 account is created and cannot be changed. To determine your region:
training.knowbe4.com = USeu.knowbe4.com = EUca.knowbe4.com = CAuk.knowbe4.com = UKde.knowbe4.com = DEfunction getBaseUrl(region) {
const regionMap = {
'US': 'https://us.api.knowbe4.com',
'EU': 'https://eu.api.knowbe4.com',
'CA': 'https://ca.api.knowbe4.com',
'UK': 'https://uk.api.knowbe4.com',
'DE': 'https://de.api.knowbe4.com'
};
return regionMap[region.toUpperCase()] || regionMap['US'];
}
// Example: GET https://us.api.knowbe4.com/v1/users
const url = `${getBaseUrl('US')}/v1/users`;
The current API version is v1. All endpoints are prefixed with /v1/:
https://{region}.api.knowbe4.com/v1/{resource}
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/account | Account-level information and summary stats |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/users | List all users |
| GET | /v1/users/{user_id} | Get user details |
| GET | /v1/users/{user_id}/risk_score_history | User risk score history |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/groups | List all groups |
| GET | /v1/groups/{group_id} | Get group details |
| GET | /v1/groups/{group_id}/members | List group members |
| GET | /v1/groups/{group_id}/risk_score_history | Group risk score history |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/phishing/campaigns | List phishing campaigns |
| GET | /v1/phishing/campaigns/{campaign_id} | Get campaign details |
| GET | /v1/phishing/security_tests | List all security tests |
| GET | /v1/phishing/security_tests/{pst_id} | Get security test details |
| GET | /v1/phishing/security_tests/{pst_id}/recipients | List test recipients |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/training/campaigns | List training campaigns |
| GET | /v1/training/campaigns/{campaign_id} | Get campaign details |
| GET | /v1/training/enrollments | List all enrollments |
| GET | /v1/training/enrollments/{enrollment_id} | Get enrollment details |
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/training/store_purchases | List store purchases |
| GET | /v1/training/store_purchases/{purchase_id} | Get purchase details |
KnowBe4 uses page-based pagination with query parameters:
GET /v1/users?page=1&per_page=500
| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
page | int | 1 | - | Page number (1-based) |
per_page | int | 25 | 500 | Records per page |
Pagination metadata is returned in response headers:
| Header | Description |
|---|---|
Total | Total number of records |
Per-Page | Records per page |
Total-Pages | Total number of pages |
Current-Page | Current page number |
Link | Links to first, last, next, prev pages |
Link: <https://us.api.knowbe4.com/v1/users?page=2&per_page=25>; rel="next",
<https://us.api.knowbe4.com/v1/users?page=10&per_page=25>; rel="last",
<https://us.api.knowbe4.com/v1/users?page=1&per_page=25>; rel="first"
async function fetchAllPages(endpoint, apiKey, region) {
const baseUrl = getBaseUrl(region);
const allItems = [];
let page = 1;
let totalPages = 1;
do {
const response = await fetch(`${baseUrl}${endpoint}?page=${page}&per_page=500`, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Accept': 'application/json'
}
});
const data = await response.json();
allItems.push(...data);
totalPages = parseInt(response.headers.get('Total-Pages') || '1');
page++;
} while (page <= totalPages);
return allItems;
}
| Limit | Value | Scope |
|---|---|---|
| Requests per day | 1,000 | Per API token |
| Requests per minute | Not published | Varies by endpoint |
When rate limited, the API returns HTTP 429:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"error": "Rate limit exceeded. Please try again later."
}
async function requestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
const jitter = Math.random() * 5000;
await sleep((retryAfter * 1000) + jitter);
continue;
}
return response;
}
throw new Error('Max retries exceeded');
}
| Code | Meaning | Action |
|---|---|---|
| 200 | Success | Process response |
| 400 | Bad Request | Check query parameters |
| 401 | Unauthorized | Invalid or missing API key |
| 403 | Forbidden | API key lacks required permissions |
| 404 | Not Found | Resource does not exist |
| 429 | Rate Limited | Wait and retry with backoff |
| 500 | Server Error | Retry with exponential backoff |
{
"error": "Description of what went wrong",
"message": "Additional detail if available"
}
| Error | Cause | Resolution |
|---|---|---|
| 401 on all requests | Wrong API key | Regenerate token in KnowBe4 console |
| 401 on some requests | Token lacks scope | Check API token permissions |
| 404 on valid ID | Wrong region | Verify KNOWBE4_REGION matches account |
| Empty results | No data in date range | Widen date range or check filters |
| Timeout | Large result set | Use pagination with smaller page sizes |
async function knowbe4Request(endpoint, options) {
const response = await requestWithRetry(endpoint, options);
if (!response.ok) {
const error = await response.json().catch(() => ({}));
switch (response.status) {
case 401:
throw new Error('Invalid API key. Regenerate at Account Settings > API > API Token');
case 403:
throw new Error('API key lacks required permissions. Check token scope.');
case 404:
throw new Error(`Resource not found. Verify the ID and that KNOWBE4_REGION (${process.env.KNOWBE4_REGION}) is correct.`);
case 429:
throw new Error('Rate limited. Reduce request frequency.');
default:
throw new Error(`KnowBe4 API error ${response.status}: ${error.error || 'Unknown error'}`);
}
}
return response.json();
}
List endpoints return a JSON array:
[
{
"id": 12345,
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@company.com"
},
{
"id": 12346,
"first_name": "John",
"last_name": "Smith",
"email": "john.smith@company.com"
}
]
Detail endpoints return a JSON object:
{
"id": 12345,
"first_name": "Jane",
"last_name": "Doe",
"email": "jane.doe@company.com",
"phish_prone_percentage": 12.5,
"current_risk_score": 28.3
}
All dates use ISO 8601 format:
2024-02-15T14:30:00.000Z
per_page=500 to minimize API callsTotal-Pages headerGET /v1/account is the simplest way to verify connectivity