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

# Métricas de Ingestão

> Endpoints REST e queries SQL para volume, bytes e heartbeat de ingestão por organização.

# Métricas de Ingestão

Três endpoints REST cobrem dashboards e integrações externas. Todas as respostas são filtradas pelo `organization_id` do JWT.

## GET `/v1/ingestion/metrics/timeseries`

Retorna pontos de série temporal de bytes e rows ingeridos. Roll-up no servidor a partir de `gold_ingestion_metrics_minute`.

**Query params:**

| Param              | Tipo    | Default     | Descrição                                           |
| ------------------ | ------- | ----------- | --------------------------------------------------- |
| `from`             | RFC3339 | obrigatório | Início do intervalo (UTC)                           |
| `to`               | RFC3339 | obrigatório | Fim do intervalo (UTC)                              |
| `bucket`           | enum    | `minute`    | `minute` \| `5min` \| `hour` \| `day`               |
| `domain`           | enum    | —           | `user` \| `casino` \| `transaction` \| `sportsbook` |
| `event`            | string  | —           | Filtra por evento (ex: `bet_placed`)                |
| `brand_id`         | UUID    | —           | Filtra por brand específica                         |
| `ingestion_source` | enum    | —           | `realtime` \| `backfill`                            |

**Exemplo:**

```bash theme={null}
curl "https://api.atlas.lifters.tech/v1/ingestion/metrics/timeseries?\
from=2026-05-04T00:00:00Z&\
to=2026-05-04T23:59:59Z&\
bucket=hour&\
domain=sportsbook" \
  -H "Authorization: Bearer $TOKEN"
```

**Response:**

```json theme={null}
{
  "bucket": "hour",
  "from": "2026-05-04T00:00:00Z",
  "to": "2026-05-04T23:59:59Z",
  "points": [
    { "bucket": "2026-05-04T08:00:00Z", "rows_count": 1250, "bytes_uncompressed": 487120 },
    { "bucket": "2026-05-04T09:00:00Z", "rows_count": 1640, "bytes_uncompressed": 612344 }
  ]
}
```

## GET `/v1/ingestion/metrics/summary`

Totais agregados + breakdown por evento sobre `gold_ingestion_metrics_daily`.

**Query params:**

| Param   | Tipo | Valores aceitos                        |
| ------- | ---- | -------------------------------------- |
| `range` | enum | `24h` \| `7d` \| `30d` (default `24h`) |

**Response:**

```json theme={null}
{
  "organization_id": "org_123",
  "range": "7d",
  "since": "2026-04-27T12:00:00Z",
  "rows_total": 4250000,
  "bytes_uncompressed": 2150000000,
  "last_ingest_at": "2026-05-04T11:59:42Z",
  "by_domain_event": [
    { "Domain": "sportsbook", "Event": "open", "RowsTotal": 1200000, "BytesUncompressed": 612000000 },
    { "Domain": "transaction", "Event": "deposit", "RowsTotal": 850000, "BytesUncompressed": 423000000 }
  ]
}
```

## GET `/v1/ingestion/heartbeat`

Lista uma row por combinação `(brand × domain × event × source)` com timestamp do último evento e ingest, mais lag em segundos. Ideal para dashboards de saúde.

**Query params:**

| Param                     | Tipo | Default | Descrição                            |
| ------------------------- | ---- | ------- | ------------------------------------ |
| `stale_only`              | bool | `false` | Filtra só chaves com lag > threshold |
| `stale_threshold_seconds` | int  | `300`   | Define stale (com `stale_only=true`) |

**Response:**

```json theme={null}
{
  "items": [
    {
      "organization_id": "org_123",
      "brand_id": "brand_xyz",
      "domain": "sportsbook",
      "event": "bet_placed",
      "ingestion_source": "realtime",
      "last_event_at": "2026-05-04T11:59:42Z",
      "last_ingest_at": "2026-05-04T11:59:43Z",
      "rows_total": 4123,
      "lag_seconds": 17
    }
  ]
}
```

## GET `/v1/ingestion/events`

Lista eventos distintos vistos pela organização. Usado para autocomplete no builder de regras.

**Query params:** `domain` (opcional)

**Response:**

```json theme={null}
{ "items": ["bet_placed", "deposit", "registration", "withdraw"] }
```

## Bytes uncompressed vs físicos

`bytes_uncompressed` é calculado via `byteSize(*)` no ClickHouse — **logical bytes** da row em memória. Não é igual ao byte físico no disco (compressão \~5-10× menor). É proxy adequado para:

* Cobrança/quota por tenant
* Detecção de spike anômalo
* Gráficos de volume

Para bytes físicos no disco use `system.parts` direto no ClickHouse (não exposto via API REST).

## Queries SQL diretas

Acesso direto ao ClickHouse Cloud (read-only export):

```sql theme={null}
-- Bytes ingeridos por org últimos 7d
SELECT organization_id, sum(bytes_uncompressed) AS bytes
FROM atlas.gold_ingestion_metrics_minute
WHERE ingest_minute >= now() - INTERVAL 7 DAY
GROUP BY organization_id
ORDER BY bytes DESC;

-- Top 20 eventos por volume nos últimos 30d
SELECT domain, event, sum(rows_count) AS rows
FROM atlas.gold_ingestion_metrics_daily
WHERE ingest_date >= today() - 30
GROUP BY domain, event
ORDER BY rows DESC
LIMIT 20;

-- Chaves "paradas" (sem ingest há > 5 min)
SELECT organization_id, brand_id, domain, event,
       maxMerge(last_ingest_at) AS last_ingest,
       now() - maxMerge(last_ingest_at) AS lag
FROM atlas.gold_ingestion_heartbeat
GROUP BY organization_id, brand_id, domain, event
HAVING lag > 300
ORDER BY lag DESC;
```

## Retention

| Tabela                          | Retention                                          |
| ------------------------------- | -------------------------------------------------- |
| `gold_ingestion_metrics_minute` | 30 dias (TTL automático, drop por partição diária) |
| `gold_ingestion_metrics_daily`  | sem TTL (série histórica longa)                    |
| `gold_ingestion_heartbeat`      | sem TTL (1 row por chave, AggregatingMergeTree)    |
