API Reference
All endpoints use https://api.kubbi.ai as the base URL. Requests and responses use JSON. All /api/v1/* responses include the header API-Version: v1.
Health
/healthLiveness and readiness check. Verifies application and database connectivity.
Auth: None
{
"status": "ok",
"database": "connected",
"timestamp": "2026-03-28T12:00:00.000Z"
}{
"status": "degraded",
"database": "unreachable",
"timestamp": "2026-03-28T12:00:00.000Z"
}Registration & Login
/auth/registerRegister a new user with email and password. Returns a JWT.
Auth: None
{
"email": "user@example.com",
"password": "your_password",
"name": "Jane Doe"
}{
"token": "eyJhbG...",
"user": {
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"email": "user@example.com",
"name": "Jane Doe",
"created_at": "2026-03-28T12:00:00.000Z"
}
}/auth/loginEmail/password login. Returns a JWT.
Auth: None
{
"email": "user@example.com",
"password": "your_password"
}Response has the same shape as register (token + user).
API Key Management
/auth/api-keysCreate an API key for the authenticated user. Requires a label. Max active keys depends on your plan tier.
Auth: JWT (Bearer token)
{ "label": "my-key" }{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"api_key": "kb_live_abc123...",
"key_prefix": "kb_abc",
"label": "my-key",
"created_at": "2026-03-28T12:00:00.000Z",
"note": "Store this API key securely — it will not be shown again."
}/auth/api-keysList the authenticated user's API keys with pagination.
Auth: JWT (Bearer token)
{
"keys": [
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"key_prefix": "kb_abc",
"label": "my-key",
"created_at": "2026-03-28T12:00:00.000Z",
"revoked_at": null
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 1,
"total_pages": 1
}
}kubbi Operations
All producer routes are under /api/v1/kubbis. Auth: API key as Bearer token. Rate limited per plan tier.
/api/v1/kubbisCreate a kubbi. The request must contain exactly one of content (single-content) or files (multi-file package), never both.
Auth: API key (Bearer token)
Single-Content Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| content | any JSON value | Yes | The data to encrypt (max size depends on your plan) |
| content_type | string | Yes | text/plain, application/json, text/markdown, or text/csv |
| ttl_seconds | integer | Yes | Time-to-live in seconds (60 – max depends on your plan) |
| max_retrievals | integer | No | ≥ 1 if set; max depends on your plan. Omit or null for unlimited |
| metadata | object | No | Arbitrary JSON (≤ 1 KB serialized) |
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"claim_url": "https://api.kubbi.ai/r/abc123xyz",
"claim_token": "abc123xyz",
"status": "active",
"content_type": "application/json",
"max_retrievals": 1,
"metadata": { "source": "my-app" },
"created_at": "2026-03-28T12:00:00.000Z",
"expires_at": "2026-03-28T13:00:00.000Z"
}Multi-File (Package) Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| files | array | Yes | File objects (max count and total size depend on your plan) |
| ttl_seconds | integer | Yes | 60 – max depends on your plan |
| max_retrievals | integer | No | ≥ 1 if set |
| metadata | object | No | ≤ 1 KB serialized |
Each file object
| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | ≤ 255 chars, alphanumeric start, unique (case-insensitive) |
| content | string | Yes | File content; valid base64 if encoding is "base64" |
| content_type | string | Yes | Valid MIME format (type/subtype) |
| role | string | No | instructions, data, context, config, or attachment |
| encoding | string | No | Only valid value: "base64" (for binary files) |
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"claim_url": "https://api.kubbi.ai/r/pkg456xyz",
"claim_token": "pkg456xyz",
"status": "active",
"file_count": 3,
"total_size_bytes": 1234,
"max_retrievals": 2,
"metadata": null,
"created_at": "2026-03-28T12:00:00.000Z",
"expires_at": "2026-03-28T13:00:00.000Z"
}/api/v1/kubbisList kubbis owned by the authenticated API key. Paginated with page and limit query params (default 20, max 100).
Auth: API key (Bearer token)
{
"kubbis": [
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"status": "active",
"content_type": "application/json",
"max_retrievals": 1,
"retrieval_count": 0,
"first_retrieved_at": null,
"last_retrieved_at": null,
"metadata": null,
"created_at": "...",
"expires_at": "..."
}
],
"pagination": { "page": 1, "limit": 20, "total": 5, "total_pages": 1 }
}Single-content items include content_type. Package items include file_count and total_size_bytes instead.
/api/v1/kubbis/:idFetch detail for a kubbi owned by the caller. Same shape as a list item. Does not return the payload.
Auth: API key (Bearer token)
/api/v1/kubbis/:idDelete a kubbi. The encrypted payload is wiped immediately. Returns 410 Gone on subsequent claim attempts.
Auth: API key (Bearer token)
{ "status": "deleted" }Returns 409 if the kubbi is already inactive (expired, burned, or deleted).
Consumer Routes
All consumer routes use the claim_token from the claim URL. No API key required — the claim URL is the credential.
/r/:claim_tokenPreview a kubbi. Returns metadata plus a claim object with the URL and method to claim. Does not count as a retrieval.
Auth: None (claim URL is the credential)
{
"status": "active",
"content_type": "text/plain",
"max_retrievals": 1,
"retrieval_count": 0,
"metadata": null,
"created_at": "...",
"expires_at": "...",
"claim": {
"url": "https://api.kubbi.ai/r/abc123xyz/claim",
"method": "POST",
"description": "POST to this URL to retrieve the content. This counts as a retrieval."
}
}{
"type": "kubbi_package",
"status": "active",
"kubbi_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"file_count": 3,
"total_size_bytes": 1234,
"max_retrievals": 2,
"retrieval_count": 0,
"remaining_reads": 2,
"metadata": null,
"files": [
{ "name": "config.json", "content_type": "application/json", "size_bytes": 128, "role": "config" }
],
"created_at": "...",
"expires_at": "...",
"claim": {
"url": "https://api.kubbi.ai/r/pkg456xyz/claim",
"method": "POST",
"message": "POST to this URL to retrieve all file contents."
}
}/r/:claim_token/inspectInspect a kubbi. Same as the preview endpoint above, but without the claim object. Does not count as a retrieval.
Auth: None (claim URL is the credential)
/r/:claim_token/claimClaim a kubbi. Returns the decrypted content. No request body needed (empty POST). Counts as a retrieval. If this is the last allowed retrieval, the payload is permanently wiped.
Auth: None (claim URL is the credential)
{
"content": "sensitive data here",
"content_type": "text/plain",
"metadata": null,
"created_at": "...",
"expires_at": "..."
}For application/json, content is parsed JSON (an object or array). For text/plain, text/markdown, and text/csv, it is a string.
{
"type": "kubbi_package",
"file_count": 3,
"metadata": null,
"files": [
{
"name": "config.json",
"content_type": "application/json",
"size_bytes": 128,
"role": "config",
"content": "{\"env\":\"prod\"}"
},
{
"name": "image.png",
"content_type": "image/png",
"size_bytes": 4096,
"role": "attachment",
"content": "iVBORw0KGgo...",
"encoding": "base64"
}
],
"created_at": "...",
"expires_at": "..."
}Text files (text/* and application/json) have content as a UTF-8 string with no encoding field. Binary files have content as base64 with "encoding": "base64".
Error Responses
All errors follow a consistent shape:
{
"error": "<error_code>",
"message": "<human-readable message>",
"messages": ["<detail>"]
}The messages array is only present on validation_error (400) responses.
| Status | Code | When |
|---|---|---|
| 400 | validation_error | Invalid request body (details in messages[]) |
| 401 | unauthorized | Missing or invalid API key / JWT |
| 404 | not_found | Resource not found or not owned by caller |
| 409 | conflict | Resource state conflict (already deleted, duplicate email) |
| 410 | gone | kubbi expired, burned, or deleted |
| 429 | rate_limited | Too many requests — rate limit depends on your plan tier |
| 429 | quota_exceeded | Daily quota, active kubbi limit, or max API keys reached |
| 500 | internal_error | Unexpected server error |
| 501 | not_implemented | API version not available |
Rate limiting
All API key-authenticated endpoints are rate-limited per API key. The exact limit depends on your plan tier. If you exceed it, you will receive a 429 Too Many Requests response with error code rate_limited. Wait and retry.