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

# Customer Journey

> How Outlit tracks contact journey stages and account billing status as dimensions of the customer context graph

Outlit tracks two independent dimensions of your customer relationships as part of the [customer context graph](/concepts/customer-context-graph): **contact journey** (how engaged each person is) and **account billing** (the commercial relationship). These are separate because a contact can be highly engaged while their company hasn't paid yet, or a company can be paying while a specific team member is inactive.

## How does Outlit model the customer journey?

Outlit tracks contacts and accounts separately:

### Contact Journey (per person)

```mermaid theme={null}
%%{init: {'theme': 'dark', 'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis', 'padding': 15}}}%%
flowchart LR
    A([Discovered]) --> B([Signed Up])
    B --> C([Activated])
    C --> D([Engaged])
    D -.->|no activity| E([Inactive])
    E -.->|meets engagement threshold| D
```

### Account Billing (per company)

```mermaid theme={null}
%%{init: {'theme': 'dark', 'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60, 'curve': 'basis', 'padding': 15}}}%%
flowchart LR
    F([None]) --> G([Trialing])
    G --> H([Paying])
    H -.-> I([Churned])
    I -.->|re-subscribes| H
```

***

## What are the contact journey stages?

Each person (contact) progresses through stages based on their product engagement:

| Stage          | Meaning                               | How It's Set                                                                 |
| -------------- | ------------------------------------- | ---------------------------------------------------------------------------- |
| **Discovered** | Email known, hasn't signed up yet     | Auto-detected by browser SDK when email is provided without userId           |
| **Signed Up**  | Created an account                    | Auto-detected by browser SDK when both email and userId are provided         |
| **Activated**  | Completed onboarding or key milestone | Manual — call `user.activate()`                                              |
| **Engaged**    | Actively using the product            | Auto-inferred from product activity via PostHog or your SDK (browser/server) |
| **Inactive**   | No activity for extended period       | Auto-inferred when no activity detected for 30+ days                         |

<Info>
  Contact stages track **product engagement**, not billing. A contact can be Engaged even if their company hasn't paid, or Inactive even if their company is actively paying.
</Info>

### How stages progress

Contacts move forward through Discovered, Signed Up, Activated, and Engaged. They can never move backwards—an Engaged contact can't become Activated again. The one exception is Inactive: contacts return to Engaged when their activity again meets the configured engagement threshold.

***

## How does Outlit detect stages automatically?

### Discovered vs Signed Up

The difference is determined by the identifiers you provide:

<Tabs>
  <Tab title="Discovered">
    When you identify a visitor with **only an email** (no userId), they're marked as Discovered:

    ```typescript theme={null}
    outlit.identify({
      email: 'jane@example.com',
      traits: { source: 'newsletter' }
    })
    ```

    Typical triggers: newsletter signup, contact form submission, lead magnet download.
  </Tab>

  <Tab title="Signed Up">
    When you identify a visitor with **both email and userId**, they're marked as Signed Up:

    ```typescript theme={null}
    outlit.identify({
      email: 'jane@example.com',
      userId: 'usr_12345',
      traits: { plan: 'free' }
    })
    ```

    Typical triggers: account creation, first login, OAuth authentication.
  </Tab>
</Tabs>

<Tip>
  If a Discovered contact later provides a userId (e.g., they sign up after submitting a lead form), they automatically advance to Signed Up.
</Tip>

### Automatic engagement and inactivity

Outlit derives Engaged and Inactive from tracked product activity. Activity signals can come from the browser SDK, server SDK, and connected product integrations. Engagement is based on consistent activity in a configured rolling window; inactivity is based on no qualifying activity for the configured inactivity period.

***

## How do I manually mark activation?

