Skip to content
SECQON

API documentation

Everything the dashboard does, your pipeline can do. Add an asset, launch a scan, pull findings, download the report — with a scoped key that cannot do more than you granted.

The specification is generated from the running service, so it is never out of date with the API it describes. Point any OpenAPI client generator at that URL.

Authentication

Every request carries a bearer token. An API key looks like secqon_<prefix>_<secret> and is shown once, when you create it — we store only a hash, so a lost key is replaced rather than recovered.

curl https://api.secqon.com/assets \
  -H "Authorization: Bearer secqon_a1b2c3_your_secret_here"

Keys are scoped, and a key is refused on any path no scope covers. There is deliberately no scope for team administration, billing, plan changes, legal acceptance, or minting another key — those are things a person does with an account behind them, not things a build server should do at 3am with a leaked variable.

ScopeGrants
assets:readList assets and their verification status.
assets:writeAdd an asset and start or check ownership verification.
scans:readList scans, read status and progress.
scans:runLaunch a scan and cancel one.
findings:readRead findings and their evidence.
findings:writeChange a finding's triage status.
reports:readList reports and download a PDF.
reports:writeGenerate a report for a completed scan.

Quickstart: scan an asset and read the findings

An asset must be verified before it can be scanned — that rule has no exception and no override. Verification is a DNS TXT record, a hosted file, or a meta tag.

1. Add the asset

curl -X POST https://api.secqon.com/assets \
  -H "Authorization: Bearer $SECQON_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"domain","value":"example.com","attest_authorized":true}'

2. Start verification, then check it

curl -X POST https://api.secqon.com/assets/$ASSET_ID/verify/start \
  -H "Authorization: Bearer $SECQON_KEY" \
  -H "Content-Type: application/json" -d '{"method":"dns_txt"}'

# publish the token it returns, then:
curl -X POST https://api.secqon.com/assets/$ASSET_ID/verify/check \
  -H "Authorization: Bearer $SECQON_KEY"

3. Launch a scan

curl -X POST https://api.secqon.com/scans \
  -H "Authorization: Bearer $SECQON_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "asset_ids": ["'$ASSET_ID'"],
        "profile": "standard",
        "attest_authorized": true
      }'

profile is light, standard or deep. A Deep scan also needs "deep_ack": true — it is intrusive, and the acknowledgement is recorded in your audit log. The full penetration test adds "pentest_ack": true and is available on Business.

4. Poll for completion, then read findings

curl https://api.secqon.com/scans/$SCAN_ID -H "Authorization: Bearer $SECQON_KEY"
# {"status":"completed","progress":100, ...}

curl https://api.secqon.com/scans/$SCAN_ID/findings -H "Authorization: Bearer $SECQON_KEY"

5. Generate and download the report

curl -X POST https://api.secqon.com/reports \
  -H "Authorization: Bearer $SECQON_KEY" \
  -H "Content-Type: application/json" -d '{"scan_id":"'$SCAN_ID'"}'

curl -L https://api.secqon.com/reports/$REPORT_ID/pdf \
  -H "Authorization: Bearer $SECQON_KEY" -o report.pdf

Using it in CI

A common pattern: scan on every release, and fail the build when a new high-severity finding appears. Findings carry a stable identity across scans, so “new” means new rather than re-reported.

- name: SecQon scan
  run: |
    SCAN=$(curl -sS -X POST $SECQON_API/scans \
      -H "Authorization: Bearer ${{ secrets.SECQON_KEY }}" \
      -H "Content-Type: application/json" \
      -d '{"asset_ids":["'$ASSET'"],"profile":"standard","attest_authorized":true}' \
      | jq -r .id)

    until [ "$(curl -sS $SECQON_API/scans/$SCAN \
        -H "Authorization: Bearer ${{ secrets.SECQON_KEY }}" | jq -r .status)" = "completed" ]; do
      sleep 20
    done

    HIGH=$(curl -sS $SECQON_API/scans/$SCAN/findings \
      -H "Authorization: Bearer ${{ secrets.SECQON_KEY }}" \
      | jq '[.[] | select(.severity=="high" or .severity=="critical")] | length')

    [ "$HIGH" -eq 0 ] || { echo "::error::$HIGH high-severity findings"; exit 1; }

Rate limits and errors

Something missing from these docs? Tell us — the API is meant to be driven by people who never open the dashboard.