Skip to content

Developers

The Hardn REST API. Scan from CI.

Register sites, prove ownership, start scans and read findings as JSON. The same ownership rules and credits as the app.

Your first scan is free, no card needed. You prove you own the site before Hardn touches it.

Authentication

Create a key in the app under API keys (owners and admins). Send it as a bearer token. Keys start with hk_live_, belong to one organisation, and are shown only once.

Header
Authorization: Bearer hk_live_…
Base URLhttps://api.hardn.si/v1
Site idstgt_…
Scan idsasm_…
TimesISO-8601, UTC

Quick start

Four calls from a new site to its report.

  1. 1. Register your site

    bash
    curl -s https://api.hardn.si/v1/targets \  -H "Authorization: Bearer $HARDN_KEY" -H "Content-Type: application/json" \  -d '{"url": "https://app.example.com", "kind": "website"}'
  2. 2. Add the DNS record from instructions.dns_txt, then verify

    bash
    curl -s -X POST https://api.hardn.si/v1/targets/tgt_…/verify -H "Authorization: Bearer $HARDN_KEY"
  3. 3. Start a scan and poll it

    bash
    curl -s https://api.hardn.si/v1/scans -H "Authorization: Bearer $HARDN_KEY" -H "Content-Type: application/json" \  -d '{"target_id": "tgt_…", "scan_type": "public"}'curl -s https://api.hardn.si/v1/scans/asm_… -H "Authorization: Bearer $HARDN_KEY"
  4. 4. Read the report

    bash
    curl -s "https://api.hardn.si/v1/scans/asm_…/report?format=json" -H "Authorization: Bearer $HARDN_KEY"

Routes

Every route is scoped to the key's organisation.

REST API routes
RouteBody → result
POST/targets{"url": "https://app.example.com", "kind": "website"}201 — the site, with verification_token and instructions (DNS and file).
GET/targetsYour organisation's sites.
POST/targets/{id}/verify{"status", "verified", "detail"} — runs the DNS or file check now.
POST/targets/{id}/accounts{"label", "level", "tenant", "auth_type", "secret", "header_name"}201 — a deep-scan test account. Secrets are write-only.
POST/scans{"target_id": "tgt_…", "scan_type": "public"}201 — the scan, queued. 409 target_unverified, 402 insufficient_credits.
GET/scans/{id}Status, progress, and when completed: score, grade and counts.
GET/scans/{id}/findingsEvery finding with severity, evidence and the fix.
GET/scans/{id}/report?format=json|htmlThe full report and fix report.

Errors

Errors are JSON: {"detail": {"code": "…", "message": "…"}}.

Status and codeWhat it means
401 unauthenticatedMissing, wrong or revoked API key.
402 insufficient_creditsThe organisation has no scan credits left.
404 not_foundThe id doesn't exist or belongs to another organisation.
409 target_unverifiedOwnership hasn't been proved, or the proof has gone.
422 validation_errorThe body is invalid; errors lists each field.
429 rate_limitedSlow down and retry after a moment.

Use it in CI

Store the key as a secret, start a scan after each deploy, and fail the job if the grade drops below what you accept.

.github/workflows/hardn.yml (excerpt)
- name: Hardn scan  env: { HARDN_KEY: ${{ secrets.HARDN_KEY }} }  run: |    ID=$(curl -s https://api.hardn.si/v1/scans -H "Authorization: Bearer $HARDN_KEY" \      -H "Content-Type: application/json" \      -d '{"target_id": "tgt_…", "scan_type": "public"}' | jq -r .id)    while :; do      SCAN=$(curl -s https://api.hardn.si/v1/scans/$ID -H "Authorization: Bearer $HARDN_KEY")      case "$(echo "$SCAN" | jq -r .status)" in queued|running) sleep 15;; *) break;; esac    done    GRADE=$(echo "$SCAN" | jq -r .grade)    case "$GRADE" in A|B) ;; *) echo "Hardn grade $GRADE"; exit 1;; esac

Create a free account to get a key.

Scan from your pipeline this week

Add your site, prove you own it, and Hardn runs 27 read-only checks. You get a grade, every issue it found and the steps to fix each one. Your first scan is free.