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

# Attributes

> Profile, device, and event attributes — when to use attributes vs events.

Attributes store the current state of users, devices, and events. Understanding when to use attributes vs events is critical for building an effective data model that supports both analytics and personalization.

***

## What are Attributes?

Attributes are **facts** that describe who a user is, what device they're on, or what happened in an event. Think of them as the labels on a record — name, email, plan type, device model, event revenue.

While events are verbs (actions), attributes are adjectives (descriptions).

### The Simple Definition

If you can describe it as "This user IS X" or "This event HAS property Y," it's an attribute.

**Examples:**

* "Sarah's email **is** [sarah@example.com](mailto:sarah@example.com)" → User attribute: `email`
* "John's plan type **is** Enterprise" → User attribute: `plan_type`
* "This device **is** an iPhone 15 Pro on iOS 17" → Device attribute: `model`, `os_version`
* "This purchase **had** revenue of \$89.99" → Event property: `revenue`

### Why Attributes Matter

Attributes are essential because they enable:

1. **Personalization**: "Hi {{name}}, your {{plan}} trial expires in {{days_remaining}} days"
2. **Targeting**: Send campaigns only to users matching specific criteria
3. **Context**: Understand who's behind each event ("This purchase was from an Enterprise customer")
4. **Enrichment**: Connect user data from your CRM, support system, or other tools
5. **Current state queries**: "How many Pro plan users do we have right now?"

### The Key Difference: State vs Action

**Attributes store current state** (one value that changes over time):

* User's current email: `sarah@example.com` → changes if they update it
* User's current plan: `pro` → changes when they upgrade to `enterprise`
* Last login date: `2026-05-04` → updates every time they log in

**Events record actions** (immutable history):

* Event: `Updated Email` at `2026-01-15` → never changes, always part of history
* Event: `Upgraded Plan` at `2026-03-20` → permanent record of this action
* Event: `Logged In` at `2026-05-04 10:30:15` → specific moment captured

**The rule:** Use attributes when you need to know "what IS true about this user right now." Use events when you need to know "what DID this user do."

### Real-World Example

**Sarah's user attributes:**

```javascript theme={null}
{
  email: "sarah@example.com",          // Current email
  name: "Sarah Johnson",               // Current name
  plan_type: "enterprise",             // Current plan (was 'pro' before)
  company: "Acme Corp",                 // Current company
  signup_date: "2026-01-15",           // Doesn't change
  total_purchases: 12,                  // Increments with each purchase
  lifetime_value: 1247.88,              // Calculated from all purchases
  last_login_at: "2026-05-04T10:30:15Z" // Updates each login
}
```

**Sarah's event history:**

```javascript theme={null}
[
  { event: "Signed Up",        timestamp: "2026-01-15T09:00:00Z" },
  { event: "Purchased",        timestamp: "2026-01-20T14:30:00Z", amount: 89.99 },
  { event: "Upgraded Plan",    timestamp: "2026-03-20T11:15:00Z", from: "pro", to: "enterprise" },
  { event: "Purchased",        timestamp: "2026-05-01T16:45:00Z", amount: 149.99 },
  { event: "Logged In",        timestamp: "2026-05-04T10:30:15Z" }
  // ... 100 more events
]
```

**Notice:**

* Attributes show her **current state**: She's enterprise now (not pro)
* Events show her **complete history**: We can see she upgraded on March 20th
* Together they give the full picture: Who she is + What she's done

***

## Attributes vs Events: The Data Modeling Decision

**Events** = Actions (verbs) → `user.track('Upgraded Plan')`\
**Attributes** = State (adjectives) → `user.plan_type = 'enterprise'`

### The Rule

**Use attributes when:**

* You need current state: "What plan are they on right now?"
* You need to personalize: "Hi {{name}}, your trial ends {{trial_ends_at}}"
* It's a fact about the user: Demographics, account info, preferences

**Use events when:**

* You need to trigger workflows: "When user upgrades → send thank-you email"
* You need analytics: "How many users upgraded this month?"
* You need history: "When did they last log in?"
* It's an action: Clicked, viewed, purchased, upgraded

### Common Mistake: Tracking State as Events

**❌ Wrong:**

```javascript theme={null}
// Don't do this
track('Email Changed', { new_email: 'user@new-domain.com' });
track('Became Enterprise Customer');
track('Credit Card Updated');
```

**Why it's wrong:** These are state updates, not user actions. You're creating noise in your event stream.

**✅ Right:**

