For the complete documentation index, see llms.txt. This page is also available as Markdown.

Create a REST API client for a customer

As a Reseller you can create a REST API client (Client ID / Client Secret) for a managed company so that company—or your integration—can call the Keepnet API with OAuth 2.0 client credentials. Use your Reseller token, scope the request with X-KEEPNET-Company-Id, then generate credentials and register the client. This mirrors Company → Company Settings → REST API in the UI; see REST API for field meanings (IP restriction, Client Role, status).


POST /api/companies/search

Get the customer’s Company ID. Each result includes companyResourceId — use that value as X-KEEPNET-Company-Id in the steps below. (Some docs refer to this value as the company’s resourceId; it is the same identifier.)

Retrieves a paginated list of all companies you manage with license details. Test it: Authorize with Client ID/Secret, then Send — request body is pre-filled.

Retrieves a list of all companies

post
Required scopes
This endpoint requires the following scopes:
  • : API
Authorizations
OAuth2clientCredentialsRequired

Client ID and Client Secret from Company → Company Settings → REST API. Enter credentials to auto-fetch token.

Token URL:
Body
pageNumberintegerRequiredExample: 1
pageSizeintegerRequiredExample: 10
orderBystringRequiredExample: CreateTime
ascendingbooleanRequiredExample: false
isTargetUserCountExceededLimitbooleanOptional

If true, only companies exceeding license limit are returned

Example: false
Responses
200

OK

application/json
statusstringOptional
messagestring · nullableOptional
validationMessagesstring[] · nullableOptional
post/api/companies/search
200

OK

From the response, note data.results[].companyResourceId for the target customer.


GET /api/roles

The create-client API requires at least one Client Role. List roles for the same customer by calling GET /api/roles with X-KEEPNET-Company-Id set to that customer’s companyResourceId. From the response, pick a role (for example Company Admin for that company’s integrations, or Reseller if your use case requires it) and copy its resourceId into roleResourceIdList when you call POST /api/companies/clients.

Returns all roles for the company in data (array of roles with name, resourceId, etc.). Test it: Endpoints → SystemRoleRetrieves a list of roles of company — set X-KEEPNET-Company-Id.

Retrieves a list of roles of company

get
Required scopes
This endpoint requires the following scopes:
  • : API
Authorizations
OAuth2clientCredentialsRequired

Client ID and Client Secret from Company → Company Settings → REST API. Enter credentials to auto-fetch token.

Token URL:
Responses
200

OK

application/json
statusstringOptional
messagestring · nullableOptional
validationMessagesstring[] · nullableOptional
get/api/roles
200

OK


GET /api/companies/clients/generate-client-credentials

Generate a Client ID and Client Secret pair for the next step. You must send the customer’s Company ID in X-KEEPNET-Company-Id so the credentials are created in that company’s context.

Returns generated clientId and clientSecret in data. Test it: Endpoints → CompanyRetrieves generated client credentials — set X-KEEPNET-Company-Id to a companyResourceId from companies/search.

Retrieves generated client credentials

get
Required scopes
This endpoint requires the following scopes:
  • : API
Authorizations
OAuth2clientCredentialsRequired

Client ID and Client Secret from Company → Company Settings → REST API. Enter credentials to auto-fetch token.

Token URL:
Responses
200

OK

application/json
statusstringOptional
messagestring · nullableOptional
validationMessagesstring[] · nullableOptional
get/api/companies/clients/generate-client-credentials
200

OK

Store data.clientSecret securely; it may not be shown again. Use data.clientId and data.clientSecret in the body of POST /api/companies/clients.


POST /api/companies/clients

Register the REST API client for that company. Send X-KEEPNET-Company-Id with the same customer Company ID.

Required body fields (see Endpoints for full schema):

  • name — Display name for this REST API configuration (1–64 characters).

  • clientId — From generate-client-credentials data.clientId.

  • clientSecret — From generate-client-credentials data.clientSecret.

  • statusId — Client status in the UI/API. 1 is Active in environments verified for this flow; use 2 or other values only if your tenant/UI indicates them (invalid statusId returns a validation error).

  • roleResourceIdListRequired by the API (at least one role). Use resourceId values from GET /api/roles for that company, e.g. ["<roleResourceIdFromGetRoles>"].

Optional:

  • hasIpAddressRestriction, allowedIpAddresses — IP allow list (see REST API). If the playground prefills hasIpAddressRestriction: true with an empty allowedIpAddresses entry, either set hasIpAddressRestriction to false or supply valid IP/CIDR strings; empty strings trigger Invalid ip address in validationMessages.

Creates a new client for the company. Test it: Endpoints → CompanyCreates a new client for company — set X-KEEPNET-Company-Id, paste clientId / clientSecret from the generate step, set name, statusId, and roleResourceIdList from GET /api/roles.

Creates a new client for company

post
Required scopes
This endpoint requires the following scopes:
  • : API
Authorizations
OAuth2clientCredentialsRequired

Client ID and Client Secret from Company → Company Settings → REST API. Enter credentials to auto-fetch token.

