# Quickstart

The Keepnet API lets you automate platform operations: pull reports, manage users, onboard customers, and integrate with SIEM, SOAR, or identity systems.

You can use the Keepnet API to:

* Pull campaign reports (phishing, smishing, quishing, vishing, callback)
* Export training reports, gamification data, and executive summaries
* Add target users and enroll them in training
* List and manage companies (Reseller)
* Export audit logs to SIEM

…and more, all via REST calls with OAuth 2.0 Client Credentials.

Use the API from AI assistants (Cursor, Claude, ChatGPT, etc.) via MCP: [Use the API with AI assistants (MCP) →](/api-reference/use-the-api-with-ai-assistants-mcp.md)

***

{% stepper %}
{% step %}
**Getting started**

You need a Keepnet platform account with API access. Generate **Client ID** and **Client Secret** from **Company → Company Settings → REST API** before making any API calls.

{% hint style="info" %}
**Platform UI:** Go to **Company → Company Settings → REST API** and click **+ NEW**. [REST API Settings →](/next-generation-product/platform/company/company-settings/rest-api.md)
{% endhint %}

| Field              | Description                                                                 |
| ------------------ | --------------------------------------------------------------------------- |
| **Name**           | A label for this credential set (e.g. `prod-integration`, `siem-connector`) |
| **Client Role**    | `Company Admin` for full access, or a custom role with limited privileges   |
| **IP Restriction** | Optionally restrict access to specific IP addresses                         |
| **Status**         | Set to `Active`                                                             |

{% hint style="warning" %}
**The Client Secret is shown only once.** Copy and store it securely immediately after you generate it — it cannot be retrieved later.
{% endhint %}
{% endstep %}

{% step %}
**Request an access token**

Keepnet API uses **OAuth 2.0 Client Credentials**. Every request requires a Bearer token in the `Authorization` header.

**Endpoint:** `POST /connect/token`

{% tabs %}
{% tab title="HTTP" %}
Send a POST request with form-encoded body:

```bash
curl -X POST https://api.keepnetlabs.com/connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=api1"
```

Response:

```json
{
  "access_token": "eyJ...",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": "api1",
  "permissions": ["tags/search|POST", "companies/search|POST", "..."]
}
```

Include `access_token` in every API request as `Authorization: Bearer <token>`. The optional `permissions` array lists endpoints your credential can call (e.g. `companies/search|POST`).
{% endtab %}

{% tab title="JavaScript" %}

```javascript
const response = await fetch('https://api.keepnetlabs.com/connect/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: process.env.CLIENT_ID,
    client_secret: process.env.CLIENT_SECRET,
    scope: 'api1',
  }),
});

const { access_token } = await response.json();
// Use: Authorization: Bearer ${access_token}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Integration tools (Zapier, Make, n8n, Postman, etc.)**\
The token endpoint accepts **only** `application/x-www-form-urlencoded`. Do **not** send a JSON body or use "Raw" with `Content-Type: application/json` — that returns a **500 Internal Server Error**. In your tool, choose **Form** (or form-urlencoded) as the body type and send the fields: `grant_type`, `client_id`, `client_secret`, `scope`.
{% endhint %}

{% hint style="success" %}
**Test it — step by step**

**Authorize** is not on this page; it is in the **Endpoints** section. Open **Endpoints** in the left sidebar → pick an endpoint (e.g. **Company** → **Retrieves a list of all companies**) → in the right panel click the lock icon **Authorize** → enter **Client ID** and **Client Secret** → click **Authorize**. The token is fetched and applied to all requests. Then click **Send** in the same panel to send the request.

* **GET /api/companies/my:** Endpoints → **Company** → **Retrieves the companies which can be simulated by user** → **Send**. No body.
* **POST /api/companies/search:** Endpoints → **Company** → **Retrieves a list of all companies** → Body is pre-filled → **Send**. Reseller credential required.

Alternatively, get a token from the **POST /connect/token** block on this page, copy `access_token` from the response, and paste it into the Bearer field in Endpoints instead of using Authorize.
{% endhint %}
{% endstep %}

{% step %}
**Make your first API call**

Verify your token and company access with `GET /api/companies/my` — returns your company (Company Admin) or companies you manage (Reseller). No request body, no extra parameters.

```bash
curl -X GET "https://api.keepnetlabs.com/api/companies/my" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json"
```

If successful, you'll get a `data` array with your company details (id, name, etc.). This confirms:

* Token is valid
* Credentials have API access
* You can fetch your own company data
  {% endstep %}
  {% endstepper %}

***

## Test it — quick checklist

The **Authorize** button is not on the Quickstart page; it appears in the right panel when you open an endpoint under **Endpoints** (left sidebar). Order:

| Step | Action                                                                                                                                                   | Result                                                                  |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| 1    | **Endpoints** → open any endpoint (e.g. Company → Retrieves a list…) → **Authorize** (lock icon) → enter Client ID + Client Secret → click **Authorize** | Token is fetched and applied to all requests                            |
| 2    | **GET /api/companies/my** (Company) → Send                                                                                                               | Returns your company (Company Admin) or companies you manage (Reseller) |
| 3    | **POST /api/companies/search** (Company) → Body pre-filled → Send                                                                                        | Company list with license details (Reseller credential required)        |

{% hint style="info" %}
With a **Company Admin** credential, step 3 returns `403 Forbidden`. This is expected — `companies/search` is for Reseller only.
{% endhint %}

***

## Base URL and response format

**Base URL:** `https://api.keepnetlabs.com`

