Skip to main content
GET
/
api
/
v1
/
analytics
/
timeline
Review Timeline
curl --request GET \
  --url https://api.example.com/api/v1/analytics/timeline
{
  "[]": [
    {}
  ],
  "[].date": "<string>",
  "[].total": 123,
  "[].completed": 123,
  "[].failed": 123
}

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

days
integer
default:"30"
Number of days to include in the timeline (7-90)

Response

Returns an array of daily statistics, ordered chronologically.
[]
array
Array of daily review statistics
[].date
string
Date in ISO format (YYYY-MM-DD)
[].total
integer
Total number of unique PRs reviewed on this day
[].completed
integer
Number of reviews that completed successfully
[].failed
integer
Number of reviews that failed

Example Request

curl -X GET "https://api.nectr.ai/api/v1/analytics/timeline?days=30" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

Example Response

[
  {
    "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

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

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

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

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

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

Build docs developers (and LLMs) love