Skip to content

API Reference

Events

The Event resource is the core unit of the Cobalt analytics graph. Every interaction — page views, clicks, backend mutations, server-side jobs — is captured as a typed event tied to a Session and an Identity. This page documents the three endpoints that make up the event surface.

Overview

Events are immutable once accepted. Each event carries a type string (using reverse-DNS notation like checkout.completed or auth.session.refreshed), a Timestamp, the session_id it was emitted within, and an arbitrary properties object validated against the project’s schema registry.

Cobalt accepts events in two shapes: single-event POST /v1/events and batched POST /v1/events:batch (recommended for backend ingest, where you’ll commonly send 100–500 events per request). Both endpoints return the canonical Event object with the server-assigned id and any schema_warnings the validator surfaced.

Authentication

All requests require a project API key supplied as a bearer token in the Authorization header. Keys are scoped per project and per environment — a cblt_test_… key cannot read or write to a live project. Rotate keys from the project settings; rotated keys remain valid for a 24-hour grace window.

bash
curl https://api.cobalt.dev/v1/events \
  -H "Authorization: Bearer cblt_live_4f8a2e1c9d7b3f6a" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "checkout.completed",
    "session_id": "ses_01HZQ3X2K8M9N4P7R6T5V0W3Y1",
    "properties": {
      "order_id": "ord_92ab4f",
      "amount_cents": 4900,
      "currency": "USD"
    }
  }'

Endpoints

POST /v1/events

Create a single event. Events are accepted asynchronously — the response indicates the event was queued for ingest, not that it has been written to long-term storage. Use the id returned to look it up later via GET /v1/events/:id.

Name Type Required Description
type string Yes Reverse-DNS event type, max 128 chars.
session_id string Yes The Session this event belongs to. Must already exist or be auto-created via the SDK.
occurred_at Timestamp No RFC3339 timestamp. Defaults to server receive time.
properties object No Arbitrary JSON, validated against the project schema registry. Max 64KB.
idempotency_key string No Client-supplied dedupe key, stable for 24h.

Example request

import { Cobalt } from "@cobalt/sdk";

const cobalt = new Cobalt({ apiKey: process.env.COBALT_KEY! });

const event = await cobalt.events.create({
  type: "checkout.completed",
  session_id: "ses_01HZQ3X2K8M9N4P7R6T5V0W3Y1",
  occurred_at: new Date().toISOString(),
  properties: {
    order_id: "ord_92ab4f",
    amount_cents: 4900,
    currency: "USD",
  },
  idempotency_key: "ord_92ab4f-completed",
});

Example response

{
  "id": "evt_01HZQ3XAC6F4N9R2T8V5W7Y0K3",
  "type": "checkout.completed",
  "session_id": "ses_01HZQ3X2K8M9N4P7R6T5V0W3Y1",
  "identity_id": "idy_74kp2m",
  "occurred_at": "2026-04-30T08:14:22.117Z",
  "received_at": "2026-04-30T08:14:22.418Z",
  "properties": {
    "order_id": "ord_92ab4f",
    "amount_cents": 4900,
    "currency": "USD"
  },
  "schema_warnings": []
}

GET /v1/sessions/:id

Retrieve a single Session by id. The response includes the session’s full event timeline by default — pass ?include=summary to receive a summary projection instead, which is dramatically cheaper for sessions with thousands of events.

Name Type Required Description
id string Yes Path parameter — the session id, e.g. ses_01HZQ3… .
include "events" | "summary" No Defaults to events .
limit integer No Cap on events returned. Max 1000, default 200.
cursor string No Pagination cursor returned by the previous page.

Example response

{
  "id": "ses_01HZQ3X2K8M9N4P7R6T5V0W3Y1",
  "status": "active",
  "started_at": "2026-04-30T07:51:09.220Z",
  "last_seen_at": "2026-04-30T08:14:22.418Z",
  "identity_id": "idy_74kp2m",
  "event_count": 47,
  "events": [
    { "id": "evt_01HZQ3X2…", "type": "page.viewed", "occurred_at": "2026-04-30T07:51:09.220Z" },
    { "id": "evt_01HZQ3X9…", "type": "search.submitted", "occurred_at": "2026-04-30T07:52:34.001Z" }
  ],
  "next_cursor": "eyJ0IjoiMjAyNi0wNC0zMFQwOC..."
}

GET /v1/insights

List computed Insight objects for the project. Insights are projections of the event stream — funnels, retention curves, anomaly detections — refreshed on a schedule defined per insight. The response is paginated and ordered by computed_at descending.

Name Type Required Description
kind "funnel" | "retention" | "anomaly" No Filter by insight kind.
since Timestamp No Only return insights computed at or after this time.
status Session.Status No Restrict to insights derived from sessions in a given status.
limit integer No Page size. Max 100, default 25.

Example request

const insights = await cobalt.insights.list({
  kind: "funnel",
  since: "2026-04-01T00:00:00Z",
  limit: 50,
});

for (const insight of insights.data) {
  console.log(insight.id, insight.computed_at, insight.summary);
}

Errors

The Cobalt API uses conventional HTTP status codes and returns a structured error body for every non-2xx response. The body contains a stable code, a human-readable message, and — when applicable — a request_id you should include in support tickets.

400 Bad Request
The request body did not parse, or a required field was missing. The error code will be one of invalid_payload, missing_field, or schema_violation.
401 Unauthorized
The bearer token was missing, malformed, or has been revoked. Rotate the key from project settings if you suspect compromise.
404 Not Found
The resource id does not exist within the authenticated project. Cross-project lookups are never permitted — use the project's own key.
409 Conflict
An idempotency_key was reused with a different payload. Cobalt will not silently overwrite a stored event; resolve the conflict client-side.
429 Too Many Requests
The project’s per-second ingest budget has been exhausted. The response includes Retry-After and X-Cobalt-RateLimit-Reset headers — back off and retry.

Webhooks

Cobalt can deliver event-derived notifications to your own HTTPS endpoint. Configure subscriptions from the project settings, choose the event types you care about, and verify the Cobalt-Signature header on every incoming request using the shared signing secret.

Verifying a delivery

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyCobaltSignature(
  rawBody: string,
  header: string,
  secret: string,
): boolean {
  const [tsPart, sigPart] = header.split(",");
  const timestamp = tsPart?.split("=")[1] ?? "";
  const signature = sigPart?.split("=")[1] ?? "";

  const age = Math.abs(Date.now() / 1000 - Number(timestamp));
  if (!Number.isFinite(age) || age > 300) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && timingSafeEqual(a, b);
}