All API responses include an `isSuccess` field. When `isSuccess` is `false`, check the `messages` array for error details.

| Field       | Type    | Description                              |
| ----------- | ------- | ---------------------------------------- |
| `isSuccess` | boolean | Indicates whether the request succeeded  |
| `data`      | object  | Response payload when successful         |
| `messages`  | array   | Error or validation messages when failed |

***

## Common conventions

| Convention              | Description                                                                                                                                                                                                                                                                                                                     |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Pagination**          | Search endpoints use `pageNumber`, `pageSize`, `orderBy`, `ascending`, `filter` in the request body                                                                                                                                                                                                                             |
| **Search request body** | **Do not send an empty body** for search endpoints. Always include at least `pageNumber`, `pageSize`, `orderBy`, `ascending`, and `filter` (or the structure shown in the endpoint). Empty or missing body can cause errors or unreliable results in automation tools.                                                          |
| **Filter**              | **Do not** send **`filter: null`** or **`null`** inside filter fields on search endpoints that expect a filter object — use a **minimal object**: `"Condition": "AND"`, `"SearchInputTextValue": ""` (then add `FilterGroups` / items only if the endpoint requires them). **`searchInputTextValue` must be `""`, not `null`**. |
| **Sorting**             | **`orderBy`** must be a **non-null string** (e.g. `CreateTime`, `Email`). Set **`ascending`** explicitly (`true` / `false`); do not rely on undocumented defaults.                                                                                                                                                              |
| **Company scope**       | Most endpoints require a company context. Company Admin: automatic. Reseller: include Company ID (header/path/query)                                                                                                                                                                                                            |

***

## Integration and automation

When calling the API from automation platforms (Zapier, Make, n8n, custom scripts, etc.), follow these rules to avoid common errors:

| Topic                                                    | Requirement                                                                                                                                                                                                                        | If you don't                                                                                     |
| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| **Token (`POST /connect/token`)**                        | Body must be **form-encoded** (`application/x-www-form-urlencoded`). Use the **Form** body type in your tool; send `grant_type`, `client_id`, `client_secret`, `scope`.                                                            | Sending JSON or Raw with `Content-Type: application/json` returns **500 Internal Server Error**. |
| **Search endpoints** (e.g. `POST /api/companies/search`) | Send **`pageNumber`**, **`pageSize`**, explicit non-null **`orderBy`**, **`ascending`**, and a **minimal `filter`** (**`Condition`** + **`SearchInputTextValue`: `""`**). Do **not** use **`filter: null`** or null filter fields. | **400** / **500** or **`INPUT_ERROR`**.                                                          |

***

## What the token contains

The access token is a **JWT**. Decode it to read company context, role, and other values — useful when endpoints require Company ID in the path, header, or query.

