> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bily.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Event and data model

> Create a stable, reviewable contract for every Bily event and property.

You control two parts of every Bily browser event: a stable name and an optional payload. The JavaScript SDK prepares both for delivery and adds a small amount of browser context.

## Give every event a clear shape

```typescript theme={null}
track("checkout_completed", {
  event_id: "checkout_01J2M8YQ7B",
  ts: "2026-07-13T08:30:00.000Z",
  checkout_id: "checkout_123",
  plan: "growth",
});
```

| Part              | Purpose                                                 |
| ----------------- | ------------------------------------------------------- |
| Event name        | Stable description of the outcome                       |
| Typed fields      | `client`, `products`, `order`, page, and search context |
| Custom properties | Application-specific IDs, categories, and safe state    |
| `event_id`        | Identifier for one logical outcome                      |
| `ts`              | ISO timestamp for the event                             |

When `event_id` or `ts` is missing, the SDK creates it. A generated event ID helps with correlation. `track()` does not acknowledge ingestion or promise exactly-once delivery.

## Name outcomes consistently

Use Bily's current title-case names for ecommerce events. Use stable snake\_case names for application events.

Good application event names:

* `workspace_created`
* `integration_connected`
* `billing_checkout_completed`
* `product_error_encountered`

Keep customer IDs, timestamps, translated labels, and UI positions out of event names. Put changing context in properties.

Event names are case-sensitive. The SDK accepts documented compatibility aliases. New code should use the current names in the [event reference](/api-reference/events).

## Track an outcome only when it is true

Send an event only after the state it names is true.

```typescript theme={null}
const workspace = await createWorkspace(input);

track("workspace_created", {
  workspace_id: workspace.id,
  plan: workspace.plan,
});
```

A button click does not prove completion when the operation can still fail. If you need to analyze failures, send a separate event with a small, safe error vocabulary.

## Keep commerce data typed

Use `products` for product, cart, and checkout activity. Each product requires `id`, `name`, `price`, and `currency`.

```typescript theme={null}
track("Product Added", {
  products: [
    {
      id: "product_123",
      name: "Everyday Tee",
      price: 39,
      currency: "USD",
      quantity: 2,
    },
  ],
});
```

Use `order` when an event represents one order. Put its products in `order.products`.

```typescript theme={null}
track("Order Completed", {
  order: {
    id: "order_789",
    currency: "USD",
    total: 78,
    products: [
      {
        id: "product_123",
        name: "Everyday Tee",
        price: 39,
        currency: "USD",
        quantity: 2,
      },
    ],
  },
});
```

When `order.total` is missing, the SDK calculates it from product price and quantity. Total, tax, discount, and shipping values can all be zero.

## Add only the customer context you need

The optional `client` object applies only to the event you send.

```typescript theme={null}
track("user_signed_in", {
  client: {
    userId: "user_123",
    userGroup: "growth",
  },
});
```

Prefer a stable internal `userId` to extra contact fields. Add email, phone, address, date of birth, or gender only when you have a documented need and permission.

## Preserve page and acquisition context

Use typed page fields when an explicit event needs that context:

```typescript theme={null}
track("PageView", {
  sourceUrl: window.location.href,
  pageTitle: document.title,
  referrer: document.referrer,
});
```

`sourceUrl` and `referrer` are inputs. They do not define a public first-touch or last-touch attribution rule. Use Bily's returned analytics fields as the reporting contract. Do not infer undocumented identity merging or attribution behavior.

## Give each property one meaning and type

Keep the meaning and type of every property stable.

| Property | Recommended value                                 |
| -------- | ------------------------------------------------- |
| IDs      | Stable string or documented numeric ID            |
| Currency | Consistent currency code string                   |
| Money    | Finite number in the unit your event plan defines |
| Flags    | Boolean, not `"true"` or `"false"`                |
| Time     | ISO timestamp string in `ts`                      |
| Category | Small controlled vocabulary                       |

Do not change a released property from a number to a formatted string. If its meaning changes, add a new, clearly named property.

## Prepare the contract for review

For each event, record:

* the exact name and owner;
* the successful trigger;
* required and optional fields;
* field types and allowed values;
* customer-data classification;
* an example payload;
* a test and expected result.

<CardGroup cols={2}>
  <Card title="Payload reference" icon="brackets-curly" href="/api-reference/payload">
    See every typed SDK field.
  </Card>

  <Card title="Validate the model" icon="list-check" href="/guides/validation-debugging">
    Prove the names, payloads, and firing behavior before you release.
  </Card>
</CardGroup>
