Files
arbiter/docs/api.md

602 lines
11 KiB
Markdown

# API Reference
Arbiter provides a REST API for managing reviews, monitoring metrics, and receiving
webhook events from GitHub and GitLab.
## Interactive Documentation
The API provides auto-generated interactive documentation:
- **Swagger UI:** `http://localhost:8000/docs`
- **ReDoc:** `http://localhost:8000/redoc`
- **OpenAPI Schema:** `http://localhost:8000/openapi.json`
## Base URL
All API endpoints are relative to the base URL:
```
http://localhost:8000
```
In production, use your configured domain with HTTPS.
## Authentication
### Webhook Authentication
Webhooks use signature/token verification:
**GitHub:**
- Header: `X-Hub-Signature-256`
- Format: `sha256=<hmac_hex_digest>`
- Algorithm: HMAC-SHA256 using `ARBITER_GITHUB_WEBHOOK_SECRET`
**GitLab:**
- Header: `X-GitLab-Token`
- Format: Plain token string
- Verification: Constant-time comparison with `ARBITER_GITLAB_WEBHOOK_TOKEN`
### API Authentication
The REST API currently does not require authentication. For production deployments,
place it behind a reverse proxy with authentication or configure network-level access
controls.
## REST Endpoints
### Reviews
#### List Reviews
```http
GET /api/reviews
```
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | 1 | Page number (1-indexed) |
| `page_size` | integer | 20 | Items per page (1-100) |
| `repository` | string | - | Filter by repository name |
| `status` | string | - | Filter by status (pending, processing, completed, failed) |
| `verdict` | string | - | Filter by verdict (approve, request_changes, comment) |
| `author` | string | - | Filter by PR author |
**Response:**
```json
{
"items": [
{
"id": "abc123",
"repository": "owner/repo",
"pr_number": 42,
"pr_title": "Add new feature",
"author": "username",
"status": "completed",
"verdict": "request_changes",
"verdict_confidence": 0.92,
"finding_count": 5,
"critical_count": 1,
"high_count": 2,
"total_cost_usd": 0.12,
"created_at": "2025-06-04T10:00:00Z",
"completed_at": "2025-06-04T10:02:30Z"
}
],
"total": 150,
"page": 1,
"page_size": 20,
"pages": 8
}
```
#### Get Review Detail
```http
GET /api/reviews/{review_id}
```
**Response:**
```json
{
"id": "abc123",
"repository": "owner/repo",
"pr_number": 42,
"pr_title": "Add new feature",
"base_sha": "abc1234",
"head_sha": "def5678",
"author": "username",
"is_draft": false,
"status": "completed",
"verdict": "request_changes",
"verdict_confidence": 0.92,
"verdict_reasoning": "Critical security issue requires resolution",
"total_tokens": 15000,
"total_cost_usd": 0.12,
"tokens_by_agent": {"security": 5000, "style": 5000, "complexity": 5000},
"cost_by_agent": {"security": 0.04, "style": 0.04, "complexity": 0.04},
"created_at": "2025-06-04T10:00:00Z",
"started_at": "2025-06-04T10:00:05Z",
"completed_at": "2025-06-04T10:02:30Z",
"error_message": null,
"findings": [
{
"id": "finding-1",
"agent": "security",
"file": "src/auth.py",
"line_start": 42,
"line_end": 45,
"severity": "critical",
"confidence": 0.95,
"title": "SQL Injection Vulnerability",
"description": "User input is passed directly to SQL query",
"reasoning": "String interpolation in SQL queries allows injection",
"suggestion": "Use parameterized queries",
"references": ["https://owasp.org/..."],
"prompt_version": "security-v1.0"
}
],
"conflicts": [
{
"id": "conflict-1",
"finding_ids": ["finding-2", "finding-3"],
"nature": "opposing_recommendation",
"description": "Style suggests splitting function, complexity wants to keep it",
"severity_weight": 0.5,
"resolution": "Keep function together for readability",
"winning_finding_id": "finding-3"
}
]
}
```
#### Get Deliberation Log
```http
GET /api/reviews/{review_id}/deliberation
```
**Response:**
```json
{
"review_id": "abc123",
"steps": [
{
"id": "step-1",
"step_type": "agent_start",
"timestamp": "2025-06-04T10:00:10Z",
"description": "Security agent started review",
"details": {"agent": "security"},
"sequence": 1
},
{
"id": "step-2",
"step_type": "finding_added",
"timestamp": "2025-06-04T10:00:45Z",
"description": "Security agent found SQL injection",
"details": {"finding_id": "finding-1", "severity": "critical"},
"sequence": 2
},
{
"id": "step-3",
"step_type": "conflict_detected",
"timestamp": "2025-06-04T10:01:30Z",
"description": "Detected conflict between style and complexity",
"details": {"conflict_id": "conflict-1"},
"sequence": 3
},
{
"id": "step-4",
"step_type": "verdict_determined",
"timestamp": "2025-06-04T10:02:00Z",
"description": "Final verdict: request_changes",
"details": {"verdict": "request_changes", "confidence": 0.92},
"sequence": 4
}
]
}
```
#### Trigger Manual Review
```http
POST /api/reviews
```
**Request Body:**
```json
{
"repository": "owner/repo",
"pr_number": 42,
"base_sha": "abc1234",
"head_sha": "def5678",
"pr_title": "Add new feature",
"author": "username",
"is_draft": false,
"policy_name": "default",
"diff_content": "diff --git a/file.py b/file.py\n..."
}
```
**Response (202 Accepted):**
```json
{
"status": "queued",
"job_id": "job-abc123",
"review_id": null,
"message": "Review queued with job ID job-abc123"
}
```
#### Get Metrics
```http
GET /api/reviews/metrics
```
**Response:**
```json
{
"total_reviews": 500,
"completed_reviews": 485,
"average_cost_usd": 0.15,
"verdict_counts": {
"approve": 300,
"request_changes": 150,
"comment": 35
},
"severity_counts": {
"critical": 50,
"high": 200,
"medium": 500,
"low": 1000,
"info": 300
},
"reviews_by_day": [
{"date": "2025-06-01", "count": 15},
{"date": "2025-06-02", "count": 22},
{"date": "2025-06-03", "count": 18}
],
"cost_by_agent": {
"security": 25.50,
"style": 20.00,
"complexity": 22.00
}
}
```
### Conversations
#### List Conversations
```http
GET /api/conversations
```
**Query Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | integer | 1 | Page number |
| `page_size` | integer | 20 | Items per page |
| `repository` | string | - | Filter by repository |
| `review_id` | string | - | Filter by review ID |
**Response:**
```json
{
"items": [
{
"id": "conv-123",
"review_id": "abc123",
"platform": "github",
"repository": "owner/repo",
"pr_number": 42,
"message_count": 4,
"total_tokens": 2000,
"total_cost_usd": 0.02,
"started_at": "2025-06-04T11:00:00Z",
"last_activity": "2025-06-04T11:15:00Z"
}
],
"total": 25,
"page": 1,
"page_size": 20,
"pages": 2
}
```
#### Get Conversation Detail
```http
GET /api/conversations/{conversation_id}
```
#### Get Conversation for Review
```http
GET /api/conversations/review/{review_id}
```
Returns `null` if no conversation exists for the review.
### Health Endpoints
#### Basic Health Check
```http
GET /health
```
**Response:**
```json
{
"status": "healthy",
"version": "0.5.0"
}
```
#### Readiness Check
```http
GET /health/ready
```
**Response:**
```json
{
"status": "ready",
"components": {
"database": {"status": "healthy"},
"redis": {"status": "healthy"},
"worker": {"status": "healthy", "queue_size": 3}
}
}
```
#### Liveness Probe
```http
GET /health/live
```
**Response:**
```json
{
"status": "alive"
}
```
#### Prometheus Metrics
```http
GET /metrics
```
Returns Prometheus-formatted metrics.
## Webhook Endpoints
### GitHub Webhook
```http
POST /webhooks/github
```
**Headers:**
| Header | Description |
|--------|-------------|
| `X-Hub-Signature-256` | HMAC-SHA256 signature |
| `X-GitHub-Event` | Event type (pull_request, issue_comment) |
**Supported Events:**
- `pull_request` — PR opened, synchronized, or reopened
- `issue_comment` — Comment created on PR (for follow-up questions)
#### Pull Request Payload
```json
{
"action": "opened",
"pull_request": {
"number": 42,
"title": "Add new feature",
"draft": false,
"base": {"sha": "abc1234"},
"head": {"sha": "def5678"},
"user": {"login": "username"}
},
"repository": {
"full_name": "owner/repo"
}
}
```
#### Issue Comment Payload
```json
{
"action": "created",
"issue": {
"number": 42,
"pull_request": {}
},
"comment": {
"id": 123456,
"body": "Can you explain the SQL injection warning?",
"user": {"login": "username"}
},
"repository": {
"full_name": "owner/repo"
}
}
```
**Responses:**
```json
{"status": "queued", "job_id": "abc123", "repository": "owner/repo", "pr_number": 42}
```
```json
{"status": "ignored", "reason": "Event type 'push' not processed"}
```
```json
{"status": "duplicate", "reason": "Review already queued for this commit"}
```
### GitLab Webhook
```http
POST /webhooks/gitlab
```
**Headers:**
| Header | Description |
|--------|-------------|
| `X-GitLab-Token` | Verification token |
**Supported Events:**
- `merge_request` — MR opened, reopened, or updated
- `note` — Comment (note) on MR (for follow-up questions)
#### Merge Request Payload
```json
{
"object_kind": "merge_request",
"object_attributes": {
"iid": 42,
"title": "Add new feature",
"action": "open",
"target_branch": "main",
"work_in_progress": false,
"last_commit": {"id": "def5678"}
},
"project": {
"path_with_namespace": "owner/repo"
},
"user": {
"username": "username"
}
}
```
#### Note Payload
```json
{
"object_kind": "note",
"object_attributes": {
"id": 123456,
"note": "Can you explain this warning?",
"noteable_type": "MergeRequest"
},
"merge_request": {
"iid": 42
},
"project": {
"path_with_namespace": "owner/repo"
},
"user": {
"username": "username"
}
}
```
## Error Responses
All endpoints return standard HTTP error codes with JSON error details:
### 400 Bad Request
```json
{
"detail": "Missing required fields"
}
```
### 401 Unauthorized
```json
{
"detail": "Invalid signature"
}
```
### 404 Not Found
```json
{
"detail": "Review abc123 not found"
}
```
### 422 Validation Error
```json
{
"detail": [
{
"loc": ["body", "pr_number"],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
}
```
### 500 Internal Server Error
```json
{
"detail": "Internal server error"
}
```
## Rate Limiting
The API applies rate limiting per client IP:
- Default: 60 requests per minute
- Configurable via `ARBITER_API_RATE_LIMIT_PER_MINUTE`
Rate limit headers are included in responses:
```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 55
X-RateLimit-Reset: 1707048120
```
## CORS
Cross-Origin Resource Sharing is configured via `ARBITER_CORS_ORIGINS`:
```bash
ARBITER_CORS_ORIGINS=["http://localhost:3000","https://dashboard.example.com"]
```
The API allows:
- Specified origins
- All standard headers
- Credentials
- GET, POST, PUT, DELETE methods