<Info>
  These examples show browser SDK usage where the user is already identified. For server-side usage, see [Node.js SDK → Activation](/tracking/server/nodejs#activation).
</Info>

### user.activate()

Mark a contact as activated after they complete onboarding:

```typescript theme={null}
outlit.user.activate({
  flow: 'onboarding',
  completedSteps: ['profile', 'first_action', 'invite_team']
})
```

Call this when users complete your onboarding flow, perform a key "aha moment" action, or reach your definition of activation.

Do not call `user.engaged()` or `user.inactive()` for new integrations. Send product activity with `track()` and let Outlit derive those stages.

***

## What are the account billing statuses?

Each account (company) has a billing status that's separate from individual contact journeys:

| Status       | Meaning                         | How It's Set                 |
| ------------ | ------------------------------- | ---------------------------- |
| **None**     | Never had a subscription        | Default                      |
| **Trialing** | Active trial period             | Automatic (Stripe) or manual |
| **Paying**   | Active paid subscription        | Automatic (Stripe) or manual |
| **Churned**  | Had subscription, now cancelled | Automatic (Stripe) or manual |

***

## How does Stripe integration work?

If you've connected Stripe to Outlit, billing status is handled automatically:

| Stripe Subscription Status | Account Billing Status |
| -------------------------- | ---------------------- |
| `trialing`                 | Trialing               |
| `active`                   | Paying                 |
| `canceled`                 | Churned                |
| `unpaid`                   | Churned                |

When a subscription status changes in Stripe, Outlit automatically updates the matching account's billing status. Individual contact journey stages are not affected.

<Info>
  Stripe integration links accounts through synced customer and contact identity. Company-domain accounts and personal-email customers are handled differently, so billing status should be read as account context, not as a contact journey stage.
</Info>

***

## How do I set billing status manually?

For non-Stripe payment processors or custom billing logic:

```typescript theme={null}
// When a trial starts
outlit.customer.trialing({
  customerId: 'cust_123', // Your app's account/workspace/customer ID
  properties: { trialEndsAt: '2025-06-15' }
})

// When payment succeeds
outlit.customer.paid({
  customerId: 'cust_123', // Your app's account/workspace/customer ID
  properties: { plan: 'pro', amount: 99 }
})

// When subscription is cancelled
outlit.customer.churned({
  customerId: 'cust_123', // Your app's account/workspace/customer ID
  properties: { reason: 'too_expensive' }
})
```

<Note>
  Billing methods target the account by `customerId`, not individual contacts. Send the same `customerId` in identify calls to link people to that account/workspace.
</Note>

***

## Best practices

**Let integrations handle what they can.** Discovered and Signed Up are auto-detected by the browser SDK based on identifiers. Engaged and Inactive are derived from tracked product activity captured by the browser SDK, server SDK, and connected product integrations. Stripe handles billing status. The main stage you need to instrument manually is Activated — call `user.activate()` when users complete your definition of activation.

**Understand the separation.** Contact journey and account billing are independent. Jane can be Engaged while her company is on None, or Inactive while her company is Paying. Jane's activity can change independently from her company's billing status.

**Call activation at the right time.** `user.activate()` should be called when the action is confirmed complete, not when the user clicks a button. Wait for the backend to confirm success before tracking.

**Include useful properties.** Properties help you analyze stage transitions. Include context like which onboarding flow was completed, time-to-activate, and which steps were skipped.

***

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="What's the difference between contact journey and account billing?">
    Contact journey tracks individual people's product engagement (Discovered through Inactive). Account billing tracks the commercial relationship (None through Churned). They're independent — Jane can be Engaged while her company is on a free plan, or Inactive while her company is actively paying.
  </Accordion>

  <Accordion title="Which stages require manual instrumentation?">
    Activated is the main stage you instrument manually by calling `user.activate()`. Discovered and Signed Up are auto-detected by the browser SDK. Engaged and Inactive are derived from tracked product activity. If Stripe is connected, billing statuses sync automatically; otherwise, set billing status manually with the customer billing methods.
  </Accordion>

  <Accordion title="Can contacts move backward in journey stages?">
    Contacts generally progress forward through Discovered, Signed Up, Activated, and Engaged. The only cycle is Engaged ↔ Inactive, which is driven by activity and inactivity.
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Identity Resolution" icon="fingerprint" href="/concepts/identity-resolution">
    How Outlit connects anonymous visitors to known contacts
  </Card>

  <Card title="Server-Side Tracking" icon="server" href="/tracking/server/nodejs">
    Track stage events from your backend
  </Card>

  <Card title="Browser Integration" icon="browser" href="/tracking/browser/script">
    Set up browser tracking
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/ingest">
    Direct API for stage events
  </Card>
</CardGroup>
