---
title: "analytics"
locale: "ru"
---
# analytics

---
title: Analytics API
description: First-party аналитика Ring — ингест и админ-чтение: app-события, device telemetry, Web Vitals, захват ошибок и platform stats через проверенные маршруты /api/analytics
last_modified: "2026-09-06"
audience: both
---

# Analytics API

> **Info**
> Используйте вкладки **Founder** / **Developer** в боковой панели, чтобы отфильтровать эту страницу. Поверхности админ-консоли потребляют эти маршруты; сторонние провайдеры — опциональные env-хуки.

Ring поставляет **first-party аналитический пайплайн**: клиентская телеметрия ингестируется через `/api/analytics/*`, валидируется Zod на границе маршрута, сохраняется в `analytics_events` / `web_vitals` / `analytics_errors` и читается обратно админ-scoped GET-маршрутами. Хранение можно полностью отключить на среду — тогда каждый маршрут подтверждает без записи.

| Previous (GA-only setup) | Ring equivalent |
|--------------------------|-----------------|
| Third-party SDK only, data leaves your clone | First-party ингест в вашу собственную базу |
| One opaque dashboard | Админ-scoped чтение + platform stats с role gates |
| No device/session context | Батчи `sessionId`, device telemetry с tunnel fan-out |
| Web Vitals left unmeasured | Ингест Core Web Vitals + вычисленный `performanceScore` |

## Поверхность маршрутов

| Endpoint | Method | Auth | Purpose |
|----------|--------|------|---------|
| `/api/analytics/app` | POST | optional userId | Батчированные app-события → `analytics_events` |
| `/api/analytics/device` | POST | session required | Device telemetry → `device_telemetry` + fan-out в user tunnel |
| `/api/analytics/navigation` | POST | — | Лёгкий acknowledgement stub (без хранения) |
| `/api/analytics/errors` | POST | optional userId | Захват клиентских ошибок → `analytics_errors` (отложенная запись) |
| `/api/analytics/errors` | GET | platform admin | Список ошибок с фильтрами severity/component |
| `/api/analytics/web-vitals` | POST | optional userId | Core Web Vitals + `performanceScore` → `web_vitals` (отложенная запись) |
| `/api/analytics/web-vitals` | GET | platform admin | Сводка `scope=platform` или per-user; timeframe `24h` / `7d` / `30d` |
| `/api/analytics/platform-stats` | GET | platform admin | Живые подсчёты для `users`, `entities`, `opportunities` |

### For founders

## Почему это важно для вашего клона

- **Ваши данные остаются на вашем Ring.** Ингест пишет в вашу базу; ничего не уходит третьей стороне, пока вы сами не включите env-хуки GA/Mixpanel/Amplitude.
- **Статистика админ-консоли** — platform counts и сводки Web Vitals закрыты role gate на `platform admin`.
- **Kill switch** — `ANALYTICS_DISABLE_STORAGE=true` отключает persistence без изменений кода; endpoints продолжают отвечать.
- **Телеметрия docs и personal-page** — platform summary также агрегирует docs 404 и визиты personal-page (`getPlatformAnalytics`).

### Опциональные сторонние хуки (только env)

| Variable | Provider |
|----------|----------|
| `GA_TRACKING_ID` / `GA4_MEASUREMENT_PROTOCOL_SECRET` | Google Analytics |
| `MIXPANEL_TOKEN` | Mixpanel |
| `AMPLITUDE_API_KEY` | Amplitude |
| `SENTRY_DSN` | Sentry error tracking |
| `DD_API_KEY` / `DD_APP_KEY` | Datadog |

Ни один не обязателен — собственный пайплайн Ring работает без них.

### For developers

## Контракт ингеста

Все записи **неблокирующие**: валидация синхронна, затем persistence в Next.js `after()`, так что ответ клиенту никогда не задерживается. Схема батча (проверено в `features/analytics/lib/analytics-db.ts`):

{`{
  sessionId: string,        // 1–128 chars: a-zA-Z0-9 _ - : .
  userId: string | null,    // enriched from session when omitted
  events: [                 // 1–50 events per batch
    { type: string, data?: unknown, timestamp?: number }
  ]
}`}

Невалидные payload возвращают `400` с первой Zod-проблемой. Батчи свыше 50 событий отклоняются, а не тихо обрезаются.

### Куда пишет каждый маршрут

| Route | Validation module | Storage |
|-------|-------------------|---------|
| `app` | `appAnalyticsBatchSchema` | `analytics_events` |
| `device` | `deviceTelemetryBodySchema` (`device-telemetry-db.ts`) | device telemetry + `publishToUserTunnel` через `telemetryChannelForDomain` |
| `errors` | `analyticsErrorPayloadSchema` | `analytics_errors` (userId обогащается внутри `after()`) |
| `web-vitals` | `webVitalsInputSchema` + `calculatePerformanceScore` | `web_vitals` с обнаруженными platform/browser |
| `navigation` | none (stub) | ничего — возвращает `{ ok: true }` |

### Админ-чтение

- `GET /api/analytics/platform-stats` — гейт `isPlatformAdmin(session.user.role)`; параллельные `countDocs` над users / entities / opportunities.
- `GET /api/analytics/web-vitals?scope=platform&timeframe=30d` — platform summary через `getPlatformAnalyticsSummary`; без `scope` — per-user метрики.
- Aggregation SSOT: `features/analytics/services/get-platform-analytics.ts` (core summary + docs not-found + personal-page stats).

## Конфигурация

| Variable | Default | Effect |
|----------|---------|--------|
| `ANALYTICS_DISABLE_STORAGE` | unset | `"true"` подтверждает события без записи в базу |

## Связанная документация

  
- [api/admin](/docs/api/admin.md) — Next-step: админ-scoped API-маршруты и RBAC-паттерны после ингеста аналитики.

  
- [features/performance](/docs/features/performance.md) — Same-workflow: телеметрия Web Vitals питает страницы performance.

  
- [architecture/data-validation](/docs/architecture/data-validation.md) — Deep-dive: Zod-охранники границы маршрута, которые использует каждый ингест-маршрут.