```javascript theme={null}
// Update the attribute
setProfileAttributes({
  email: 'user@new-domain.com',
  plan_type: 'enterprise',
  payment_method_updated_at: '2026-05-04T10:00:00Z'
});

// Track the action that caused the change (if relevant)
track('Upgraded Plan', {
  from_plan: 'pro',
  to_plan: 'enterprise',
  mrr_change: 200
});
```

**Why it's right:** Attribute stores current state for queries and personalization. Event records the action for analytics and workflow triggers.

***

## Three Types of Attributes

### 1. **User Attributes**

User attributes are facts stored on the user profile, identified by `user_id`. They come in two kinds: **system-defined fields** (always present, set by the platform) and **traits** (custom key-value data you send via `identify()` or `setProfileAttributes()`).

#### System-Defined Fields

These fields exist on every user profile automatically:

| Field                                            | Description                                                      |
| ------------------------------------------------ | ---------------------------------------------------------------- |
| `id`                                             | Internal Sonyflake BIGINT primary key                            |
| `user_id`                                        | Your application's identifier for this user                      |
| `anonymous_id`                                   | The anonymous ID before identification                           |
| `name`                                           | From traits                                                      |
| `email`                                          | From traits                                                      |
| `phone`                                          | From traits                                                      |
| `timezone`                                       | IANA timezone, e.g. `America/New_York`                           |
| `language`                                       | ISO 639-1 code, e.g. `en`                                        |
| `email_opt_out`                                  | Channel opt-out status (1=not opted out, 2=opted out, 3=unknown) |
| `sms_opt_out`                                    | Same 3-state model                                               |
| `whatsapp_opt_out`                               | Same 3-state model                                               |
| `rcs_opt_out`                                    | Same 3-state model                                               |
| `push_opt_out`                                   | Same 3-state model                                               |
| `*_opt_out_at`                                   | Timestamp when each opt-out occurred                             |
| `first_seen_at`                                  | Timestamp of first identify call                                 |
| `last_seen_at`                                   | Timestamp of most recent identify call                           |
| `last_activity_at`                               | Timestamp of most recent event                                   |
| `is_deleted`                                     | Set to true by `User Deleted` lifecycle event                    |
| `is_suppressed`                                  | Set to true by `User Suppressed` lifecycle event                 |
| `deleted_at` / `suppressed_at`                   | When those states were applied                                   |
| `first_campaign_source/medium/name/term/content` | First-touch UTM attribution                                      |
| `first_referrer`                                 | Referring URL from first visit                                   |
| `signup_platform`                                | Platform at time of first identify                               |
| `creation_source`                                | How the profile was created                                      |
| `external_id`                                    | Optional external system identifier                              |
| `customer_created_at`                            | User's creation time in your own system                          |
| `created_at` / `updated_at` / `processed_at`     | System timestamps                                                |

#### Traits (Custom Attributes)

Anything you pass in `traits` via `identify()` or via `setProfileAttributes()` is stored as a custom trait and merged with existing data:

```dart theme={null}
// On identify (signup/login)
Zixflow.instance.identify(
  userId: 'user_12345',
  traits: {
    'plan_type': 'enterprise',
    'company': 'Acme Corp',
    'mrr': 499,
    'trial_ends_at': '2026-06-01T00:00:00Z',
  },
);

// Update anytime — merges with existing traits
Zixflow.instance.setProfileAttributes({
  'last_upgraded_at': '2026-05-04T10:00:00Z',
  'feature_flags': ['beta_reports', 'new_dashboard'],
});
```

Traits are schema-less — you define the keys. All trait keys are automatically catalogued for use in queries and targeting.

***

### 2. **Device Attributes**

Device attributes are facts stored on a device record, identified by `device_id` (derived from the push token). Like user attributes, they split into system-defined fields and custom traits.

#### System-Defined Fields

| Field                                               | Description                                    |
| --------------------------------------------------- | ---------------------------------------------- |
| `device_id`                                         | Internal device ID (derived from token hash)   |
| `token`                                             | Push token (FCM/APNs)                          |
| `platform`                                          | `ios` or `android`                             |
| `manufacturer`                                      | e.g. `Apple`, `Samsung`                        |
| `model`                                             | e.g. `iPhone 15 Pro`, `Pixel 8`                |
| `os_name`                                           | e.g. `iOS`, `Android`                          |
| `os_version`                                        | e.g. `17.4.1`, `14`                            |
| `app_version`                                       | Your app's version string                      |
| `sdk_version`                                       | Zixflow SDK version                            |
| `screen_width` / `screen_height` / `screen_density` | Display info                                   |
| `locale`                                            | e.g. `en-US`                                   |
| `timezone`                                          | IANA timezone                                  |
| `carrier`                                           | Mobile carrier name                            |
| `active`                                            | `false` after `Device Deleted` lifecycle event |
| `push_enabled`                                      | Whether the device can receive push            |
| `last_used_at`                                      | Last time SDK sent an event from this device   |
| `created_at` / `updated_at` / `processed_at`        | System timestamps                              |