Token URL:
Body
namestring · min: 1 · max: 64Required
clientIdstring · min: 1 · max: 32Required
clientSecretstring · min: 1 · max: 100Required
statusIdinteger · int32Required
hasIpAddressRestrictionbooleanOptional
allowedIpAddressesstring[] · nullableOptional
roleResourceIdListstring[] · nullableOptional
Responses
200

OK

application/json
statusstringOptional
messagestring · nullableOptional
validationMessagesstring[] · nullableOptional
post/api/companies/clients
200

OK

Example request headers:

Example body (replace placeholders with values from generate-client-credentials and GET /api/roles; hasIpAddressRestriction omitted or false avoids empty-IP 400 errors in the playground):


List, update, or delete clients (optional)

POST /api/companies/clients/search

List REST API clients for the company. Send X-KEEPNET-Company-Id and Content-Type: application/json. Do not send filter: null — the API may respond with INTERNAL_SERVER_ERROR. Avoid null inside filter fields; use "" for SearchInputTextValue and "AND" for Condition. A minimal body that returns 200 on https://api.keepnetlabs.com:

You can add FilterGroups (empty FilterItems) if your client or older docs require it; the minimal shape above is enough for listing.

CamelCase (condition, searchInputTextValue, …) is also accepted if your client serializes that way.

Test it: Endpoints → CompanyRetrieves a list of all clients of company — set X-KEEPNET-Company-Id and paste the JSON above (replace filter: null in the playground if needed).

Retrieves a list of all clients of company

post
Required scopes
This endpoint requires the following scopes:
  • : API
Authorizations
OAuth2clientCredentialsRequired

Client ID and Client Secret from Company → Company Settings → REST API. Enter credentials to auto-fetch token.

Token URL:
Body
pageNumberinteger · int32Optional
pageSizeinteger · int32Optional
orderBystring · nullableOptional
ascendingbooleanOptional
Responses
200

OK

application/json
statusstringOptional
messagestring · nullableOptional
validationMessagesstring[] · nullableOptional
post/api/companies/clients/search
200

OK

Other client endpoints

  • GET /api/companies/clients/{resourceId} — Get one client by its resourceId (returned in the create response data.resourceId). Send X-KEEPNET-Company-Id when acting as Reseller.

  • PUT /api/companies/clients/{resourceId} — Update client (same header when Reseller).

  • DELETE /api/companies/clients/{resourceId} — Delete client (same header when Reseller).

Full request bodies: EndpointsCompany in the API Reference sidebar.


Verification note

The full Reseller flow was exercised end-to-end against https://api.keepnetlabs.com using a Reseller client_credentials token from ~/.zhc.env (or ~/zhc.env): POST /connect/tokenPOST /api/companies/searchGET /api/roles (with X-KEEPNET-Company-Id) → GET /api/companies/clients/generate-client-credentialsPOST /api/companies/clients (body included roleResourceIdList from roles, statusId: 1, hasIpAddressRestriction: false) → GET /api/companies/clients/{resourceId}POST /api/companies/clients/search (minimal filter: Condition + SearchInputTextValue: "", pageSize: 10) → DELETE /api/companies/clients/{resourceId} (cleanup).

Last automated check (2026-04-01T15:01Z, repo script, exit code 0): each step printed OK through ALL_OK for python3 scripts/test-reseller-rest-api-client-e2e.py (including POST /api/companies/clients and POST /api/companies/clients/search).

To repeat the check locally (creates then deletes a client on the chosen company):

Optional environment variables: KEEPNET_TEST_COMPANY_RESOURCE_ID, KEEPNET_TEST_ROLE_RESOURCE_ID, KEEPNET_API_BASE_URL (default https://api.keepnetlabs.com). Credentials: KEEPNET_CLIENT_ID / KEEPNET_CLIENT_SECRET or CLIENT_ID / CLIENT_SECRET in the env file.


Common errors

  • 403 Forbidden — Credential is not Reseller, or X-KEEPNET-Company-Id is not a company you manage. Use a Reseller REST API client for your own calls. Roles and permissions →

  • 401 Unauthorized — Missing or invalid token. Request a new token via POST /connect/token.

  • 400 Bad Request — POST /api/companies/clients — Typical when the body still has empty name, clientId, or clientSecret (use values from GET /api/companies/clients/generate-client-credentials), when roleResourceIdList is missing (Role should be selected), when statusId is invalid (try 1 for Active), or when hasIpAddressRestriction is true but allowedIpAddresses contains empty or invalid entries (Invalid ip address). Response may include validationMessages with these hints.

  • 400 Bad Request — POST /api/companies/clients/search — Often Content-Type: application/json-patch+json (switch to application/json) or a filter object that includes null for condition / searchInputTextValue / nested fields; use the sample JSON above with "" and "AND" / "OR" strings, not null.

  • 500 / INTERNAL_SERVER_ERROR on POST /api/companies/clients/search — Often caused by "filter": null. Use the non-null minimal filter in POST /api/companies/clients/search above (Condition + SearchInputTextValue: "").

Related: Scope API requests to a customer → · Add system user for a customer → (same X-KEEPNET-Company-Id pattern)

Last updated