---
title: Lazy User Mirror
tags: [concept, architecture, cost-tracking, users]
created: 2026-04-27
updated: 2026-04-27
---

# Lazy User Mirror

The cost-tracker maintains a read-only mirror of users from each connected project. "Lazy" means the mirror record is created **on first use**, not when the user is created in the source project.

## Why lazy (not eager)?

- Source projects own their users; we don't want to add mandatory webhooks to every user CRUD operation
- A user who never triggers an AI call doesn't need to exist in the mirror
- Keeps integration surface minimal — just send `user_external_id` with every event

## How it works

1. When `POST /v1/preflight` or `POST /v1/usage/record` arrives with a `user_external_id` not yet in `users_mirror` → cost-tracker **auto-creates** a mirror record using `email`, `full_name`, and `role` from the request payload.

2. The SDK sends these on every call:
   ```python
   await ct.preflight(
       user_external_id=user.id,
       user_email=user.email,          # for mirror creation
       user_full_name=user.full_name,  # for analytics display
       user_role=user.role,            # for RBAC in analytics
       ...
   )
   ```

3. `users_mirror` record fields: `source_app`, `external_id`, `email`, `full_name`, `role`, `workspace_id` (from project lookup), `team_id`, `first_seen_at`, `last_seen_at`.

4. Subsequent calls with the same `user_external_id` update `last_seen_at` only — no overwrite of profile fields. Profile changes are pushed via explicit `POST /v1/users/upsert`.

## Explicit sync for profile changes

If a user's email, role, or name changes in the source project:

```python
await ct.upsert_user(
    external_id=user.id,
    email=user.email,
    full_name=user.full_name,
    role=user.role,
    project_external_id=...,
)
```

In video-accessibility this is called from:
- `POST /admin/users` — on user creation (`routes_admin.py:101`)
- Login flow — on every successful login (keeps mirror fresh)

## Analytics implications

- Users without any AI events never appear in the mirror → analytics shows only users who actually consumed AI
- Orphan users (no `project_external_id` provided) are mirrored but show `workspace_id=null` until assigned via bulk action in admin UI
