API Reference
HTTP contracts for event ingestion, customer traits, administration, and service health.
Kite exposes a versioned ingestion API for product backends and a separate administrative API used by the CLI.
| Surface | Authentication | Intended client |
|---|---|---|
/v1/events, /v1/events/batch | Project environment API key | Backend services and SDKs |
/projects/* | CLI bearer session and organization header | Kite CLI |
| Health probes | None | Infrastructure |
/metrics | Internal bearer token | Kite operators |
For Node.js applications, prefer the Node.js SDK. It implements retries, idempotency, timeouts, and typed errors around the ingestion endpoints documented here.
Base URL
Kite Cloud:
https://api.usekite.cloudAll public ingestion paths are versioned. Use /v1/events, not /events; the unversioned path
does not exist.
Authentication
Send a project API key as a bearer token:
Authorization: Bearer kt_test_...
Content-Type: application/jsonThe key selects both the project and environment:
| Prefix | Environment |
|---|---|
kt_test_ | test |
kt_live_ | live |
There is no environment field in an ingestion request. To change environments, use the corresponding key.
Keep keys on the server
Never send project API keys to a browser or mobile client. Route product events through a trusted backend and store keys in your deployment platform's secret manager.
Track an event
POST /v1/events
Records one product event. This endpoint accepts only track operations; customer traits must be sent through the batch endpoint.
curl -X POST https://api.usekite.cloud/v1/events \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customerId": "customer_123",
"event": "feature.used",
"properties": { "feature": "reports" },
"idempotencyKey": "usage_456"
}'Request body
| Field | Type | Required | Description |
|---|---|---|---|
customerId | string | yes | Stable customer ID from your system, up to 256 characters. |
event | string | yes | Event name, up to 256 characters. |
properties | JSON object | no | Event properties, up to 64 KiB when serialized. Defaults to {}. |
timestamp | ISO 8601 string | no | Event time with timezone. Defaults to ingestion time. |
idempotencyKey | string | no | Non-empty deduplication key, up to 256 characters. |
type | "track" | no | Optional discriminator. Other values are rejected. |
Valid timestamps include 2026-07-20T14:30:00Z and 2026-07-20T11:30:00-03:00. A timestamp
without a timezone is rejected.
Response
Kite returns HTTP 202 Accepted after accepting the event:
{
"eventId": "evt_01K...",
"accepted": true,
"queued": true,
"duplicate": false
}queued: true means durable ingestion accepted the event; downstream effects such as signals
and webhook deliveries may continue asynchronously.
Identify a customer
Identify operations update traits used by lifecycle, rules, segments, and health. They are sent
inside POST /v1/events/batch:
curl -X POST https://api.usekite.cloud/v1/events/batch \
-H "Authorization: Bearer $KITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"type": "identify",
"customerId": "customer_123",
"traits": { "plan": "growth", "seats": 12 }
}
]
}'| Field | Type | Required | Description |
|---|---|---|---|
type | "identify" | yes | Selects a trait update. |
customerId | string | yes | Stable customer ID, up to 256 characters. |
traits | JSON object | yes | Traits, up to 64 KiB when serialized. |
Only customerId and traits are processed for an identify operation. Do not include track
fields such as event, properties, or idempotencyKey.
Batch operations
POST /v1/events/batch
Sends between 1 and 1,000 track or identify operations in one ordered request.
{
"events": [
{
"type": "identify",
"customerId": "customer_123",
"traits": { "plan": "growth" }
},
{
"type": "track",
"customerId": "customer_123",
"event": "workspace.invited",
"properties": { "role": "admin" },
"idempotencyKey": "invite_789"
}
]
}For track operations, type can be omitted and defaults to track. Operations are evaluated
in array order, so an identify can affect a track operation that follows it.
Successful batches return HTTP 202:
{
"accepted": 2,
"rejected": 0,
"eventIds": ["evt_01K..."]
}eventIds contains IDs for track operations. Identify operations update state but do not
create product-event IDs.
Atomic validation
Kite validates the complete batch before ingesting it. An invalid operation or idempotency conflict rejects the request without partially accepting earlier operations.
Schema validation
The active configuration defines event properties and customer traits. Kite validates:
- declared event names;
- required and unknown properties;
string,number,boolean,date, andarraytypes;- enum values;
- identify traits.
With strictSchema: true, violations return HTTP 422. The error includes the first failure
and a diagnostics array with all detected paths:
{
"error": {
"code": "INVALID_PROPERTY_ENUM",
"message": "Property \"plan\" must be one of: free, pro.",
"field": "events[0].properties.plan",
"requestId": "req_...",
"diagnostics": [
{
"code": "INVALID_STRUCTURE",
"message": "Property \"plan\" must be one of: free, pro.",
"path": "events[0].properties.plan"
}
]
}
}With strictSchema: false, Kite accepts the operation and logs the schema diagnostics. See
the Config Reference for schema definitions.
Idempotency
Set idempotencyKey on track operations that may be retried.
- Repeating the same key with the same canonical payload returns the original
eventIdandduplicate: truefor a single event. - Reusing a key with a different customer, event, properties, or explicit timestamp returns
HTTP
409 IDEMPOTENCY_CONFLICT. - Keys are isolated by project and environment.
- Within a batch, conflicting uses reject the entire batch atomically.
Property key order does not change the canonical payload, so semantically identical JSON objects remain duplicates.
Errors
Errors use a consistent envelope:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "...",
"requestId": "req_...",
"field": "customerId",
"value": null,
"reason": "...",
"fix": "...",
"docs": "https://docs.usekite.cloud/api#VALIDATION_ERROR"
}
}field and diagnostics appear only when relevant. Include requestId when contacting
support.
| Status | Typical code | Meaning |
|---|---|---|
400 | VALIDATION_ERROR, MALFORMED_JSON, BATCH_SIZE_INVALID | Invalid request shape, field, timestamp, or batch size. |
401 | INVALID_API_KEY | Missing, malformed, unknown, or revoked key. |
402 | PLAN_REQUIRED | The organization cannot use cloud execution. |
409 | IDEMPOTENCY_CONFLICT | An idempotency key was reused with another payload. |
422 | Schema diagnostic code | Payload violates a strict active schema. |
429 | RATE_LIMIT_EXCEEDED, MONTHLY_QUOTA_EXCEEDED | Per-key rate limit or monthly quota exceeded. |
5xx | Server error | Temporary service failure. Retry with backoff. |
Retry network errors, 429, and 5xx. Do not retry validation, authentication, or
idempotency conflicts without correcting the request.
Rate limits
Rate limiting is applied per API key and each operation in a batch counts as one event. The service returns:
| Header | Meaning |
|---|---|
RateLimit-Limit | Current event allowance for the window. |
RateLimit-Remaining | Remaining operations. |
Retry-After | Seconds to wait after HTTP 429. |
RateLimit-Reset | Seconds until capacity is available after HTTP 429. |
Limits can vary by deployment and plan; clients should rely on response headers rather than a hard-coded value.
Request IDs
Every response includes X-Request-ID. You may provide your own value:
X-Request-ID: checkout_456_eventUse a non-sensitive correlation ID. Kite returns it in the response header and includes it in structured error bodies.
Administrative API
The /projects/* surface powers the CLI for project creation, API-key management,
configuration versions, customer inspection, events, recomputation, diagnostics, webhooks,
and AI assistance.
Administrative requests use both headers:
Authorization: Bearer <cli-session>
X-Kite-Organization: org_...These are user and organization credentials, not project ingestion keys. Roles and billing
entitlements are enforced per operation. Prefer kite auth login and CLI commands instead of
building directly against this surface while its public contract evolves.
Service health
| Endpoint | Authentication | Meaning |
|---|---|---|
GET /livez | none | Process is alive. |
GET /startupz | none | Startup completed; otherwise returns 503. |
GET /readyz | none | Ready for traffic, including database and worker checks. |
GET /health | none | Compatibility alias for readiness. |
GET /metrics | internal token | Detailed service and queue metrics for operators. |
Successful probes return:
{ "status": "ok" }Webhook delivery
Webhook delivery is at least once. A worker crash after the receiver accepts a request but before Kite records completion can cause the same event to be sent again.
Deduplicate the stable identifier in the kite-event-id header. The same value is available
as id in the JSON payload and remains unchanged across retries. A webhook receives only
events emitted after that webhook was created.
See Webhooks and signatures for payloads, signatures, retry timing, and verification. See Backfill and recomputation for delayed data and state replay semantics.