Event Ingestion Guide
This guide shows how to send analytics events to BreakGround via the POST /api/sdk/events endpoint. It covers the full integration lifecycle, event schema, and realistic payload examples for common event types.
Integration Lifecycle
The recommended order for SDK integration:
- Fetch config —
GET /api/sdk/configto get company settings and feature flags - Identify user —
POST /api/sdk/identifyto register or update the end user - Start session —
POST /api/sdk/sessionto create a tracking session - Send events —
POST /api/sdk/eventsto submit batched analytics events
Full TypeScript Example
const API_KEY = "wfx_your_api_key_here";
const BASE = "https://api.breakground.io/api/sdk";
const headers = {
"Content-Type": "application/json",
"X-API-Key": API_KEY,
};
// 1. Fetch company config
const config = await fetch(`${BASE}/config`, { headers }).then((r) => r.json());
console.log("Company:", config.data.name, "Plan:", config.data.plan);
// 2. Identify the user
const identity = await fetch(`${BASE}/identify`, {
method: "POST",
headers,
body: JSON.stringify({
userId: "user-42",
traits: { name: "Jane Doe", plan: "professional" },
}),
}).then((r) => r.json());
console.log("Internal ID:", identity.data.id);
// 3. Send events
const result = await fetch(`${BASE}/events`, {
method: "POST",
headers,
body: JSON.stringify({
events: [
{
type: "PAGE_VIEW",
userId: "user-42",
url: "https://app.example.com/dashboard",
timestamp: new Date().toISOString(),
properties: {
title: "Dashboard",
referrer: "https://app.example.com/login",
},
},
],
}),
}).then((r) => r.json());
console.log("Accepted:", result.data.accepted); // 1
Event Schema
Each event in the events array has the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Event type (see Event Types below) |
name | string | No | Human-readable name (e.g., "feature_activated" for CUSTOM events) |
properties | object | No | Arbitrary key-value data specific to this event |
timestamp | string | No | ISO 8601 timestamp. Defaults to server time if omitted |
userId | string | No | External user ID (as passed to POST /api/sdk/identify) |
url | string | No | Page URL where the event occurred |
metadata | object | No | Additional context (device info, SDK version, etc.) |
Request Format
{
"events": [
{ "type": "PAGE_VIEW", "userId": "user-42", "url": "..." },
{
"type": "CLICK",
"userId": "user-42",
"properties": { "selector": "#btn" }
}
]
}
Response Format (HTTP 202)
{
"data": {
"accepted": 2,
"correlationId": "req-abc123"
}
}
The correlationId can be used to trace events through the processing pipeline.
Event Types
Page and Interaction Events
| Type | Description |
|---|---|
PAGE_VIEW | User navigated to a page |
CLICK | User clicked an element |
ELEMENT_INTERACTION | User interacted with a specific UI element |
FORM_INTERACTION | User submitted or interacted with a form |
SCROLL_DEPTH | User scrolled to a specific depth on a page |
UX Quality Events
| Type | Description |
|---|---|
RAGE_CLICK | User clicked rapidly on the same element (frustration signal) |
DEAD_CLICK | User clicked an element that produced no response |
Flow Lifecycle Events
| Type | Description |
|---|---|
FLOW_STARTED | User entered a guided flow |
STEP_VIEWED | A flow step was displayed to the user |
STEP_COMPLETED | User advanced past a flow step |
FLOW_COMPLETED | User finished all steps in a flow |
FLOW_SKIPPED | User skipped a flow |
FLOW_DISMISSED | User closed/dismissed a flow before completing it |
Content Lifecycle Events
| Type | Description |
|---|---|
CONTENT_LOADED | Content was fetched from the API |
CONTENT_AUDIENCE_MATCHED | User matched the audience targeting rules |
CONTENT_RENDERED | Content widget was displayed to the user |
CONTENT_RENDER_FAILED | Content widget failed to render |
CONTENT_SUBMISSION_PERSISTED | A survey or NPS response was saved |
Journey Events
| Type | Description |
|---|---|
JOURNEY_STARTED | User began a multi-step journey |
JOURNEY_COMPLETED | User finished all journey steps |
Feedback Events
| Type | Description |
|---|---|
SURVEY_RESPONSE | User submitted a survey response |
NPS_RESPONSE | User submitted an NPS score |
Experiment Events
| Type | Description |
|---|---|
EXPERIMENT_CONVERSION | User completed a conversion goal in an experiment |
System Events
| Type | Description |
|---|---|
SDK_INIT | SDK was initialized |
SELECTOR_NOT_FOUND | A CSS selector referenced by content could not be found on the page |
ERROR | An error occurred in the SDK |
NETWORK | A network request was made or failed |
IDENTIFY | User identification event |
Custom Events
| Type | Description |
|---|---|
CUSTOM | Custom event defined by the integrator. Use the name field to specify the event name |
Payload Examples
Page View
curl -X POST https://api.breakground.io/api/sdk/events \
-H "Content-Type: application/json" \
-H "X-API-Key: wfx_your_api_key_here" \
-d @- << 'EOF'
{
"events": [
{
"type": "PAGE_VIEW",
"userId": "user-42",
"url": "https://app.example.com/dashboard",
"timestamp": "2026-03-18T14:30:00.000Z",
"properties": {
"title": "Main Dashboard",
"path": "/dashboard",
"referrer": "https://app.example.com/login"
}
}
]
}
EOF
Click Event
{
"events": [
{
"type": "CLICK",
"userId": "user-42",
"url": "https://app.example.com/dashboard",
"timestamp": "2026-03-18T14:30:12.456Z",
"properties": {
"tagName": "BUTTON",
"id": "create-report-btn",
"className": "btn btn-primary",
"text": "Create Report",
"x": 450,
"y": 320
}
}
]
}
Flow Lifecycle (Batched)
A typical flow generates multiple events. Send them together in a single batch:
{
"events": [
{
"type": "FLOW_STARTED",
"userId": "user-42",
"timestamp": "2026-03-18T14:31:00.000Z",
"properties": {
"flowId": "550e8400-e29b-41d4-a716-446655440000",
"flowName": "New User Onboarding"
}
},
{
"type": "STEP_VIEWED",
"userId": "user-42",
"timestamp": "2026-03-18T14:31:00.100Z",
"properties": {
"flowId": "550e8400-e29b-41d4-a716-446655440000",
"stepIndex": 0,
"stepType": "TOOLTIP"
}
},
{
"type": "STEP_COMPLETED",
"userId": "user-42",
"timestamp": "2026-03-18T14:31:15.000Z",
"properties": {
"flowId": "550e8400-e29b-41d4-a716-446655440000",
"stepIndex": 0
}
},
{
"type": "FLOW_COMPLETED",
"userId": "user-42",
"timestamp": "2026-03-18T14:31:45.000Z",
"properties": {
"flowId": "550e8400-e29b-41d4-a716-446655440000",
"flowName": "New User Onboarding",
"duration": 45000,
"stepsCompleted": 3,
"totalSteps": 3
}
}
]
}
Custom Event
Use CUSTOM type with a descriptive name field for application-specific events:
{
"events": [
{
"type": "CUSTOM",
"name": "feature_activated",
"userId": "user-42",
"timestamp": "2026-03-18T14:32:00.000Z",
"properties": {
"featureName": "advanced-search",
"plan": "professional",
"source": "onboarding-tour"
}
}
]
}
Survey Response
{
"events": [
{
"type": "SURVEY_RESPONSE",
"userId": "user-42",
"timestamp": "2026-03-18T14:33:00.000Z",
"properties": {
"surveyId": "770e8400-e29b-41d4-a716-446655440000",
"answers": {
"satisfaction": 4,
"feedback": "The onboarding was very helpful"
}
}
}
]
}
UX Quality (Rage Click)
{
"events": [
{
"type": "RAGE_CLICK",
"userId": "user-42",
"url": "https://app.example.com/settings",
"timestamp": "2026-03-18T14:34:00.000Z",
"properties": {
"selector": "#save-button",
"clickCount": 7,
"timeSpanMs": 2100
}
}
]
}
Error Event
{
"events": [
{
"type": "ERROR",
"userId": "user-42",
"url": "https://app.example.com/reports",
"timestamp": "2026-03-18T14:35:00.000Z",
"properties": {
"message": "Failed to load resource: net::ERR_CONNECTION_REFUSED",
"source": "sdk",
"stack": "TypeError: Failed to fetch\n at SDKApiClient.request (api.js:42)"
}
}
]
}
Error Handling
Validation Error (400)
Returned when the event payload fails schema validation:
{
"error": {
"message": "Validation failed",
"statusCode": 400,
"code": "VALIDATION_ERROR",
"category": "VALIDATION",
"details": [
{
"path": "events.0.type",
"message": "Invalid enum value. Expected 'PAGE_VIEW' | 'CLICK' | ... | 'JOURNEY_COMPLETED', received 'INVALID_TYPE'"
}
]
}
}
Authentication Error (401)
{
"error": {
"message": "Unauthorized: Invalid API key",
"statusCode": 401,
"code": "AUTH_FAILED",
"category": "AUTH"
}
}
Rate Limit Error (429)
{
"error": {
"message": "Rate limit exceeded",
"statusCode": 429,
"code": "RATE_LIMITED",
"category": "RATE_LIMIT"
}
}
Check the Retry-After response header for when to retry.
See Error Codes for the full error reference.
Best Practices
- Batch events — Send multiple events in a single request to reduce HTTP overhead and stay within the 600 req/min rate limit. The BreakGround SDK batches up to 50 events per request by default.
- Include
userId— Events without this field cannot be attributed to users in analytics. - Use ISO 8601 timestamps — The
timestampfield accepts ISO 8601 strings (e.g.,2026-03-18T14:30:00.000Z). If omitted, the server uses the current time. - Handle page unload — Use
navigator.sendBeacon()with the_apiKeybody field to flush remaining events when the page is closing, since custom headers are not supported by sendBeacon. - Retry with backoff — On
429or5xxerrors, retry with exponential backoff (e.g., 1s, 2s, 4s) up to a maximum of 60 seconds. Respect theRetry-Afterheader on429responses.