The hypatia platform exposes a REST API for programmatic access to the ruleset registry, verification services, and fleet operations. This document supplements the existing api-reference.adoc with additional endpoint details.
- Production
- Local Development
All API endpoints require authentication via Bearer token:
Authorization: Bearer <token>Obtain tokens via:
-
hyper config set registry.token <token> -
Environment variable
HYPER_API_TOKEN -
OAuth2 flow at
/oauth/authorize
GET /rulesetsQuery Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
|
integer |
Maximum results (1-100) |
50 |
|
integer |
Pagination offset |
0 |
|
string |
Sort field: |
|
|
string |
Sort order: |
|
Response:
{
"rulesets": [
{
"id": "security-basics",
"name": "Security Basics",
"version": "1.2.0",
"description": "Basic security rules for all repos",
"author": "hyperpolymath",
"downloads": 1523,
"tags": ["security", "workflow"],
"updated_at": "2026-01-15T10:30:00Z"
}
],
"total": 42,
"limit": 50,
"offset": 0
}GET /rulesets/{id}Path Parameters:
| Parameter | Type | Description |
|---|---|---|
|
string |
Ruleset ID or name |
Query Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
|
string |
Specific version to retrieve |
latest |
Response:
{
"id": "security-basics",
"name": "Security Basics",
"version": "1.2.0",
"description": "Basic security rules for all repos",
"author": {
"name": "hyperpolymath",
"email": "team@hyperpolymath.io"
},
"license": "MPL-2.0",
"tags": ["security", "workflow", "permissions"],
"rules": {
"preventive": [
{
"id": "SEC-001",
"name": "Block unpinned actions",
"pattern": "uses: .*@v\\d+",
"message": "GitHub Actions must be pinned to SHA",
"severity": "high"
}
],
"curative": [...],
"diagnostic": [...]
},
"metadata": {
"created_at": "2025-12-01T00:00:00Z",
"updated_at": "2026-01-15T10:30:00Z",
"downloads": 1523,
"verified": true
}
}POST /rulesetsRequest Body:
{
"name": "my-custom-rules",
"version": "1.0.0",
"description": "Custom rules for my organization",
"license": "MPL-2.0",
"tags": ["custom", "org-policy"],
"rules": {
"preventive": [...],
"curative": [...],
"diagnostic": [...]
}
}Response:
{
"id": "my-custom-rules",
"version": "1.0.0",
"sha256": "a1b2c3d4...",
"created_at": "2026-01-18T12:00:00Z",
"verified": false
}GET /searchQuery Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
|
string |
Full-text search query |
none |
|
string |
Filter by effect: |
all |
|
string |
Filter by target language |
all |
|
string |
Filter by category |
all |
|
string |
Filter by tag (can repeat) |
none |
|
string |
Filter by author |
all |
|
string |
Minimum version |
none |
|
integer |
Maximum results |
50 |
Response:
{
"results": [
{
"id": "security-basics",
"name": "Security Basics",
"version": "1.2.0",
"score": 0.95,
"matches": ["security", "permissions"],
"snippet": "...unpinned actions must be **pinned** to SHA..."
}
],
"total": 12,
"query": "security permissions"
}POST /verifyRequest Body:
{
"ruleset": {
"name": "test-rules",
"rules": {...}
},
"options": {
"check_conflicts": true,
"check_coverage": true,
"run_quickcheck": true,
"quickcheck_tests": 100
}
}Response:
{
"verified": true,
"checks": {
"conflicts": {"passed": true},
"coverage": {"passed": true, "percentage": 95.2},
"completeness": {"passed": true},
"dependencies": {"passed": true},
"quickcheck": {
"passed": true,
"tests_run": 100,
"properties": [
{"name": "idempotent", "passed": true},
{"name": "deterministic", "passed": true}
]
}
},
"warnings": [],
"errors": []
}Failure Response:
{
"verified": false,
"checks": {...},
"errors": [
{
"check": "conflicts",
"message": "Rules SEC-001 and SEC-002 conflict on pattern '*.yml'",
"details": {
"rule1": "SEC-001",
"rule2": "SEC-002",
"pattern": "*.yml"
}
}
]
}POST /fleet/runsRequest Body:
{
"repository": "https://github.com/org/repo",
"bots": ["robot-repo-automaton", "glambot"],
"options": {
"parallel": true,
"timeout": 600,
"auto_fix": true,
"continue_on_error": false
}
}Response:
{
"run_id": "abc123",
"status": "running",
"started_at": "2026-01-18T12:00:00Z",
"bots": [
{"id": "robot-repo-automaton", "status": "running"},
{"id": "glambot", "status": "pending"}
]
}GET /fleet/runs/{run_id}Response:
{
"run_id": "abc123",
"status": "completed",
"started_at": "2026-01-18T12:00:00Z",
"completed_at": "2026-01-18T12:05:30Z",
"duration_ms": 330000,
"bots": [
{
"id": "robot-repo-automaton",
"status": "completed",
"findings": 5,
"fixes_applied": 3,
"duration_ms": 180000
},
{
"id": "glambot",
"status": "completed",
"findings": 2,
"fixes_applied": 2,
"duration_ms": 120000
}
],
"summary": {
"total_findings": 7,
"total_fixes": 5,
"by_severity": {
"high": 2,
"medium": 3,
"low": 2
}
}
}DELETE /fleet/runs/{run_id}Response:
{
"run_id": "abc123",
"status": "stopped",
"stopped_at": "2026-01-18T12:03:00Z"
}GET /fleet/botsQuery Parameters:
| Parameter | Type | Description | Default |
|---|---|---|---|
|
string |
Filter by category |
all |
Response:
{
"bots": [
{
"id": "robot-repo-automaton",
"name": "Robot Repo Automaton",
"description": "Repository automation and standards enforcement",
"category": "quality",
"can_fix": true,
"checks": ["workflow-lint", "spdx-headers", "readme"],
"estimated_runtime": 120
}
]
}POST /scanRequest Body:
{
"repository": "https://github.com/org/repo",
"options": {
"min_severity": "low",
"categories": ["security", "policy"],
"skip_checks": [],
"include_hidden": false
}
}Response:
{
"scan_id": "scan-xyz789",
"status": "completed",
"repository": "https://github.com/org/repo",
"timestamp": "2026-01-18T12:00:00Z",
"duration_ms": 5230,
"findings": [
{
"check_id": "unpinned-actions",
"title": "Unpinned GitHub Action",
"description": "Action 'actions/checkout' uses version 'v4' instead of SHA",
"severity": "high",
"category": "security",
"file_path": ".github/workflows/ci.yml",
"line_number": 15,
"auto_fixable": true,
"rule_id": "SEC-WF-001"
}
],
"summary": {
"files_scanned": 42,
"total_findings": 8,
"by_severity": {
"high": 3,
"medium": 2,
"low": 3
},
"auto_fixable": 6
}
}GET /healthResponse:
{
"status": "healthy",
"version": "0.1.0",
"uptime_seconds": 86400,
"components": {
"database": "healthy",
"cache": "healthy",
"registry": "healthy"
}
}All errors follow a consistent format:
{
"error": {
"code": "RULESET_NOT_FOUND",
"message": "Ruleset 'unknown-ruleset' not found",
"details": {
"id": "unknown-ruleset"
},
"request_id": "req-abc123"
}
}| Code | HTTP Status | Description |
|---|---|---|
|
401 |
Missing or invalid authentication |
|
403 |
Insufficient permissions |
|
404 |
Requested ruleset does not exist |
|
404 |
Requested version does not exist |
|
400 |
Request body validation failed |
|
409 |
Version already exists |
|
429 |
Too many requests |
|
500 |
Unexpected server error |
API requests are rate limited:
-
Anonymous: 60 requests/hour
-
Authenticated: 5000 requests/hour
-
Enterprise: Unlimited
Rate limit headers:
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4985
X-RateLimit-Reset: 1705579200Configure webhooks to receive notifications:
POST /webhooksRequest Body:
{
"url": "https://my-server.com/webhook",
"events": ["ruleset.deposited", "ruleset.verified", "run.completed"],
"secret": "my-webhook-secret"
}| Event | Description |
|---|---|
|
New ruleset deposited |
|
Ruleset verification completed |
|
Ruleset deleted |
|
Fleet run started |
|
Fleet run completed |
|
Fleet run failed |
# List rulesets
curl -H "Authorization: Bearer $TOKEN" \
https://api.hypatia.io/v1/rulesets
# Deposit ruleset
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @ruleset.json \
https://api.hypatia.io/v1/rulesets
# Start fleet run
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"repository":"https://github.com/org/repo","bots":["glambot"]}' \
https://api.hypatia.io/v1/fleet/runsuse reqwest::Client;
use serde_json::json;
async fn list_rulesets(token: &str) -> Result<Vec<Ruleset>, Error> {
let client = Client::new();
let response = client
.get("https://api.hypatia.io/v1/rulesets")
.bearer_auth(token)
.send()
.await?;
response.json().await
}
async fn start_fleet_run(token: &str, repo: &str) -> Result<FleetRun, Error> {
let client = Client::new();
let response = client
.post("https://api.hypatia.io/v1/fleet/runs")
.bearer_auth(token)
.json(&json!({
"repository": repo,
"options": { "auto_fix": true }
}))
.send()
.await?;
response.json().await
}