Skip to main content

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.

MethodPathDescription
POST/api/repos/{owner}/{repo}/{branch}/jobsSubmit a job for a repo.
GET/api/repos/{owner}/{repo}/{branch}/jobsList a repo's jobs (paginated).
GET/api/jobsList all jobs across the workspace (paginated).
GET/api/jobs/{job_id}Get a single job's detail.
POST/api/jobs/{job_id}/cancelCancel a pending or running job. REST-only — not exposed as an MCP tool.
POST/api/jobs/{job_id}/retryRetry 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.

MethodPathDescription
GET/api/reposList repos in the workspace (paginated).
POST/api/reposRegister 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}/resetReset 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}/discoveryDiscovery files for the repo.
GET/api/repos/{owner}/{repo}/{branch}/knowledgeThe repo's knowledge bundle.
GET/api/repos/{owner}/{repo}/{branch}/fix-prsFix 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 the anonymous path the count cap arrives instead as 400 with code: "repo_cap_exceeded" — same limit, different shape, so branch on both.
  • 400 invalid_request_error with code: "repo_no_longer_public" — an anonymous registration whose repo GitHub won't serve publicly (private, renamed, or gone).
  • 409 conflict with code: "repo_already_registered" — that owner/repo/branch triple 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 a Retry-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.

MethodPathDescription
GET/api/observationsList observations (filterable, paginated).
GET/api/observations/{ref}Get one observation by HYRAX-N ref.
POST/api/observations/{ref}/fixSpawn a fix job for the observation.
POST/api/observations/{ref}/dismissDismiss the observation.
POST/api/observations/{ref}/reopenReopen a closed observation.
POST/api/observations/{ref}/completeMark 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}/acknowledgeAcknowledge the observation — stamps acknowledged_at; it stays open. No body.
POST/api/observations/{ref}/publish-ticketPublish 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:

FilterValues
kindfinding, suggestion, or advisory
statusnew or closed
priorityP0, P1, P2, P3
categorysecurity, correctness, maintainability, performance, architecture, operations
repoA repo slug in repo@branch form (e.g. billing-service@main) to scope to one repo
sinceAn ISO-8601 timestamp; returns observations updated after it
close_reasonfixed, dismissed, completed, or expired. Only closed observations carry one, so setting it narrows to closed rows
has_fix_prtrue 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_winstrue returns only Easy Wins — open findings that are high-priority, cheap, and low-risk. Pair with kind=finding&status=new
needs_reviewtrue returns only findings that exhausted Hyrax's automatic fix attempts and now want a human. Pair with kind=finding&status=new
domainsecurity, 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
orderrecency (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-all other. (exhausted_fix_attempts behaves 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.

MethodPathDescription
GET/api/repos/{owner}/{repo}/{branch}/forecastsCost 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