Sentinel exposes a tRPC API for the dashboard and REST endpoints for webhooks and admin operations.
Base URL: /api/trpc
All procedures use superjson for serialization (handles Dates, BigInts, etc.).
Dashboard data for repository overview.
Returns summary metrics for the dashboard cards.
// Input
{ repoId: string }
// Output
{
aiCodePercentage: number, // 0-100
verificationTaxHours: number, // Hours spent reviewing AI code
highRiskFiles: number, // Count of T3/T4 files
openIncidents: number // Unresolved incidents
}Returns time-series data for charts.
// Input
{
repoId: string,
days?: number // Default: 30
}
// Output
Array<{
date: string, // YYYY-MM-DD
aiCodePercentage: number,
totalCommits: number,
aiCommits: number,
humanCommits: number,
avgReviewTimeMins: number,
verificationTaxHours: number
}>Returns files with elevated risk scores.
// Input
{
repoId: string,
minRiskScore?: number, // Default: 0.5
limit?: number // Default: 50
}
// Output
Array<{
filePath: string,
riskTier: 'T1_boilerplate' | 'T2_glue' | 'T3_core' | 'T4_novel',
riskScore: number,
aiConfidence: number,
commitSha: string,
analyzedAt: Date
}>Code event history from GitHub webhooks.
Paginated event feed.
// Input
{
repoId: string,
limit?: number, // Default: 50, max: 100
cursor?: string // Event ID for pagination
}
// Output
{
events: Array<{
id: string,
eventType: 'commit' | 'pr_opened' | 'pr_reviewed' | 'pr_merged' | 'deploy',
timestamp: Date,
commitSha: string | null,
prNumber: number | null,
authorLogin: string,
metadata: Record<string, unknown>
}>,
nextCursor: string | null
}Single event with full details.
// Input
{ eventId: string }
// Output
{
id: string,
repoId: string,
eventType: string,
timestamp: Date,
commitSha: string | null,
prNumber: number | null,
authorLogin: string,
metadata: Record<string, unknown>,
createdAt: Date
}Production incident tracking.
List incidents for a repository.
// Input
{
repoId: string,
status?: 'investigating' | 'identified' | 'resolved'
}
// Output
Array<{
id: string,
title: string,
severity: 'sev1' | 'sev2' | 'sev3' | 'sev4',
status: string,
detectedAt: Date,
resolvedAt: Date | null,
suspectedCommitSha: string | null,
aiAttributed: boolean | null
}>Single incident with attribution details.
// Input
{
incidentId: string,
includeAttribution?: boolean // Default: false
}
// Output
{
id: string,
repoId: string,
title: string,
severity: string,
status: string,
detectedAt: Date,
resolvedAt: Date | null,
suspectedCommitSha: string | null,
affectedFiles: string[],
aiAttributed: boolean | null,
rootCause: string | null,
metadata: Record<string, unknown>,
// When includeAttribution is true:
attribution?: {
aiConfidence: number,
riskTier: string,
detectionSignals: Record<string, unknown>
}
}Alert history and acknowledgment.
Filtered alert list.
// Input
{
repoId: string,
severity?: 'info' | 'warning' | 'critical',
acknowledged?: boolean,
days?: number, // Default: 30
limit?: number // Default: 50
}
// Output
Array<{
id: string,
ruleName: string,
severity: string,
title: string,
message: string,
metricValue: string,
threshold: string,
channels: string[],
triggeredAt: Date,
sentAt: Date | null,
acknowledgedAt: Date | null,
acknowledgedBy: string | null
}>Alert counts for summary cards.
// Input
{ repoId: string }
// Output
{
total: number, // Last 30 days
critical: number, // Critical severity count
unacknowledged: number, // Not yet acknowledged
mostRecent: {
title: string,
triggeredAt: Date,
severity: string
} | null
}Mark an alert as seen.
// Input
{
alertId: string,
userId: string // Clerk user ID
}
// Output
{
id: string,
acknowledgedAt: Date,
acknowledgedBy: string
}Receives GitHub webhook events.
Headers:
X-GitHub-Event— Event type (push, pull_request, etc.)X-GitHub-Delivery— Unique delivery IDX-Hub-Signature-256— HMAC signature for verification
Response: 200 OK with { received: true }
The handler validates the signature, checks if the repository is tracked, and queues a job for async processing. Always responds quickly to avoid GitHub timeouts.
All admin endpoints require the X-Admin-Key header.
Triggers metrics computation.
Query Parameters:
date— Single date (YYYY-MM-DD)startDate+endDate— Date range for backfillrepoId— Specific repository (optional, defaults to all active repos)
Examples:
# Yesterday (default)
curl -X POST "http://localhost:3000/api/admin/metrics/compute" \
-H "X-Admin-Key: your-key"
# Specific date
curl -X POST "http://localhost:3000/api/admin/metrics/compute?date=2026-01-15" \
-H "X-Admin-Key: your-key"
# Backfill range
curl -X POST "http://localhost:3000/api/admin/metrics/compute?startDate=2026-01-01&endDate=2026-01-31" \
-H "X-Admin-Key: your-key"Response:
{
"success": true,
"processed": 7,
"skipped": 0
}Triggers weekly code survival analysis.
curl -X POST "http://localhost:3000/api/admin/metrics/survival" \
-H "X-Admin-Key: your-key"Triggers review saturation check.
curl -X POST "http://localhost:3000/api/admin/metrics/saturation" \
-H "X-Admin-Key: your-key"Acknowledges an alert (called from dashboard).
Response:
{
"success": true,
"alert": { ... }
}tRPC procedures throw typed errors:
throw new TRPCError({
code: 'NOT_FOUND',
message: 'Repository not found'
});Common codes:
BAD_REQUEST— Invalid inputNOT_FOUND— Resource doesn't existUNAUTHORIZED— Missing or invalid auth (future)INTERNAL_SERVER_ERROR— Something broke
REST endpoints return JSON with appropriate status codes:
{
"error": "Description of what went wrong"
}Status codes:
400— Bad request (missing params, invalid format)401— Unauthorized (missing/invalid admin key)404— Not found500— Internal error
Currently no rate limits are enforced. When needed:
- Admin API: 60 requests/minute
- tRPC queries: 1000 requests/minute per IP
- Webhook handler: No limit (GitHub handles this)
List endpoints use cursor-based pagination:
// First page
const { events, nextCursor } = await trpc.events.getCodeEvents({
repoId: '...',
limit: 50
});
// Next page
const page2 = await trpc.events.getCodeEvents({
repoId: '...',
limit: 50,
cursor: nextCursor
});When nextCursor is null, there are no more results.