Quickstart

Go from zero to your first authenticated Kabeen Public API call in about five minutes

Go from zero to your first authenticated call against the Kabeen Public API in about five minutes. Swap in your own API key and host and every example below is copy-pasteable as-is.

1. Prerequisites

You need:

  • A Kabeen workspace.
  • An API key, created by a workspace admin from inside the Kabeen app. Keys are workspace-scoped (never passed in the path or body) and carry a set of permission scopes (e.g. applications:read, applications:add). See Authentication for how keys are issued, rotated, and scoped.

Every request is authenticated with either header — pick one and stick with it:

Authorization: Bearer kbn_live_...

or

X-Api-Key: kbn_live_...

Export your key and host as environment variables so every snippet below just works:

export KABEEN_API_KEY="kbn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export KABEEN_HOST="app.kabeen.io"

All requests are made against:

https://{KABEEN_HOST}/public/v1

2. Verify your key

Before anything else, confirm the key works and see exactly what it's allowed to do. GET /me requires no specific scope — any valid key can call it.

curl -s "https://${KABEEN_HOST}/public/v1/me" \
  -H "Authorization: Bearer ${KABEEN_API_KEY}"

Response:

{
  "workspace": {
    "id": "8f0a2b1e-2b8b-4e2a-9c3e-1a2b3c4d5e6f",
    "name": "Acme Corp"
  },
  "key": {
    "id": "3c9d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
    "name": "CMDB sync",
    "alias": "read"
  },
  "permissions": [
    "applications:read",
    "contracts:read",
    "infrastructure:read"
  ]
}

Two things worth checking before you go further:

  • workspace is the workspace your key is bound to — there is no way to address a different one with the same key.
  • permissions is the exact list of scopes this key grants. If the call you want isn't covered by this list, you'll get a 403 and need a new key with a broader scope.

3. List a resource

List endpoints share the same shape across the whole API. Let's list applications (requires scope applications:read):

curl -s "https://${KABEEN_HOST}/public/v1/applications?limit=10" \
  -H "Authorization: Bearer ${KABEEN_API_KEY}"
{
  "data": [
    {
      "id": "1b2c3d4e-5f60-4a1b-8c2d-3e4f5a6b7c8d",
      "name": "Salesforce",
      "description": "CRM platform",
      "logo": "https://cdn.kabeen.io/logos/salesforce.png",
      "state": "active",
      "criticality": "high",
      "hostingType": "saas",
      "category": {
        "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
        "name": "CRM"
      }
    }
  ],
  "pagination": {
    "limit": 10,
    "offset": 0,
    "total": 42
  }
}

The envelope is always { "data": [...], "pagination": {...} }:

  • data — an array of slim, list-friendly items. Fetch a single resource for the rich version — see step 4.
  • paginationlimit (what you asked for, clamped to [1, 200]), offset (what you asked for, floored at 0), and total (how many items match across all pages, not just this one). Increment offset by limit to walk the rest of the list. Full details in Pagination and errors.

GET /applications also accepts search, categoryId, criticality, hostingType, tag, teamId, sort, and direction as query filters — see the Endpoint reference.

4. Fetch one resource

List items are intentionally slim. Fetch a single application by id to get the full detail record — category, vendor, tags, owners, lifecycle, support/authentication info, and custom fields (requires scope applications:read):

curl -s "https://${KABEEN_HOST}/public/v1/applications/1b2c3d4e-5f60-4a1b-8c2d-3e4f5a6b7c8d" \
  -H "Authorization: Bearer ${KABEEN_API_KEY}"

Response (trimmed):

{
  "id": "1b2c3d4e-5f60-4a1b-8c2d-3e4f5a6b7c8d",
  "name": "Salesforce",
  "description": "CRM platform",
  "logo": "https://cdn.kabeen.io/logos/salesforce.png",
  "state": "active",
  "criticality": "high",
  "hostingType": "saas",
  "accessUrl": "https://acme.salesforce.com",
  "usageActivated": true,
  "desktopApplicationNames": [],
  "support": { "phone": null, "email": "support@salesforce.com", "url": null },
  "authentication": { "type": "login_password" },
  "category": { "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d", "name": "CRM" },
  "vendor": { "id": "5e4f3a2b-1c0d-9e8f-7a6b-5c4d3e2f1a0b", "name": "Salesforce Inc." },
  "tags": [{ "id": "tag-1", "name": "critical" }],
  "owners": [],
  "lifecycle": null,
  "customFields": [],
  "updatedAt": "2026-07-15T10:22:00Z"
}

5. Create and update

Writes need the specific scope for the action, not just applications:read.

Create an application — requires scope applications:add. Only name is required:

curl -s -X POST "https://${KABEEN_HOST}/public/v1/applications" \
  -H "Authorization: Bearer ${KABEEN_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Zoom"
  }'

The response is a slim projection of the created application:

{
  "id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
  "name": "Zoom",
  "description": null,
  "criticality": null,
  "logo": "https://cdn.kabeen.io/logos/default.png"
}

Update it — requires scope applications:edit. PATCH only changes the fields you send; a JSON null is treated as "not provided", so you can't clear a field this way:

curl -s -X PATCH "https://${KABEEN_HOST}/public/v1/applications/2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer ${KABEEN_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "criticality": "high",
    "accessUrl": "https://zoom.us"
  }'

The response this time is the full application detail (same shape as step 4), reflecting your change.

6. Handle errors and pagination

  • Every 4xx/5xx response is the same JSON shape: { "code", "message", "status" }. Check code for programmatic handling — it's stable across releases even if message wording changes. The one exception is 401, which comes back with an empty body.
  • The two errors you'll hit constantly while integrating: 401 (bad or missing key) and 403 (key is valid but missing the scope the endpoint requires — re-check permissions from step 2).
  • Pagination is offset-based everywhere except the audit log, which is cursor-based (cursor / nextCursor) because it's append-only and high-volume.

Full reference: Pagination and errors.

7. The same call in three languages

Here's GET /applications?limit=10 from step 3, once in each language.

curl

curl -s "https://${KABEEN_HOST}/public/v1/applications?limit=10" \
  -H "Authorization: Bearer ${KABEEN_API_KEY}"

JavaScript (Node 18+, global fetch)

const host = process.env.KABEEN_HOST ?? "app.kabeen.io";
const apiKey = process.env.KABEEN_API_KEY;
 
const response = await fetch(`https://${host}/public/v1/applications?limit=10`, {
  headers: {
    Authorization: `Bearer ${apiKey}`,
  },
});
 
if (!response.ok) {
  const problem = await response.json();
  throw new Error(`${problem.code}: ${problem.message}`);
}
 
const { data, pagination } = await response.json();
console.log(`Got ${data.length} of ${pagination.total} applications`);

Python (requests)

import os
import requests
 
host = os.environ.get("KABEEN_HOST", "app.kabeen.io")
api_key = os.environ["KABEEN_API_KEY"]
 
response = requests.get(
    f"https://{host}/public/v1/applications",
    headers={"Authorization": f"Bearer {api_key}"},
    params={"limit": 10},
)
response.raise_for_status()
 
body = response.json()
print(f"Got {len(body['data'])} of {body['pagination']['total']} applications")

Next steps