> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Nectr-AI/nectr-ai-pr-review-agent/llms.txt
> Use this file to discover all available pages before exploring further.

# Review Timeline

> Get daily review counts and completion rates over time

## Overview

Retrieve daily review statistics for the specified time period, including total reviews, completed reviews, and failed reviews. Data is deduplicated by unique PR per day to provide accurate daily metrics.

## Authentication

Requires a valid JWT token in the `Authorization` header:

```
Authorization: Bearer YOUR_JWT_TOKEN
```

## Query Parameters

<ParamField query="days" type="integer" default="30">
  Number of days to include in the timeline (7-90)
</ParamField>

## Response

Returns an array of daily statistics, ordered chronologically.

<ResponseField name="[]" type="array">
  Array of daily review statistics
</ResponseField>

<ResponseField name="[].date" type="string">
  Date in ISO format (YYYY-MM-DD)
</ResponseField>

<ResponseField name="[].total" type="integer">
  Total number of unique PRs reviewed on this day
</ResponseField>

<ResponseField name="[].completed" type="integer">
  Number of reviews that completed successfully
</ResponseField>

<ResponseField name="[].failed" type="integer">
  Number of reviews that failed
</ResponseField>

## Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET "https://api.nectr.ai/api/v1/analytics/timeline?days=30" \
    -H "Authorization: Bearer YOUR_JWT_TOKEN"
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.nectr.ai/api/v1/analytics/timeline",
      headers={"Authorization": f"Bearer {token}"},
      params={"days": 30}
  )

  timeline = response.json()
  for day in timeline[-7:]:  # Last 7 days
      print(f"{day['date']}: {day['total']} reviews ({day['completed']} completed)")
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://api.nectr.ai/api/v1/analytics/timeline?days=30',
    {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    }
  );

  const timeline = await response.json();
  timeline.slice(-7).forEach(day => {
    console.log(`${day.date}: ${day.total} reviews (${day.completed} completed)`);
  });
  ```
</CodeGroup>

## Example Response

```json theme={null}
[
  {
    "date": "2026-02-08",
    "total": 3,
    "completed": 3,
    "failed": 0
  },
  {
    "date": "2026-02-09",
    "total": 5,
    "completed": 4,
    "failed": 1
  },
  {
    "date": "2026-02-10",
    "total": 2,
    "completed": 2,
    "failed": 0
  },
  {
    "date": "2026-03-07",
    "total": 7,
    "completed": 6,
    "failed": 1
  },
  {
    "date": "2026-03-08",
    "total": 4,
    "completed": 4,
    "failed": 0
  },
  {
    "date": "2026-03-09",
    "total": 6,
    "completed": 5,
    "failed": 0
  },
  {
    "date": "2026-03-10",
    "total": 8,
    "completed": 7,
    "failed": 1
  }
]
```

## Deduplication Logic

For each day, if multiple events exist for the same PR (repo + PR number), only the event with the highest priority status is counted:

1. **completed** (highest priority)
2. **failed**
3. **processing**
4. **pending** (lowest priority)

This ensures accurate daily counts even when a PR triggers multiple webhook events.

## Use Cases

### Activity Chart

```python theme={null}
import matplotlib.pyplot as plt
from datetime import datetime

timeline = get_timeline(days=30)

dates = [datetime.fromisoformat(d['date']) for d in timeline]
totals = [d['total'] for d in timeline]
completed = [d['completed'] for d in timeline]

plt.figure(figsize=(12, 6))
plt.plot(dates, totals, label='Total Reviews', marker='o')
plt.plot(dates, completed, label='Completed', marker='s')
plt.xlabel('Date')
plt.ylabel('Reviews')
plt.title('PR Review Activity (Last 30 Days)')
plt.legend()
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
```

### Success Rate Trend

```python theme={null}
timeline = get_timeline(days=30)

print("Daily Success Rate:")
for day in timeline[-7:]:
    if day['total'] > 0:
        success_rate = (day['completed'] / day['total']) * 100
        print(f"{day['date']}: {success_rate:.1f}% ({day['completed']}/{day['total']})")
    else:
        print(f"{day['date']}: No reviews")
```

### Weekly Aggregation

```python theme={null}
from datetime import datetime, timedelta
from collections import defaultdict

timeline = get_timeline(days=90)

weeks = defaultdict(lambda: {'total': 0, 'completed': 0, 'failed': 0})

for day in timeline:
    date = datetime.fromisoformat(day['date'])
    week_start = date - timedelta(days=date.weekday())
    week_key = week_start.strftime('%Y-%m-%d')
    
    weeks[week_key]['total'] += day['total']
    weeks[week_key]['completed'] += day['completed']
    weeks[week_key]['failed'] += day['failed']

print("Weekly Review Summary:")
for week, stats in sorted(weeks.items()):
    success_rate = (stats['completed'] / stats['total'] * 100) if stats['total'] > 0 else 0
    print(f"Week of {week}: {stats['total']} reviews, {success_rate:.1f}% success rate")
```

### Detect Anomalies

```python theme={null}
import numpy as np

timeline = get_timeline(days=30)
totals = [d['total'] for d in timeline]

mean = np.mean(totals)
std = np.std(totals)

print("Anomaly Detection:")
for day in timeline[-7:]:
    z_score = (day['total'] - mean) / std if std > 0 else 0
    
    if z_score > 2:
        print(f"⬆️ {day['date']}: Unusually high activity ({day['total']} reviews, z={z_score:.1f})")
    elif z_score < -2:
        print(f"⬇️ {day['date']}: Unusually low activity ({day['total']} reviews, z={z_score:.1f})")
    else:
        print(f"✅ {day['date']}: Normal activity ({day['total']} reviews)")
```

### Compare Periods

```python theme={null}
timeline = get_timeline(days=60)

this_month = timeline[-30:]
last_month = timeline[-60:-30]

this_month_total = sum(d['total'] for d in this_month)
last_month_total = sum(d['total'] for d in last_month)

change = ((this_month_total - last_month_total) / last_month_total * 100) if last_month_total > 0 else 0

print(f"This Month: {this_month_total} reviews")
print(f"Last Month: {last_month_total} reviews")
print(f"Change: {change:+.1f}%")
```

## Notes

* All days in the specified range are included, even if no reviews occurred (total = 0)
* The timeline always starts from `(current_date - days + 1)` to ensure consistent range
* Data is ordered chronologically from oldest to newest
* Failed reviews typically indicate webhook processing errors or AI service failures
* Reviews in `processing` or `pending` state are counted in `total` but not in `completed` or `failed`
