> ## 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.

# Payload types

> Build consistent event payloads with Bily client, product, and order types.

Pass an optional `BilyPayload` to `track()`. Typed fields keep common data consistent, while additional top-level keys let you describe your application.

## Build a payload

```typescript theme={null}
type BilyPayload = {
  client?: BilyClient;
  order?: BilyOrder;
  products?: BilyProduct[];
  pageTitle?: string;
  userAgent?: string;
  ip?: string;
  referrer?: string;
  sourceUrl?: string;
  source?: "app" | "web";
  category?: string;
  searchQuery?: string;
  eventAlias?: string;
  [key: string]: unknown;
};
```

The browser SDK sets `source` to `web` for you.

For explicit SPA page views, send `sourceUrl` and `pageTitle`. For product searches, send `searchQuery`.

Do not supply an IP address from browser code.

## Describe a client

```typescript theme={null}
type BilyAddress = {
  city?: string;
  state?: string;
  zip?: string;
  country?: string;
};

type BilyClient = {
  firstname?: string;
  lastname?: string;
  dateOfBirth?: string;
  email?: string;
  phone?: string;
  gender?: string;
  address?: BilyAddress;
  userId?: string;
  userGroup?: string;
};
```

Every client field is optional. Send only the customer data allowed by the current consent state.

## Describe a product

```typescript theme={null}
type BilyProduct = {
  id: string | number;
  name: string;
  price: number;
  currency: string;
  quantity?: number;
  sku?: string;
  category?: string;
  brand?: string;
  image?: string;
};
```

When you omit `quantity`, it defaults to `1`. When you provide `image`, the SDK preserves it.

## Describe an order

```typescript theme={null}
type BilyOrder = {
  id: string | number;
  products?: BilyProduct[];
  currency?: string;
  total?: number;
  tax?: number;
  discount?: number;
  discountCode?: string;
  shippingFees?: number;
};
```

If `total` is absent, the SDK calculates it from each product's price and quantity. Zero remains valid for `total`, `tax`, `discount`, and `shippingFees`.

When a payload contains `order`, place its products in `order.products`. Do not rely on the top-level `products` field for the same event.

## Add application properties

Add your own top-level keys when an event needs application-specific properties.

```typescript theme={null}
track("workspace_created", {
  workspace_id: "workspace_456",
  organization_id: "organization_789",
  plan: "growth",
});
```

The SDK creates `event_id` and `ts` when they are absent. Provide them yourself when you already have a stable event ID or timestamp.

```typescript theme={null}
track("campaign_launched", {
  event_id: "campaign_launch_01J2M91S2P",
  ts: "2026-07-13T08:30:00.000Z",
  campaign_id: "campaign_123",
});
```

<CardGroup cols={2}>
  <Card title="Event and data model" icon="diagram-project" href="/guides/event-data-model" />

  <Card title="Ecommerce examples" icon="cart-shopping" href="/guides/ecommerce" />

  <Card title="Data safety" icon="shield" href="/privacy/data-safety" />
</CardGroup>
