Endpoint reference
A reference to the Hyrax REST API, grouped by resource. Every endpoint lives under /api/..., takes and returns JSON, and authenticates with an hk_live_ API key (-H "Authorization: Bearer hk_live_...", omitted from the examples below). Start with the API overview for authentication, the error envelope, pagination, and rate limits. This page is the guided tour — what each route is for and what a status code MEANS; for the full request and response schemas of any operation, open the API explorer, which is generated from the live API.
Repositories are addressed by the owner/repo/branch triple in path segments (for example /api/repos/acme/billing-service/main/jobs). Where a repo is named in a query parameter — the repo filter on observations, repo_name on a forecast lookup — use the shorter repo@branch slug (for example billing-service@main). Observations are addressed by their HYRAX-N ref.
Jobs
A job is one run of a workflow against a repo.
| Method | Path | Description |
|---|---|---|
POST | /api/repos/{owner}/{repo}/{branch}/jobs | Submit a job for a repo. |
GET | /api/repos/{owner}/{repo}/{branch}/jobs | List a repo's jobs (paginated). |
GET | /api/jobs | List all jobs across the workspace (paginated). |
GET | /api/jobs/{job_id} | Get a single job's detail. |
POST | /api/jobs/{job_id}/cancel | Cancel a pending or running job. REST-only — not exposed as an MCP tool. |
POST | /api/jobs/{job_id}/retry | Retry a failed or cancelled job, preserving its workflow. Returns 201 with the new retry job, or 200 when an in-flight retry of the same parent already exists (idempotent match — the returned job is the existing retry; no new work was enqueued). |
Submit a job
The body is { "workflow": "...", "params": { ... } }. workflow is one of mini_audit, standard_audit, audit, discover, revalidate, review, fix, or publish — which of those you can submit depends on your plan; see Workflows. params is the per-workflow parameter object (often {}).
Two further workflows, task (a free-form directive that opens a pull request) and suggest, are in private beta and sit on no plan tier: submitting one without it enabled for your workspace fails with 403 permission_error (code: "feature_not_enabled"), and upgrading does not grant it. Contact Hyrax if you want them turned on.
curl -s -X POST \
"https://api.hyrax.dev/api/repos/acme/billing-service/main/jobs" \
-H "Content-Type: application/json" \
-d '{ "workflow": "standard_audit", "params": {} }'
The response is a JobResponse. The status starts at pending and moves through running to one of completed / failed / cancelled:
{
"id": "...",
"workflow": "audit",
"status": "pending",
"repo_name": "billing-service@main"
}
A fresh submission returns 201. When the submission matches an in-flight or recently-completed job with identical parameters on the same commit — or matches an Idempotency-Key header you sent within the last 24 hours — the endpoint returns 200 with the existing job instead (no new work was enqueued). Both codes carry the same JobResponse shape, so treat them as semantically distinct: only a 201 means your request started a run. Send an Idempotency-Key header to make network retries safe, and pass ?force=true to push a fresh submission past the short soft-dedupe window on terminal jobs.
A workflow your plan doesn't include, or a request that exceeds your credit or a budget, comes back as 402 billing_error — for a workflow, error.details.allowed_workflows lists your plan's workflow entitlements. Retry with one of the verbs named above rather than any name in that list: it's derived from the entitlements on your plan, so it can also carry an internal verb that no longer accepts submissions (that one comes back 422). A private-beta workflow that isn't enabled for your workspace comes back as 403 permission_error with code: "feature_not_enabled". See Errors.
Repositories
Manage the repos in your workspace and read their discovery output.
| Method | Path | Description |
|---|---|---|
GET | /api/repos | List repos in the workspace (paginated). |
POST | /api/repos | Register a new repo. |
GET | /api/repos/{owner}/{repo}/{branch} | Get a repo's detail. |
DELETE | /api/repos/{owner}/{repo}/{branch} | Remove a repo from the workspace (204). Requires the delete_repo permission (admin-default + grant-pickable; owners hold it via the bypass). Soft-delete only. REST-only — not exposed as an MCP tool. |
POST | /api/repos/{owner}/{repo}/{branch}/reset | Reset a repo to a clean state. Body flags pick what to clear: { "findings": ..., "history": ..., "suggestions": ..., "wipe": ... }. REST-only — not exposed as an MCP tool. |
GET | /api/repos/{owner}/{repo}/{branch}/discovery | Discovery files for the repo. |
GET | /api/repos/{owner}/{repo}/{branch}/knowledge | The repo's knowledge bundle. |
GET | /api/repos/{owner}/{repo}/{branch}/fix-prs | Fix PRs opened for the repo. |
Register a repo
POST /api/repos returns 201 and auto-enqueues a discovery run for the new repo. That enqueue is minted on a separate thread, so its job id isn't knowable when the response is built and discover_job_id on the 201 is always null — don't build a poll around it. To find the run, list the repo's jobs a few seconds later: GET /api/repos/{owner}/{repo}/{branch}/jobs?workflow=discover. The row appears within a second or two but doesn't start until the next dispatch tick (~30s), so give it that long before concluding nothing happened. If it genuinely never appears you can submit discover yourself — but note the auto-enqueue goes through the same admission gates a manual submission does, so if it was refused for plan reasons (period attempt cap, exhausted plan credit, paused spend) the manual call will be refused identically. A manual retry only helps the transient cases. Set access_mode to "installed" (the default — Hyrax clones through your GitHub App installation) or "anonymous" for a public repo you haven't installed on.
Its refusals are worth handling explicitly, because two of them are plan limits rather than bad input:
403 permission_error— the repo would push you past your plan's repo count (code: "plan_repo_cap_exceeded") or its size cap (code: "plan_repo_size_exceeded", checked against the size GitHub reports; no plan sets a size cap today, so this code is not currently reachable). On theanonymouspath the count cap arrives instead as400withcode: "repo_cap_exceeded"— same limit, different shape, so branch on both.400 invalid_request_errorwithcode: "repo_no_longer_public"— ananonymousregistration whose repo GitHub won't serve publicly (private, renamed, or gone).409 conflictwithcode: "repo_already_registered"— thatowner/repo/branchtriple is already in the workspace. The body echoes the existing repo's name.503 server_error— Hyrax couldn't verify the repo or check your cap. Retry; this one may arrive without aRetry-After.
Observations
Observations are the findings and suggestions Hyrax produces. Each has a stable HYRAX-N ref, which is how you address one. See Findings & suggestions for the model and lifecycle.
| Method | Path | Description |
|---|---|---|
GET | /api/observations | List observations (filterable, paginated). |
GET | /api/observations/{ref} | Get one observation by HYRAX-N ref. |
POST | /api/observations/{ref}/fix | Spawn a fix job for the observation. |
POST | /api/observations/{ref}/dismiss | Dismiss the observation. |
POST | /api/observations/{ref}/reopen | Reopen a closed observation. |
POST | /api/observations/{ref}/complete | Mark the observation addressed (resolved outside a Hyrax PR) — closes it as completed. Body is optional: { "url": ..., "note": ... }, a proof link and a free-text annotation. |
POST | /api/observations/{ref}/acknowledge | Acknowledge the observation — stamps acknowledged_at; it stays open. No body. |
POST | /api/observations/{ref}/publish-ticket | Publish the observation to the repo's configured ticket tracker. 422 when the repo has no ticket integration. |
List observations
The list endpoint accepts these filters as query parameters:
| Filter | Values |
|---|---|
kind | finding, suggestion, or advisory |
status | new or closed |
priority | P0, P1, P2, P3 |
category | security, correctness, maintainability, performance, architecture, operations |
repo | A repo slug in repo@branch form (e.g. billing-service@main) to scope to one repo |
since | An ISO-8601 timestamp; returns observations updated after it |
close_reason | fixed, dismissed, completed, or expired. Only closed observations carry one, so setting it narrows to closed rows |
has_fix_pr | true keeps only observations Hyrax ever opened a fix PR for; false keeps only the rest. See the note under Fix an observation before combining it with status=new |
easy_wins | true returns only Easy Wins — open findings that are high-priority, cheap, and low-risk. Pair with kind=finding&status=new |
needs_review | true returns only findings that exhausted Hyrax's automatic fix attempts and now want a human. Pair with kind=finding&status=new |
domain | security, code_quality, reliability, data_apis, testing, devops, or ux — narrows to observations found by the audit tools that feed one score domain. A different axis from category, not a coarser one |
order | recency (default, newest first), display_rank for the same "top findings" order the app leads with, or resolved to order by when each was resolved (newest first) — the order the app's Completed view uses |
Add include=locations to embed each observation's file locations inline.
curl -s -G "https://api.hyrax.dev/api/observations" \
-d kind=finding \
-d status=new \
-d priority=P0 \
-d category=security \
-d repo=billing-service@main \
-d include=locations
Each observation carries its ref, kind, status, priority, category, title, and description; with include=locations, a locations array of { file_path, symbol, line_start, line_end }. List responses also carry next_cursor and has_more for pagination.
Fix an observation
Spawning a fix job does not close the observation. It moves to closed[fixed] only when the resulting pull request is merged.
One thing to know if you poll: once the fix attempt has a pull request, the observation drops out of GET /api/observations?status=new even though its status is still new — handed-off findings leave the active worklist and surface on {owner}/{repo}/{branch}/fix-prs instead. Watch that lane rather than treating the disappearance as a state change. Adding has_fix_pr=true won't bring it back: the hand-off exclusion still applies to a status=new query, so the two filters cancel out on exactly the rows you're looking for. has_fix_pr is for querying without status=new — it partitions closed observations into the ones Hyrax opened a PR for and the ones resolved another way. If the PR is closed unmerged, the observation reappears in the status=new list on its own.
curl -s -X POST "https://api.hyrax.dev/api/observations/HYRAX-42/fix"
The response carries the updated observation plus the ids of the spawned fix job and its attempt record:
{
"observation": { "ref": "HYRAX-42", "status": "new" },
"attempt_id": "...",
"job_id": "..."
}
Dismiss an observation
The body requires dismissal_category (a structured reason) and dismissal_note (free-text rationale, 10–500 characters, required). The category determines whether a later audit can resurface the observation:
- Stays closed on re-discovery:
false_positive,wont_fix,out_of_scope. - Can reappear if re-found:
stale,no_longer_found, and the catch-allother. (exhausted_fix_attemptsbehaves the same way but Hyrax sets it automatically — you won't normally submit it.)
curl -s -X POST \
"https://api.hyrax.dev/api/observations/HYRAX-42/dismiss" \
-H "Content-Type: application/json" \
-d '{
"dismissal_category": "wont_fix",
"dismissal_note": "Accepted risk for v1; tracked in our backlog."
}'
Reopen an observation
reopen_reason (free text, 1–2000 characters) is required.
curl -s -X POST \
"https://api.hyrax.dev/api/observations/HYRAX-42/reopen" \
-H "Content-Type: application/json" \
-d '{ "reopen_reason": "Regressed in the latest release." }'
Forecasts
Forecasts give you an estimated cost range for a workflow on a repo — the same number the app shows before you submit.
| Method | Path | Description |
|---|---|---|
GET | /api/repos/{owner}/{repo}/{branch}/forecasts | Cost forecasts for a repo across workflows. |
GET | /api/forecasts/lookup?workflow=&repo_name= | A single forecast for one workflow on one repo. |
workflow is required; repo_name (the repo@branch slug) is optional — omit it for a workspace-wide estimate.
curl -s -G "https://api.hyrax.dev/api/forecasts/lookup" \
-d workflow=audit \
-d repo_name=billing-service@main
The response is a cost band in USD (cost_low / cost_mid / cost_high) plus a wall-clock duration band in seconds (duration_low / duration_mid / duration_high), with a sample_size and a banded confidence. Use it to check a run fits your remaining credit before you submit.
See also
- API overview — authentication, errors, pagination, rate limits.
- Findings & suggestions — the observation model and lifecycle.
- Workflows — what each workflow does.