Skip to content

API Reference

REST API reference for the Kitbase SDK endpoints, plus the conventions — authentication, errors, rate limits, versioning — that every Kitbase API operation follows.

OpenAPI specification

The full public surface (the same operations the CLI and dashboard use) is published as an OpenAPI 3.0 document. Every operation has a unique operationId, a description, typed parameters, response schemas and typed error responses, so it can be loaded straight into an API client, a code generator or an LLM's function-calling tool set.

FormatURL
JSONhttps://api.kitbase.dev/openapi.json (mirrored at https://kitbase.dev/openapi.json)
YAMLhttps://api.kitbase.dev/openapi.yaml (mirrored at https://kitbase.dev/openapi.yaml)

Both are served anonymously with Cache-Control: public, max-age=3600, an ETag, and permissive CORS.

bash
curl -s https://api.kitbase.dev/openapi.json | jq '.info.version, (.paths | length)'

Base URL

https://api.kitbase.dev

Self-hosted deployments serve the API under https://<your-host>/api.

Authentication

CredentialHeaderUsed by
SDK key (pk_kitbase_…)x-sdk-key: pk_kitbase_…The /sdk/v1/* ingestion endpoints below (browser SDKs, tracking script)
Private API key (sk_kitbase_…)Authorization: Bearer sk_kitbase_… or X-API-Key: sk_kitbase_…Everything else: the CLI in CI, server-side ingestion (/ingest/v1/*), the MCP server, your own scripts
User session (JWT)Authorization: Bearer <jwt>The dashboard and the CLI's browser login

SDK endpoints use the x-sdk-key header:

x-sdk-key: <YOUR_SDK_KEY>

Permissions and scoping

Every operation is guarded by a named permission (for example analytics.view, webhook.create, aivisibility.manage). A user holds the permissions of their role in the organization — owner, admin, developer, analyst or support. A private API key is created by a user and holds that user's live permissions, limited to the key's project; it can never create or delete credentials (private_api_key.*, sdk_key.*). Revoking a role or a key takes effect on the next request. A permission failure is a 403 with error code PERM_001.

The MCP server is authorized with OAuth 2.1 (PKCE). At consent time the user picks the organization, the project and the exact subset of permissions the connection may use; each tool call is then checked against that subset intersected with the user's live permissions.


Events

Track Event

Track an event in your application.

POST /sdk/v1/logs

Headers

HeaderRequiredDescription
x-sdk-keyYesYour Kitbase SDK key
Content-TypeYesapplication/json

Request Body

json
{
  "event": "New Subscription",
  "channel": "payments",
  "user_id": "user-123",
  "anonymous_id": "550e8400-e29b-41d4-a716-446655440000",
  "icon": "💰",
  "notify": true,
  "description": "User subscribed to premium plan",
  "timestamp": 1705321800000,
  "tags": {
    "plan": "premium",
    "amount": 9.99
  }
}

Parameters

FieldTypeRequiredDescription
eventstringYesEvent name
channelstringYesEvent channel/category
user_idstringNoIdentified user ID
anonymous_idstringNoAnonymous user ID (UUID)
iconstringNoEmoji icon
notifybooleanNoSend real-time notification
descriptionstringNoEvent description
timestampnumberNoUnix timestamp in ms
tagsobjectNoKey-value metadata

Response

Status: 202 Accepted

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "event": "New Subscription",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Errors

StatusDescription
400Invalid parameters
401Missing or invalid SDK key
503Queue full

Server-Side Crawler Detection

Detect crawlers and AI bots that never run the JavaScript SDK (GPTBot, Googlebot, ClaudeBot, scrapers, …). Install lightweight middleware on your server or edge that forwards each request's signals; Kitbase classifies the actor server-side and stores bot/crawler traffic with full attribution.

POST /ingest/v1/server

Authentication: this is a server-to-server endpoint, so it uses your secret API key (sk_kitbase_…), sent as Authorization: Bearer <API_KEY> (or X-API-Key) — never the browser-exposed SDK key. The key is bound to a project, which is the target for everything you send. This is the generic source in the ingestion family at /ingest/v1/{source}; per-vendor log-drain endpoints (/ingest/v1/vercel, /ingest/v1/cloudfront, /ingest/v1/netlify, /ingest/v1/fastly, …) are added under the same namespace over time.

Why this exists: tag-based analytics only sees clients that execute JS, so non-JS crawlers are invisible. This endpoint observes them in your request path. Human requests are ignored here (the JS SDK already counts them, so this avoids double-counting); detected bot/crawler requests are stored whenever events are enabled for the project. Forwarded human traffic is classified in memory and discarded — not stored; for stored bot events, the raw IP is kept only when IP logging is enabled for the project.

Request Body

Send the original visitor's signals (not your server's connection):

json
{
  "events": [
    {
      "user_agent": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.1; +https://openai.com/gptbot",
      "ip_address": "203.0.113.5",
      "method": "GET",
      "host": "example.com",
      "path": "/pricing",
      "referrer": "https://www.google.com/",
      "signature": "...",
      "signature_input": "...",
      "signature_agent": "https://chatgpt.com",
      "client_timestamp": 1705321800000
    }
  ]
}

Parameters (per item)

FieldTypeRequiredDescription
user_agentstringYesOriginal request User-Agent
ip_addressstringYesOriginal client IP
methodstringNoHTTP method (used for Web Bot Auth signature base)
hoststringNoOriginal host/authority
pathstringNoOriginal request path
referrerstringNoOriginal Referer
signature, signature_input, signature_agentstringNoWeb Bot Auth headers, if present — enables cryptographic verification
client_timestampnumberNoObservation time (ms); defaults to receipt time
eventstringNoEvent name; defaults to server_request

Response

Status: 202 Acceptedaccepted counts how many were stored (bots); humans/disabled are silently skipped.

json
{ "accepted": 1, "total": 1 }

Stored rows carry the attribution fields documented in Server-Side Tag Enrichment (actor_type, bot_name, bot_vendor, actor_verified, verification_method, etc.) and are flagged is_bot so they're excluded from human visitor and billing counts. (Surfacing these as dedicated dashboard filter dimensions is incremental.)

Platform setup guides

Copy-paste setup for each platform lives under Bot & Crawler Detection — pick yours:

Batch requests (up to 500 per call) and send fire-and-forget so analytics never adds latency to your responses.


Vercel Log Drain

Hosted on Vercel? Point a Log Drain at Kitbase to capture every request — including crawlers that never run JS — with zero code.

POST /ingest/v1/vercel

Vercel POSTs batches of request logs (JSON array or NDJSON); Kitbase reads each log's proxy object (clientIp, userAgent, method, host, path, referer), classifies the actor, and stores bot/crawler requests (humans ignored). The drain authenticates with your project's secret key (as a custom Authorization: Bearer header), which resolves the target project.

200 OK{ "accepted": <stored bots>, "received": <request logs parsed> }. Non-request logs (build/function output) are ignored.

See the Vercel setup guide for step-by-step Log Drain configuration. The same per-vendor pattern extends to other hosts (CloudFront, Netlify, Fastly) under /ingest/v1/<vendor>.


AWS CloudFront Standard Logs

Served through AWS CloudFront (including AWS Amplify sites)? Stream CloudFront standard logs (v2) to Kitbase through Amazon Data Firehose to capture every request — including crawlers that never run JS — with zero code.

POST /ingest/v1/cloudfront

Firehose POSTs batched log records (base64 envelope, JSON output format); Kitbase decodes each CloudFront log's fields (c-ip, cs(User-Agent), cs-method, cs(Host), cs-uri-stem, cs(Referer)), classifies the actor, and stores bot/crawler requests (humans ignored). The stream authenticates with your project's secret key, which Firehose sends in the X-Amz-Firehose-Access-Key header and which resolves the target project.

200 OK — Firehose's required acknowledgement, { "requestId": "…", "timestamp": <ms> }. Rows without a usable IP + User-Agent are ignored; a non-2xx makes Firehose retry then back up to S3.

See the AWS CloudFront setup guide for the CloudFront + Firehose configuration.


Identify User

Link an anonymous user to an identified user.

POST /sdk/v1/identify

Request Body

json
{
  "anonymous_id": "550e8400-e29b-41d4-a716-446655440000",
  "user_id": "user-123",
  "traits": {
    "email": "user@example.com",
    "plan": "premium"
  }
}

Parameters

FieldTypeRequiredDescription
anonymous_idstringYesAnonymous user ID to link
user_idstringYesIdentified user ID
traitsobjectNoUser properties

Response

Status: 200 OK

json
{
  "success": true
}

Tag Enrichment

Events are automatically enriched with these tags:

TagDescription
__browserBrowser name
__browser_versionBrowser version
__osOperating system
__os_versionOS version
__deviceDevice type
__countryCountry code
__regionRegion/State
__cityCity
__session_idSession ID
__pathPage path
__referrerReferrer URL
__utm_sourceUTM source
__utm_mediumUTM medium
__utm_campaignUTM campaign

HTTP Status Codes

CodeDescription
200Success
202Accepted (queued)
400Invalid parameters
401Missing or invalid credential
403Authenticated, but not permitted to do this
404Not found (or not visible to this credential)
409Conflict with existing state
429Rate limited — see Rate Limits
5xxUnexpected server error

401 and 403 mean different things and are worth handling differently: 401 says the credential is absent, malformed or expired — presenting a valid one will work. 403 says the credential was accepted and still may not perform this action, so retrying with the same key never helps. A log drain that receives 401 should check that its key is actually being sent.

Error Response

Every non-2xx response — from any endpoint, including authentication failures raised before a request reaches a handler — has the same JSON body, the ErrorResponse schema in the OpenAPI spec:

json
{
  "error": {
    "code": "AUTH_003",
    "message": "Authentication required",
    "details": {}
  },
  "timestamp": "2026-08-22T10:45:52.855Z"
}
FieldMeaning
error.codeStable, machine-readable identifier. Branch on this, not on message. Families: AUTH_*, PERM_*, VAL_*, ORG_*, PROJECT_*, BILLING_*, WF_*, …
error.messageHuman-readable explanation; may change without notice
error.detailsTyped extras when they exist — field, value, resource, reason, retryAfterSeconds
timestampWhen the error was produced (RFC 3339)

Rate Limits

Authenticated requests are limited per credential (user, private API key or MCP connection) to 1,000 requests per minute in a fixed one-minute window. Every response carries the IETF rate-limit headers plus the legacy X-RateLimit-* trio, so a client can throttle itself before it is throttled:

RateLimit-Policy: "per-minute";q=1000;w=60
RateLimit: "per-minute";r=997;t=42
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 42
HeaderMeaning
RateLimit-PolicyThe policy: quota q requests per window w seconds
RateLimitLive state: r requests remaining, t seconds until the window resets
X-RateLimit-Limit / -Remaining / -ResetThe same three numbers, in the older de-facto format

Over the limit, the response is 429 with the standard error body (AUTH_014) and a Retry-After header in seconds. Public, unauthenticated endpoints (/public/*) have their own stricter per-IP limits and answer 429 + Retry-After the same way. Event ingestion (/sdk/v1/*, /ingest/v1/*) is not subject to the per-credential limit.

Versioning and Deprecation

The API is version 1 (info.version in the spec). Paths are stable and changes are additive: new fields, parameters and endpoints may appear at any time, and clients must ignore fields they do not recognise. Breaking changes ship under a new path prefix (/v2/…) rather than by changing an existing operation.

An operation scheduled for removal is marked deprecated: true in the OpenAPI spec at least 90 days before its sunset, and during that period every response from it carries:

Deprecation: @1767225600
Sunset: Wed, 01 Apr 2026 00:00:00 GMT
Link: <https://docs.kitbase.dev/changelog/>; rel="deprecation"; type="text/html"

Deprecation (RFC 9745) is the date the deprecation was announced, Sunset (RFC 8594) the date after which the operation may stop working, and the Link points at the migration notes. Changes are announced in the changelog.

Next steps

  • OpenAPI spec — the complete, machine-readable public surface.
  • MCP Server — the same data and actions for AI assistants, with a server card for discovery.
  • CLI — every endpoint here is also a kitbase command.
  • SDKs & Tools — typed SDKs that wrap this API.
  • Bot & Crawler Detection — platform guides for the server-side ingestion endpoint.