Files
arbiter/docs/api.md

11 KiB

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

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:

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

GET /api/reviews/{review_id}

Response:

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

GET /api/reviews/{review_id}/deliberation

Response:

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

POST /api/reviews

Request Body:

{
  "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):

{
  "status": "queued",
  "job_id": "job-abc123",
  "review_id": null,
  "message": "Review queued with job ID job-abc123"
}

Get Metrics

GET /api/reviews/metrics

Response:

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

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:

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

GET /api/conversations/{conversation_id}

Get Conversation for Review

GET /api/conversations/review/{review_id}

Returns null if no conversation exists for the review.

Health Endpoints

Basic Health Check

GET /health

Response:

{
  "status": "healthy",
  "version": "0.5.0"
}

Readiness Check

GET /health/ready

Response:

{
  "status": "ready",
  "components": {
    "database": {"status": "healthy"},
    "redis": {"status": "healthy"},
    "worker": {"status": "healthy", "queue_size": 3}
  }
}

Liveness Probe

GET /health/live

Response:

{
  "status": "alive"
}

Prometheus Metrics

GET /metrics

Returns Prometheus-formatted metrics.

Webhook Endpoints

GitHub Webhook

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

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

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

{"status": "queued", "job_id": "abc123", "repository": "owner/repo", "pr_number": 42}
{"status": "ignored", "reason": "Event type 'push' not processed"}
{"status": "duplicate", "reason": "Review already queued for this commit"}

GitLab Webhook

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

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

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

{
  "detail": "Missing required fields"
}

401 Unauthorized

{
  "detail": "Invalid signature"
}

404 Not Found

{
  "detail": "Review abc123 not found"
}

422 Validation Error

{
  "detail": [
    {
      "loc": ["body", "pr_number"],
      "msg": "value is not a valid integer",
      "type": "type_error.integer"
    }
  ]
}

500 Internal Server Error

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

ARBITER_CORS_ORIGINS=["http://localhost:3000","https://dashboard.example.com"]

The API allows:

  • Specified origins
  • All standard headers
  • Credentials
  • GET, POST, PUT, DELETE methods