Skip to content
ABTO Guide

Integration guide

Event design

How to name and declare your events, and how they are kept apart from ABTO system events.

Events are the raw material of success metrics. How you name and declare them decides how easy it will be to build metrics and trust the numbers later.

User actionCall capture for a declared event SDK deliveryQueue → server acknowledgment Dashboard queryConfirm events → define and aggregate metrics

An event appears in Success metrics after tracking code runs and the server receives it.

The Browser SDK starts sending when 20 events are queued, or uses a default 5-second timer for a smaller batch. It also attempts delivery when the page becomes hidden or closes. This describes Browser SDK delivery, not a guarantee that the dashboard updates within 5 seconds. Network retries, server processing, and the query for the selected period must complete before the result appears. See each App SDK guide for its delivery schedule.

During setup, perform one test action and check its name under Success metrics → Tracked events. After confirming receipt, define a Success metric and inspect the results in Overview or Compare. Declaring an event name or installing the Skill alone does not send an event.

System Event
  • Name starts with $: $pageview, $ai_prompt_submitted
  • Defined by ABTO, emitted only by an explicit SDK helper or SDK helper
Custom Event
  • Named in your product's language without a $: checkout_completed, summary_copied
  • Defined by you, sent with capture

Before digging into design, it helps to trace the road one event travels from a user’s screen to a number on the dashboard. There are four stretches — name it, place it in the client, join it to AI calls by device_id, and meet it on the dashboard.

abto.events.ts in your repository is the starting point. A name that is missing here is dropped in production, so this stretch cannot be skipped.

export const events = defineEvents({
summary_copied: { description: 'Summary copied' },
});

Keep only the Event Key (ek-abto-…) in the client. A Calling Key placed here rides the bundle onto your users’ devices.

In the browser, pass the declared events to initialization and call it where the behavior happens.

export const abto = initAbto({
projectKey: 'ek-abto-...',
apiHost: 'https://api.abto.app',
environment: 'production',
events,
});
abto.capture('summary_copied');

Mobile follows the same grain. On Android:

abto.capture("summary_copied")

Either way the SDK buffers the event on the device before sending, so the call returns immediately instead of waiting on the network.

This is the seam of the whole flow. A client SDK mints an anonymous device_id the moment it is installed and keeps it on the device, and that value is the only axis joining product behavior to AI calls.

So your server must carry the same value when it calls the gateway. Read it with abto.getIdentity().deviceId in the browser or abto.deviceId on mobile, pass it to your backend, and put it in the Server SDK’s context.

await abto.withContext(
{ deviceId, featureId: 'review.summary' },
async () => { /* the model call */ },
);

That deviceId goes out as the x-abto-device-id header. Put an arbitrary value such as a login id there and it will differ from the device_id the client sent, so behavior and calls never meet.

To narrow down to reactions to a single response, send the x-abto-request-id the gateway returns back down to the browser. Renders, copies, and feedback for that response bind by the same request_id.

The two records arrive by different roads. The gateway records a call’s cost, latency, and tokens as it forwards the call, while the client SDK uploads the event you just sent. The server meshes the two by device_id and stands them on one screen.

Confirm arrival in the event table at the bottom of Success metrics. Events sent by a client appear there automatically, so a name in the table means it was received. If it is missing, the event has either never fired or was dropped for disagreeing with the declaration; the FAQ walks through the causes.

Once the name is in the table it becomes material for a metric. Ratio metrics count people rather than calls, so “what percentage of users who got this option copied the summary” splits cleanly per device.

Custom Event names are declared in abto.events.ts in your repository.

export const events = defineEvents({
checkout_completed: { description: 'Checkout completed' },
});

The reason declarations live in code rather than in a dashboard form is that changes to the event contract then travel through code review and deployment together.

Besides its name, an event carries only two values you set: the number value and its unit label scale. Use value for the number to sum or average on the dashboard. An event sent with its name alone counts as a conversion.

Development is lenient, production is strict

Section titled “Development is lenient, production is strict”
SituationDevelopmentProduction
Unregistered eventSent, with a discovery warningDropped

During development, unregistered events are still sent with a warning so nothing blocks your work. In production, events missing from the declaration are dropped so the data stays clean.

Ordinary behavior such as a purchase or a copy joins AI usage through device_id, so there is nothing extra to do. To narrow it to “what the person who saw this response did next”, take the x-abto-request-id from the gateway response on your server and pass it down to the browser. Render, copy, and feedback interactions on that response then share the same request_id.

The SDK guards against loss and duplication

Section titled “The SDK guards against loss and duplication”

The SDK keeps events on the device until delivery and removes them only after the server confirms receipt. Transient network errors are retried automatically and the server filters out duplicates, so even on an unstable connection events are neither lost nor counted twice.

Sessions need no instrumentation of their own. Session boundaries are derived automatically from each event’s session_id and timestamp.