| [ Web Proxy ] |
| Viewing: https://docs.kitbase.dev/api-reference | [Back] [Original] |
REST API reference for the Kitbase SDK endpoints, plus the conventions authentication, errors, rate limits, versioning that every Kitbase API operation follows.
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.
| Format | URL |
|---|---|
| JSON | https://api.kitbase.dev/openapi.json (mirrored at https://kitbase.dev/openapi.json) |
| YAML | https://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.
curl -s https://api.kitbase.dev/openapi.json | jq '.info.version, (.paths | length)'https://api.kitbase.devSelf-hosted deployments serve the API under https://<your-host>/api.
| Credential | Header | Used 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>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.
Track an event in your application.
POST /sdk/v1/logs| Header | Required | Description |
|---|---|---|
x-sdk-key | Yes | Your Kitbase SDK key |
Content-Type | Yes | application/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
}
}| Field | Type | Required | Description |
|---|---|---|---|
event | string | Yes | Event name |
channel | string | Yes | Event channel/category |
user_id | string | No | Identified user ID |
anonymous_id | string | No | Anonymous user ID (UUID) |
icon | string | No | Emoji icon |
notify | boolean | No | Send real-time notification |
description | string | No | Event description |
timestamp | number | No | Unix timestamp in ms |
tags | object | No | Key-value metadata |
Status: 202 Accepted
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"event": "New Subscription",
"timestamp": "2024-01-15T10:30:00.000Z"
}| Status | Description |
|---|---|
400 | Invalid parameters |
401 | Missing or invalid SDK key |
503 | Queue full |
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/serverAuthentication: 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.
Send the original visitor's signals (not your server's connection):
{
"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
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
user_agent | string | Yes | Original request User-Agent |
ip_address | string | Yes | Original client IP |
method | string | No | HTTP method (used for Web Bot Auth signature base) |
host | string | No | Original host/authority |
path | string | No | Original request path |
referrer | string | No | Original Referer |
signature, signature_input, signature_agent | string | No | Web Bot Auth headers, if present enables cryptographic verification |
client_timestamp | number | No | Observation time (ms); defaults to receipt time |
event | string | No | Event name; defaults to server_request |
Status: 202 Accepted accepted counts how many were stored (bots); humans/disabled are silently skipped.
{ "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.)
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.
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/vercelVercel 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>.
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/cloudfrontFirehose 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.
Link an anonymous user to an identified user.
POST /sdk/v1/identify{
"anonymous_id": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "user-123",
"traits": {
"email": "user@example.com",
"plan": "premium"
}
}| Field | Type | Required | Description |
|---|---|---|---|
anonymous_id | string | Yes | Anonymous user ID to link |
user_id | string | Yes | Identified user ID |
traits | object | No | User properties |
Status: 200 OK
{
"success": true
}Events are automatically enriched with these tags:
| Tag | Description |
|---|---|
__browser | Browser name |
__browser_version | Browser version |
__os | Operating system |
__os_version | OS version |
__device | Device type |
__country | Country code |
__region | Region/State |
__city | City |
__session_id | Session ID |
__path | Page path |
__referrer | Referrer URL |
__utm_source | UTM source |
__utm_medium | UTM medium |
__utm_campaign | UTM campaign |
| Code | Description |
|---|---|
200 | Success |
202 | Accepted (queued) |
400 | Invalid parameters |
401 | Missing or invalid credential |
403 | Authenticated, but not permitted to do this |
404 | Not found (or not visible to this credential) |
409 | Conflict with existing state |
429 | Rate limited see Rate Limits |
5xx | Unexpected 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.
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:
{
"error": {
"code": "AUTH_003",
"message": "Authentication required",
"details": {}
},
"timestamp": "2026-08-22T10:45:52.855Z"
}| Field | Meaning |
|---|---|
error.code | Stable, machine-readable identifier. Branch on this, not on message. Families: AUTH_*, PERM_*, VAL_*, ORG_*, PROJECT_*, BILLING_*, WF_*, |
error.message | Human-readable explanation; may change without notice |
error.details | Typed extras when they exist field, value, resource, reason, retryAfterSeconds |
timestamp | When the error was produced (RFC 3339) |
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| Header | Meaning |
|---|---|
RateLimit-Policy | The policy: quota q requests per window w seconds |
RateLimit | Live state: r requests remaining, t seconds until the window resets |
X-RateLimit-Limit / -Remaining / -Reset | The 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.
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.
kitbase command.Released under the MIT License.
Copyright 2024-present Kitbase
| Web Proxy Viewer | New URL | Original Page |