602 lines
11 KiB
Markdown
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
|