#### Traits (Custom Attributes)

Custom key-value data you attach to a specific device:

```dart theme={null}
Zixflow.instance.setDeviceAttributes({
  'notification_categories': ['promotions', 'transactional'],
  'dark_mode_enabled': true,
  'preferred_locale': 'en-US',
});
```

**Use cases:**

* Target specific app versions: "Send 'Update Available' push to devices on v1.x"
* Platform-specific campaigns: "iOS-only feature announcement"
* Notification category filtering

***

### 3. **Event Properties**

Event properties are the custom key-value pairs attached to individual events. Unlike user or device attributes (which describe current state), event properties are **immutable** — they record the details of a specific moment in time.

#### Anatomy of an Event with Properties

```json theme={null}
{
  "event_name": "Order Completed",
  "event_type": 1,
  "user_id": "user_12345",
  "anonymous_id": null,
  "timestamp": "2026-05-04T14:32:15Z",
  "properties": {
    "order_id": "ord_789",
    "revenue": 129.99,
    "currency": "USD",
    "product_name": "Nike Air Max 90",
    "payment_method": "credit_card"
  },
  "context": {
    "platform": "ios",
    "app": { "version": "2.1.0" },
    "os": { "name": "iOS", "version": "17.4.1" },
    "device": { "model": "iPhone 15 Pro" },
    "locale": "en-US",
    "timezone": "America/New_York"
  }
}
```

#### System-Defined Event Fields

Every event has these fields set automatically:

| Field          | Description                                                           |
| -------------- | --------------------------------------------------------------------- |
| `event_name`   | The name you pass to `track()` / `screen()` / `page()`                |
| `event_type`   | Integer: 1=track, 2=screen, 3=page, 4=identify                        |
| `user_id`      | Set if user is identified                                             |
| `anonymous_id` | Set if user is anonymous                                              |
| `timestamp`    | When the event occurred (millisecond precision)                       |
| `context`      | Platform, device, OS, app version, locale, timezone — captured by SDK |
| `workspace_id` | Your workspace                                                        |

#### Custom Properties

The `properties` object is fully custom — you define the keys and values:

```dart theme={null}
Zixflow.instance.track('Purchased', properties: {
  'order_id': 'ord_123',
  'revenue': 89.99,
  'currency': 'USD',
  'product_name': 'Red Sneakers',
  'payment_method': 'credit_card',
});
```

All property keys are automatically catalogued for use in queries and campaign conditions.

**Note:** Event properties are stored in dedicated event tables (`profile_events`, `device_logs`, `profile_identify_events`) with separate retention policies — not in the user profile.

***

## Automatic Attribute Discovery

Zixflow automatically learns what attributes exist based on the data you send - no need to define a schema upfront.

### How It Works

**You track an event with properties:**

```javascript theme={null}
Zixflow.track('Purchased', {
  order_id: 'ord_123',
  product_name: 'Enterprise Plan',
  revenue: 499.00,
  currency: 'USD',
  payment_method: 'credit_card'
});
```

**Zixflow automatically:**

1. Discovers all the property names (order\_id, product\_name, revenue, etc.)
2. Figures out their data types (order\_id is text, revenue is a number)
3. Makes them available for querying and filtering

**Now you can:**

* Segment: "Users who purchased where product\_name = 'Enterprise Plan'"
* Analyze: "Top 10 products by revenue"
* Filter: "Show events where payment\_method = 'credit\_card'"

**Benefits:**
✅ **No schema setup** — Just start tracking, Zixflow learns automatically\
✅ **Self-documenting** — See what attributes you're actually using\
✅ **Type checking** — Zixflow warns if data types don't match\
✅ **Easy filtering** — All attributes available for querying

***

## Reserved Attribute Names

These attribute names are reserved for system use. Don't use them as custom attributes:

**User-level:**

* `id`, `workspace_id`, `user_id`, `email`, `anonymous_id`
* `created_at`, `updated_at`, `last_seen_at`

**Device-level:**

* `device_id`, `device_token`, `platform`, `last_seen_at`

**If you try to set a reserved attribute:**

* System logs a warning
* Value is ignored (not written)
* Original system value preserved

***

**Next:** Understand how events flow through Zixflow → [The Data Journey](/documentation/events/data-journey)