**To decode:** Paste the token at [jwt.io](https://jwt.io) (signature verification optional) or Base64URL-decode the middle part (between the two dots).

### Company context

| Claim                                   | Description                                                                                          | Example                                       |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `user_company_resourceid`               | **Company ID** — use in `X-KEEPNET-Company-Id`, path `/api/.../companies/{id}/...`, or `?companyId=` | `xC5kfGz7w2Nz`                                |
| `user_company_name`                     | Company name                                                                                         | `ACME LLC`                                    |
| `user_company_id`                       | Numeric company ID                                                                                   | `1`                                           |
| `user_company_logopath`                 | Company logo URL                                                                                     | `https://api.keepnetlabs.com/companylogo/...` |
| `user_company_industry_name`            | Industry (e.g. Technology)                                                                           | `Technology`                                  |
| `user_company_parentcompany_resourceid` | Parent/MSSP company ID — **Reseller only**, empty for Company Admin                                  | `xC5kfGz7w2Nz` (Reseller) or empty            |
| `user_company_parentcompany_name`       | Parent company name — **Reseller only**                                                              | `ACME Corp`                                   |

### Role and access

| Claim                  | Description                   | Company Admin   | Reseller   |
| ---------------------- | ----------------------------- | --------------- | ---------- |
| `role`                 | User role                     | `Company Admin` | `Reseller` |
| `company_admin_access` | Can manage own company        | `true`          | `true`     |
| `reseller_access`      | Can manage multiple companies | `false`         | `true`     |
| `root_access`          | Platform root/admin           | `false`         | `false`    |
| `scope`                | API scope                     | `["api1"]`      | `["api1"]` |

### Token metadata

| Claim       | Description                        | Example            |
| ----------- | ---------------------------------- | ------------------ |
| `exp`       | Expiry (Unix timestamp)            | `1772935488`       |
| `iat`       | Issued at (Unix timestamp)         | `1772931888`       |
| `client_id` | Client ID used to obtain the token | `xxxxxxxxxxxxxxxx` |

{% hint style="info" %}
**Company Admin**

* `user_company_resourceid` = your Company ID — use when an endpoint requires a company context (e.g. `GET /api/companies/{resourceId}`)
* `user_company_parentcompany_*` = empty
  {% endhint %}

{% hint style="info" %}
**Reseller**

* `user_company_resourceid` = your Reseller company
* `user_company_parentcompany_resourceid` = parent/MSSP company
* Managed company IDs → `POST /api/companies/search` — use those IDs in `X-KEEPNET-Company-Id` for company-scoped requests
  {% endhint %}

***

## Who can use the API

| Role              | Access                                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------------------------- |
| **Company Admin** | Full access to your company's data — no Company ID needed                                               |
| **Reseller**      | Cross-company management — list companies, onboard customers, pull reports per company using Company ID |
| **Custom role**   | Scoped to specific products or actions based on role definition                                         |

***

## Token expiry and errors

Tokens expire after **1 hour**. No refresh token — request a new one when the current token expires.

| HTTP status                            | Cause                                              | Action                                                                                                             |
| -------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `400 Bad Request` — `invalid_client`   | Wrong or placeholder credentials                   | Use real credentials from **Company → Company Settings → REST API**                                                |
| `401 Unauthorized`                     | Missing or invalid token                           | Re-authenticate and retry                                                                                          |
| `403 Forbidden`                        | Insufficient role permissions                      | Check the Client Role in platform settings                                                                         |
| `500 Internal Server Error` (on token) | Token request sent with JSON or wrong Content-Type | Use **form-encoded** body only: `Content-Type: application/x-www-form-urlencoded` and Form body type in your tool. |
| `429 Too Many Requests`                | Rate limit exceeded                                | Back off and retry after a delay                                                                                   |

***

## Reseller: scope by Company ID

If your credential has the **Reseller** role, include Company ID when calling company-scoped endpoints:

* **Header:** `X-KEEPNET-Company-Id: {companyId}` (when supported)
* **Path:** `/api/.../companies/{companyId}/...`
* **Query:** `?companyId={companyId}`

Company IDs are returned by `POST /api/companies/search`. For a full explanation and usage in report and user endpoints, see [Scope API requests to a customer →](/api-reference/reseller/companies/scope-api-requests-to-customer.md). For the list: [List companies with license details →](/api-reference/reseller/companies/list-companies-with-license-details.md).

***

## Keep credentials secure

{% hint style="success" %}
**Do:** Store credentials in environment variables or a secrets manager. Enable IP Restriction if your integration runs from a fixed IP. Use a custom role scoped to only the endpoints you need.
{% endhint %}

{% hint style="danger" %}
**Don't:** Hard-code `client_id` or `client_secret` in source code. Don't commit credentials to version control.
{% endhint %}

***

## Explore the API

| What you want to do                  | Use case                                                                                                                                       |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| List companies (Reseller)            | [List companies with license details →](/api-reference/reseller/companies/list-companies-with-license-details.md)                              |
| Scope requests to a customer         | [Scope API requests to a customer →](/api-reference/reseller/companies/scope-api-requests-to-customer.md)                                      |
| Export customer list for billing     | [Export customer list for billing →](/api-reference/reseller/billing/export-customer-list-for-billing.md)                                      |
| View customer's training enrollments | [View customer's enrollment list and report →](/api-reference/reseller/training/view-customer-enrollment-list-and-report.md)                   |
| View customer's simulation campaigns | [View customer's campaign list and report →](/api-reference/reseller/phishing-simulation/view-customer-simulation-campaign-list-and-report.md) |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.keepnetlabs.com/api-reference/quickstart.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
