Appearance
Sites
Create, retrieve, update, and delete analytics sites. Each site represents a domain you want to track.
Base URL: https://app.krafter.dev/api/v1/analytics
Site Object
json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"domain": "example.com",
"name": "My Website",
"site_key": "sk_abc123def456",
"public_stats": false,
"retention_days": 30,
"created_at": "2025-01-15T10:00:00Z"
}| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (UUID). |
domain | string | The domain being tracked. |
name | string | Display name for the site. |
site_key | string | Public site key used in the tracker script. Starts with sk_. |
public_stats | boolean | Whether the stats dashboard is publicly accessible. |
retention_days | integer | How many days of events are kept. Events older than this are deleted nightly. |
created_at | string | ISO 8601 timestamp of when the site was created. |
List Sites
Retrieve all analytics sites for your team.
GET /analytics/sitesRequired scope: analytics:read
Example Request
bash
curl https://app.krafter.dev/api/v1/analytics/sites \
-H "Authorization: Bearer YOUR_API_TOKEN"Example Response
json
{
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"domain": "example.com",
"name": "My Website",
"site_key": "sk_abc123def456",
"public_stats": false,
"retention_days": 30,
"created_at": "2025-01-15T10:00:00Z"
},
{
"id": "660f9511-f39c-52e5-b827-557766551111",
"domain": "blog.example.com",
"name": "Company Blog",
"site_key": "sk_def456ghi789",
"public_stats": true,
"retention_days": 365,
"created_at": "2025-02-01T14:30:00Z"
}
]
}Create Site
Create a new analytics site. A site_key is auto-generated and returned in the response.
POST /analytics/sitesRequired scope: analytics:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
domain | string | Yes | Domain to track. Must be alphanumeric with dots (e.g., example.com). |
name | string | Yes | Display name for the site. |
retention_days | integer | No | Days of events to keep. Defaults to your plan's allowance; may not exceed it. |
Example Request
bash
curl -X POST https://app.krafter.dev/api/v1/analytics/sites \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"name": "My Website"
}'Example Response
json
// 201 Created
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"domain": "example.com",
"name": "My Website",
"site_key": "sk_abc123def456",
"public_stats": false,
"retention_days": 30,
"created_at": "2025-01-15T10:00:00Z"
}
}WARNING
The site_key is generated automatically and cannot be changed. Store it securely -- you will need it for the tracker script and API queries.
Validation Errors
json
// 422 Unprocessable Entity
{
"error": "Validation failed",
"details": {
"domain": ["must be a valid domain"]
}
}The body is flat — error is a string and details is its sibling, as described in Errors. A retention_days above your plan's allowance is rejected the same way, with "retention_days": ["must be less than or equal to 30"].
Get Site
Retrieve a single site by its site key.
GET /analytics/sites/:keyRequired scope: analytics:read
Example Request
bash
curl https://app.krafter.dev/api/v1/analytics/sites/sk_abc123def456 \
-H "Authorization: Bearer YOUR_API_TOKEN"Example Response
json
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"domain": "example.com",
"name": "My Website",
"site_key": "sk_abc123def456",
"public_stats": false,
"retention_days": 30,
"created_at": "2025-01-15T10:00:00Z"
}
}Update Site
Update a site's name, domain, or data retention.
PATCH /analytics/sites/:keyRequired scope: analytics:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | New display name. |
domain | string | No | New domain. Must be alphanumeric with dots. |
retention_days | integer | No | Days of events to keep, between 1 and your plan's allowance. |
Example Request
bash
curl -X PATCH https://app.krafter.dev/api/v1/analytics/sites/sk_abc123def456 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Main Website"
}'Example Response
json
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"domain": "example.com",
"name": "Main Website",
"site_key": "sk_abc123def456",
"public_stats": false,
"retention_days": 30,
"created_at": "2025-01-15T10:00:00Z"
}
}Delete Site
Delete a site and all associated analytics data. This action is irreversible.
DELETE /analytics/sites/:keyRequired scope: analytics:write
Example Request
bash
curl -X DELETE https://app.krafter.dev/api/v1/analytics/sites/sk_abc123def456 \
-H "Authorization: Bearer YOUR_API_TOKEN"Example Response
204 No ContentThe response body is empty. 204 means the site is gone and the deletion of its events is queued: the event store is separate from the site record, so the purge runs as a background job and is retried until the store confirms it.
WARNING
Deleting a site permanently removes all analytics data, including page views, events, goals, and statistics. This cannot be undone.
Retention
Each site has a retention_days setting that controls how long its events are kept. A nightly cleanup job deletes events older than that value, per site.
A new site starts at your plan's allowance, and that allowance is also the ceiling:
| Plan | Longest retention_days |
|---|---|
| Free | 30 |
| Starter | 180 |
| Pro | 365 |
| Scale | 730 |
| Enterprise | 730 |
A value above your plan's limit is refused with 422.
Retention safety net
Underneath the per-site rule, the ClickHouse analytics_events table carries a hard TTL of 2 years. It is the backstop for a cleanup job that stops running, not the mechanism: events are normally removed at your site's own retention_days, well before the engine touches them. Nothing can be kept beyond 730 days, which is why that is also the enterprise ceiling.
Scopes
| Scope | Endpoints |
|---|---|
analytics:read | GET /analytics/sites, GET /analytics/sites/:key |
analytics:write | POST /analytics/sites, PATCH /analytics/sites/:key, DELETE /analytics/sites/:key |