Authentication

All API requests require authentication via the X-API-Key header. You can generate API keys from your Dashboard → Settings → API Keys page.

curl -H "X-API-Key: your-api-key" https://jcht.org/api/v1/events

Requests without a valid API key return a 401 Unauthorized response. Keys are scoped to a single project and can be revoked at any time.

Base URL

Base URL https://jcht.org/api/v1

All endpoints below are relative to this base URL. For self-hosted deployments, replace with your custom domain.

Endpoints

POST /api/v1/events

Track a new event. Events are processed in real-time and appear in your dashboard within seconds.

Request Body

FieldTypeRequiredDescription
eventstringYesEvent name (e.g., "button_click")
propertiesobjectNoArbitrary key-value event properties
userIdstringNoUser identifier for attribution
timestampstringNoISO 8601 timestamp (defaults to now)

Example Request

curl -X POST https://jcht.org/api/v1/events \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "button_click",
    "properties": {
      "buttonId": "signup",
      "page": "/pricing"
    },
    "userId": "user-123",
    "timestamp": "2026-02-22T10:30:00Z"
  }'

Example Response

{
  "status": "ok",
  "eventId": "evt_a8Kx92mNqP3f",
  "receivedAt": "2026-02-22T10:30:00.142Z"
}
GET /api/v1/events

Query tracked events with filters. Returns paginated results sorted by timestamp descending.

Query Parameters

ParamTypeRequiredDescription
fromstringNoStart date (ISO 8601). Defaults to 30 days ago.
tostringNoEnd date (ISO 8601). Defaults to now.
eventstringNoFilter by event name
limitnumberNoMax results to return (1–1000, default 100)

Example Request

curl "https://jcht.org/api/v1/events?event=button_click&from=2026-02-01&limit=10" \
  -H "X-API-Key: your-api-key"

Example Response

{
  "data": [
    {
      "eventId": "evt_a8Kx92mNqP3f",
      "event": "button_click",
      "properties": { "buttonId": "signup", "page": "/pricing" },
      "userId": "user-123",
      "timestamp": "2026-02-22T10:30:00Z",
      "source": "sdk-web"
    },
    {
      "eventId": "evt_b3Lm45nRsT7g",
      "event": "button_click",
      "properties": { "buttonId": "demo", "page": "/home" },
      "userId": "user-456",
      "timestamp": "2026-02-22T09:15:22Z",
      "source": "sdk-web"
    }
  ],
  "meta": {
    "total": 1284,
    "limit": 10,
    "hasMore": true
  }
}
GET /api/v1/sources

List all connected analytics sources for the current project.

Example Request

curl https://jcht.org/api/v1/sources \
  -H "X-API-Key: your-api-key"

Example Response

{
  "data": [
    {
      "id": "src_ga4_prod",
      "provider": "google_analytics",
      "name": "GA4 — Production",
      "status": "active",
      "lastSync": "2026-02-22T10:00:00Z",
      "eventsToday": 14832
    },
    {
      "id": "src_mixpanel_01",
      "provider": "mixpanel",
      "name": "Mixpanel Main",
      "status": "active",
      "lastSync": "2026-02-22T09:55:00Z",
      "eventsToday": 8491
    }
  ]
}
POST /api/v1/sources

Connect a new third-party analytics source to your project.

Request Body

FieldTypeRequiredDescription
providerstringYesProvider identifier (e.g., "google_analytics", "mixpanel", "amplitude")
namestringYesDisplay name for this source
credentialsobjectYesProvider-specific authentication credentials

Example Request

curl -X POST https://jcht.org/api/v1/sources \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "google_analytics",
    "name": "GA4 — Staging",
    "credentials": {
      "measurementId": "G-XXXXXXXXXX",
      "apiSecret": "xxxxxxxxxxxxxxxx"
    }
  }'

Example Response

{
  "id": "src_ga4_staging",
  "provider": "google_analytics",
  "name": "GA4 — Staging",
  "status": "provisioning",
  "createdAt": "2026-02-22T10:32:00Z"
}
GET /api/v1/dashboards

List all dashboards for the current project. Each dashboard contains widgets that visualize your aggregated analytics data.

Example Request

curl https://jcht.org/api/v1/dashboards \
  -H "X-API-Key: your-api-key"

Example Response

{
  "data": [
    {
      "id": "dash_overview",
      "name": "Product Overview",
      "widgets": 8,
      "createdAt": "2026-01-15T09:00:00Z",
      "updatedAt": "2026-02-21T14:30:00Z"
    },
    {
      "id": "dash_growth",
      "name": "Growth Metrics",
      "widgets": 5,
      "createdAt": "2026-02-01T11:00:00Z",
      "updatedAt": "2026-02-20T16:45:00Z"
    }
  ]
}
GET /api/v1/metrics

Query aggregated metrics over a time range. Supports multiple aggregation intervals for charting and reporting.

Query Parameters

ParamTypeRequiredDescription
metricstringYesMetric to query (e.g., "events_count", "unique_users", "sessions")
fromstringNoStart date (ISO 8601). Defaults to 7 days ago.
tostringNoEnd date (ISO 8601). Defaults to now.
intervalstringNoAggregation interval: "hour", "day", "week", "month". Default "day".

Example Request

curl "https://jcht.org/api/v1/metrics?metric=unique_users&from=2026-02-15&to=2026-02-22&interval=day" \
  -H "X-API-Key: your-api-key"

Example Response

{
  "metric": "unique_users",
  "interval": "day",
  "data": [
    { "date": "2026-02-15", "value": 1243 },
    { "date": "2026-02-16", "value": 1189 },
    { "date": "2026-02-17", "value": 1356 },
    { "date": "2026-02-18", "value": 1401 },
    { "date": "2026-02-19", "value": 1522 },
    { "date": "2026-02-20", "value": 1487 },
    { "date": "2026-02-21", "value": 1390 },
    { "date": "2026-02-22", "value": 982 }
  ]
}

Rate Limits

API requests are rate-limited per API key. Exceeding the limit returns a 429 Too Many Requests response with a Retry-After header.

Starter Plan

100 requests / minute

Pro Plan

1,000 requests / minute

Need higher limits? Contact us about our Enterprise plan with custom rate limits and dedicated infrastructure.

Error Codes

The API uses standard HTTP status codes. Error responses include a JSON body with error and message fields.

Status Code Description
400 bad_request Request body is malformed or missing required fields.
401 unauthorized API key is missing or invalid.
403 forbidden API key does not have permission for this resource.
404 not_found The requested resource does not exist.
429 rate_limited Too many requests. Retry after the duration in the Retry-After header.
500 internal_error An unexpected error occurred. Contact support if it persists.

Example error response:

{
  "error": "bad_request",
  "message": "Missing required field: event"
}