The same API that powers npx pqcheck, the browser extension, and the GitHub Action. Free, no signup, no API key. Rate-limited at ~60 requests/minute per IP for cost protection.
Scan any HTTPS domain — returns a complete JSON report with score, grade, findings, and component breakdown. Try it from your terminal:
# Quick scan curl -s "https://www.cipherwake.io/api/scan?domain=stripe.com" | jq '.grade, .score' # Or use the CLI wrapper (also free, also no signup) npx pqcheck stripe.com
Both call the same endpoint. Pick whichever fits your workflow.
All endpoints live under https://www.cipherwake.io. Responses are JSON unless noted. CORS-enabled (Access-Control-Allow-Origin: *) so you can call directly from browser code.
| Endpoint | Method | Purpose | Returns |
|---|---|---|---|
/api/scan?domain=<d> free |
GET | Full Decryption Blast Radius scan | JSON — score, grade, findings, components, public surface details |
/api/history?domain=<d>&days=N free |
GET | Score history (default 90 days) | JSON — array of {score, grade, recordedAt} |
/api/badge?domain=<d> free |
GET | Embeddable SVG badge | SVG image |
/api/r?domain=<d> free |
GET | Full HTML report (shareable URL) | HTML |
/api/og?domain=<d> free |
GET | Social-media OG image (Twitter/Slack/LinkedIn unfurl) | PNG |
/api/feed.xml free |
GET | RSS/Atom feed (recent scans, watchlists, methodology updates) | RSS XML |
/api/sitemap.xml free |
GET | SEO sitemap | XML |
/api/scanThe main endpoint. Pass a domain, get a complete report.
GET /api/scan?domain=stripe.com&source=my-tool Optional query params: domain required — public HTTPS domain (apex; we strip leading "www.") source optional — for analytics attribution. Examples: ext, my-tool force optional — pass "1" to bypass our 5-min server-side cache (debug)
{
"domain": "stripe.com",
"score": 5.5,
"grade": "C",
"scoreLabel": "MEDIUM",
"reachable": true,
"impact": {
"headline": "If quantum decryption arrives in 2030-2040, harvested traffic...",
"topContributors": [...]
},
"findings": [
{ "title": "RSA fallback enabled", "severity": "high", "detail": "..." },
{ "title": "HSTS preload increases key persistence", "severity": "medium", "detail": "..." }
],
"components": {
"keyExchange": { "raw": 7, "weight": 0.35, "contribution": 2.45, "rationale": "..." },
"certLifetime": { "raw": 5, "weight": 0.25, "contribution": 1.25, "rationale": "..." },
"keyPersistence":{ "raw": 7, "weight": 0.15, "contribution": 1.05, "rationale": "..." },
"subdomainScale":{ "raw": 3, "weight": 0.25, "contribution": 0.75, "rationale": "..." }
},
"publicSurface": {
"tlsVersion": "TLSv1.3",
"hybridPQC": false,
"daysUntilCertExpiry": 54,
"wildcardCert": false,
/* ... full TLS + cert + headers + email-security details ... */
}
}
See methodology for what each component measures and how the score is computed. The full response is documented in /methodology/decryption-blast-radius.
Same call in your stack of choice:
# Just the grade curl -s "https://www.cipherwake.io/api/scan?domain=stripe.com" | jq -r '.grade' # Score + top finding titles, formatted curl -s "https://www.cipherwake.io/api/scan?domain=stripe.com" \ | jq '{ grade, score, top: .findings[0:3] | map(.title) }' # Fail a CI step if score >= 7 SCORE=$(curl -s "https://www.cipherwake.io/api/scan?domain=$DOMAIN" | jq -r '.score') awk -v s="$SCORE" 'BEGIN { exit !(s+0 >= 7) }' && exit 1
// Node 18+ (built-in fetch)
const resp = await fetch(`https://www.cipherwake.io/api/scan?domain=stripe.com`);
const report = await resp.json();
console.log(`${report.domain}: grade ${report.grade}, score ${report.score}`);
report.findings.forEach(f => console.log(` [${f.severity}] ${f.title}`));
# Python 3.7+ (requests) import requests r = requests.get("https://www.cipherwake.io/api/scan", params={"domain": "stripe.com"}) report = r.json() print(f"{report['domain']}: {report['grade']} ({report['score']}/10)") for f in report["findings"][:5]: print(f" [{f['severity']}] {f['title']}")
// Go 1.18+ resp, _ := http.Get("https://www.cipherwake.io/api/scan?domain=stripe.com") defer resp.Body.Close() var report struct { Domain string `json:"domain"` Grade string `json:"grade"` Score float64 `json:"score"` } json.NewDecoder(resp.Body).Decode(&report) fmt.Printf("%s: %s (%.1f/10)\n", report.Domain, report.Grade, report.Score)
# Pre-built wrapper around the same API. Zero install. npx pqcheck stripe.com # pretty terminal output npx pqcheck stripe.com --format json # raw JSON npx pqcheck stripe.com --threshold 7 # exit 2 if score >= 7 npx pqcheck stripe.com --format sarif # SARIF for GitHub Code Scanning npx pqcheck deps stripe.com --lock # scan all third-party dependencies npx pqcheck history stripe.com # 90-day score history npx pqcheck diff old.lock new.lock # diff QXM lockfiles for regressions # Source on npm: https://www.npmjs.com/package/pqcheck
?force=1 bypasses the smart-cache so it's more expensive; lower cap here. Plenty for legitimate iterative work on your own domain?force=1 to bypass when testing your own changes429 with a JSON {"error": "rate_limit_exceeded", "detail": "...", "need_more": {...}}. Back off and retry; or tell us via the feedback form if you need higher limits — we're prioritizing the API tier based on real demand signals/api/v2/scan with a deprecation timeline for v1?source= query param. We do not log query strings beyond domain and source, and we don't track individual users across scansAPI keys (CIPHERWAKE_API_KEY=qpk_<hex>) authenticate paid-tier usage and bind it to your account's monthly quota. Treat them like passwords.
secrets.CIPHERWAKE_API_KEY in GitHub Actions; .env.local ignored in .gitignore for local dev). GitHub scans pushes for known secret prefixes and will alert you on accidental commit, but don't rely on that.If a key was exposed publicly (e.g., committed and pushed), rotate immediately and email security@cipherwake.io with the exposure window. We can audit usage to see whether the key was abused before you rotated.
Alert webhooks deliver a signed JSON POST to a URL you configure in /account. Every delivery is signed so you can verify the request actually came from Cipherwake before acting on it.
POST <your-webhook-url> HTTP/1.1
Host: your-server.example.com
User-Agent: cipherwake-webhook/1.0
Content-Type: application/json
X-Cipherwake-Signature: sha256=<hex-hmac>
X-Cipherwake-Event-Id: <alert-uuid>
X-Cipherwake-Delivery: <attempt-number>
{
"alertId": "...",
"domain": "example.com",
"severity": "high",
...
}
Each delivery includes an X-Cipherwake-Signature: sha256=<hex> header. The hex value is HMAC-SHA256 of the raw request body using a secret you specify when configuring the webhook. Always verify the signature before acting on the payload — without verification, anyone who guesses your URL could send fake alerts.
Reference verification (Node.js):
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyCipherwakeSignature(rawBody, signatureHeader, secret) {
if (!signatureHeader?.startsWith("sha256=")) return false;
const provided = Buffer.from(signatureHeader.slice(7), "hex");
const expected = createHmac("sha256", secret).update(rawBody).digest();
if (provided.length !== expected.length) return false;
return timingSafeEqual(provided, expected);
}
// In your webhook handler — verify on the RAW body before JSON.parse:
const sig = req.headers["x-cipherwake-signature"];
const rawBody = await readRawBody(req); // your framework's raw-body utility
if (!verifyCipherwakeSignature(rawBody, sig, process.env.CIPHERWAKE_WEBHOOK_SECRET)) {
return res.status(401).json({ error: "invalid signature" });
}
const payload = JSON.parse(rawBody.toString("utf8"));
X-Cipherwake-Event-Id header carries a UUID that's stable for the alert. If your handler sees the same event ID twice, it's a retry — handle idempotently (cache last-seen IDs for ~24h).X-Cipherwake-Delivery increments on each retry attempt for the same event. First delivery is 1.Generate a strong random secret (32+ bytes / 256+ bits) and configure it in /account → Alerts → Webhook URL. Never reuse a secret across multiple integrations. If your secret is exposed (e.g., copy-pasted into a chat), rotate it in /account — old signatures stop verifying immediately.
Don't want to call the API directly? We ship four clients that wrap it. All free, all open about what they do.
npx pqcheckZero-install command-line scanner. Same API + nicer output + lockfile / diff / SARIF / cert-analysis subcommands.
npx pqcheck <domain> — single scan, pretty outputnpx pqcheck deps <domain> — supply-chain HNDL: scan all third-party origins on the pagenpx pqcheck lock <domain> — generate cipherwake.lock (QXM committable manifest)npx pqcheck diff old.lock new.lock — diff two QXM lockfiles, exit 2 on regressionnpx pqcheck history <domain> — 90-day score history with sparklinenpx pqcheck cert <file.pem> — analyze a local PEM/CRT cert offlinenpx pqcheck --file domains.txt — bulk scan from a list--format json / markdown / csv / sarif / --gh-action--threshold 7 (exit 2), pqcheck deps --allowlist file (exit 3 on violation)Source: github.com/cipherwake-io/pqcheck · npm: pqcheck · full reference: npx pqcheck --help
Live HNDL grade for every HTTPS site you visit + supply-chain change detection. Color-coded toolbar badge updates per tab; click for the full report.
Install from Chrome Web Store → Firefox AMO + Edge Add-ons coming next.
Drop into any workflow to gate PRs on quantum-decryption risk regressions.
# .github/workflows/pqcheck.yml - uses: cipherwake-io/pqcheck/action@main with: domain: mycompany.com threshold: '7' # exit 2 if score >= 7 comment-on-pr: 'true' # sticky PR comment with summary generate-sarif: 'true' # write SARIF for upload to Code Scanning generate-lockfile: 'true' # write cipherwake.lock for diffing # Optional follow-on step — surface findings in GitHub Security tab - uses: github/codeql-action/upload-sarif@v3 with: sarif_file: pqcheck-results.sarif
Source: github.com/cipherwake-io/pqcheck/tree/main/action
If you ship something that uses our API — internal dashboard, Grafana panel, Datadog integration, vendor-risk pipeline, anything — we'd love to know. hello@cipherwake.io.