Welcome! This guide shows how to authenticate, submit scans, poll job status, and retrieve results (HTML, JSON, SARIF). All examples are ready to copy/paste.
Base URL & Auth
- Base URL:
https://api.craftedcybersolutions.com
- Auth header:
access-token: <YOUR_API_KEY>
Get an API key from the signup form on the main site, confirm your email, and keep the key safe.
Linux/macOS (bash)
# 1) set your key
export TOKEN="YOUR_API_KEY"
# 2) scan a public repo (HTTPS)
JOB=$(curl -s -H "access-token: $TOKEN" \
-X POST -d repo_url=https://github.com/pallets/flask \
https://api.craftedcybersolutions.com/v2/scan | jq -r .job_id)
# 3) poll until done
curl -s -H "access-token: $TOKEN" \
https://api.craftedcybersolutions.com/v2/status/$JOB | jq
# 4) fetch HTML report
curl -s -H "access-token: $TOKEN" \
-o /tmp/report.html https://api.craftedcybersolutions.com/v2/report/$JOB
# 5) fetch JSON report
curl -s -H "access-token: $TOKEN" \
"https://api.craftedcybersolutions.com/v2/report/$JOB?format=json" | jq
# 6) fetch SARIF 2.1.0
curl -s -H "access-token: $TOKEN" \
https://api.craftedcybersolutions.com/v2/report/$JOB/sarif | jq '.version, (.runs[0].results|length)'
Windows (PowerShell)
$env:TOKEN = "YOUR_API_KEY"
# Repo scan
$resp = Invoke-RestMethod `
-Headers @{ 'access-token' = $env:TOKEN } `
-Method Post -ContentType 'application/x-www-form-urlencoded' `
-Body @{ repo_url = 'https://github.com/pallets/flask' } `
-Uri 'https://api.craftedcybersolutions.com/v2/scan'
$job = $resp.job_id
# Poll
Invoke-RestMethod -Headers @{ 'access-token' = $env:TOKEN } `
-Uri "https://api.craftedcybersolutions.com/v2/status/$job"
# HTML report
$path = "$env:TEMP\scan-report.html"
Invoke-WebRequest -Headers @{ 'access-token' = $env:TOKEN } `
-Uri "https://api.craftedcybersolutions.com/v2/report/$job" -OutFile $path
Start-Process $path
Endpoints
1) Scan a public Git repo
POST /v2/scan
Content-Type: application/x-www-form-urlencoded
Header: access-token: <key>
Body: repo_url=https://example.com/owner/repo[.git]
Returns: { "job_id": "<uuid>" }
Notes:
- Public
https://
repositories only (no SSH). - The service performs a shallow clone (depth=1) and scans for secrets.
2) Upload a ZIP archive
POST /v2/scan/upload
Header: access-token: <key>
Body (multipart/form-data): archive=@/path/to/code.zip
Returns: { "job_id": "<uuid>" }
Notes:
- Use a standard
.zip
(no nested symlinks outside the archive; zip-slip is blocked). - Typical limit: ~50 MB archives (you’ll get 413 if too large).
- We scan the extracted files without relying on a Git history (
--no-git
mode).
(cd /path/to/project && zip -r /tmp/project.zip . -x '*.git*')
JOB=$(curl -s -H "access-token: $TOKEN" \
-F "archive=@/tmp/project.zip;type=application/zip" \
https://api.craftedcybersolutions.com/v2/scan/upload | jq -r .job_id)
3) Scan raw text/blobs
POST /v2/scan/text
Header: access-token: <key>
Content-Type: application/json
Body:
{
"content": "AWS_ACCESS_KEY_ID=AKIA... \nAWS_SECRET_ACCESS_KEY=....",
"filename": "secrets.env" // optional
}
Returns: { "job_id": "<uuid>" }
4) Check job status
GET /v2/status/{job_id}
Header: access-token: <key>
Example response:
{
"job_id": "…",
"status": "done", // queued | running | done | error
"severity_counts": { "low": 0, "med": 1, "high": 0, "crit": 0 },
"findings": 1
}
If status = error
, check the error
column (support can assist). Common messages include:
invalid_repo_url
clone_failed: …
(bad URL, removed/renamed repo, or credentials required)zip_slip_blocked
5) Retrieve reports
GET /v2/report/{job_id}
Header: access-token: <key>
GET /v2/report/{job_id}?format=json
Header: access-token: <key>
GET /v2/report/{job_id}/sarif
Header: access-token: <key>
Result schema (JSON)
Each finding is normalized with these fields:
{
"tool": "gitleaks", // scanner that found it
"rule_id": "github-pat", // detection rule
"severity": "med", // low | med | high | crit
"file": "path/to/file",
"line": 12,
"summary": "Short human summary",
"details": "Redacted or shortened secret value",
"ai_fix": "Safe remediation steps in plain English",
"false_positive_likelihood": 0.2
}
Severity & counts
We compute counts across low | med | high | crit
and return them in /v2/status/{job_id}
.
Rate limits & performance
- Rate limit: bursts are throttled (HTTP 429). Start with ≤5 requests/sec per client and back-off on 429s.
- Queueing: scans are background jobs; poll
/v2/status/{job_id}
untildone
. - Timeouts: large repos/archives take longer—poll every 2–5 seconds.
Security & privacy
- Secrets in output are redacted by default where the underlying tool supports it.
- We log request metadata (not bodies) and redact sensitive headers.
- If you accidentally upload real secrets, rotate them immediately.
- Need a data purge? Contact support with the job_id.
CI examples
GitHub Actions
name: Secrets Scan
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Zip workspace
run: zip -r /tmp/work.zip . -x '*.git*'
- name: Submit scan
env:
TOKEN: ${{ secrets.CCS_API_TOKEN }}
run: |
JOB=$(curl -s -H "access-token: $TOKEN" \
-F "archive=@/tmp/work.zip;type=application/zip" \
https://api.craftedcybersolutions.com/v2/scan/upload | jq -r .job_id)
echo "JOB=$JOB" >> $GITHUB_ENV
- name: Wait for result
env: { TOKEN: ${{ secrets.CCS_API_TOKEN }} }
run: |
for i in {1..120}; do
S=$(curl -s -H "access-token: $TOKEN" https://api.craftedcybersolutions.com/v2/status/$JOB | jq -r .status)
[ "$S" = "done" ] && break
[ "$S" = "error" ] && exit 1
sleep 3
done
- name: Download SARIF
env: { TOKEN: ${{ secrets.CCS_API_TOKEN }} }
run: |
curl -s -H "access-token: $TOKEN" \
https://api.craftedcybersolutions.com/v2/report/$JOB/sarif \
-o results.sarif
- name: Upload SARIF to GitHub
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif
Plain Bash script
#!/usr/bin/env bash
set -euo pipefail
TOKEN="${TOKEN:?Set TOKEN=your_api_key}"
zip -r /tmp/code.zip . -x '*.git*'
JOB=$(curl -s -H "access-token: $TOKEN" \
-F "archive=@/tmp/code.zip;type=application/zip" \
https://api.craftedcybersolutions.com/v2/scan/upload | jq -r .job_id)
until true; do
S=$(curl -s -H "access-token: $TOKEN" https://api.craftedcybersolutions.com/v2/status/$JOB | jq -r .status)
case "$S" in
done) break ;;
error) echo "Scan failed"; exit 1 ;;
esac
sleep 3
done
curl -s -H "access-token: $TOKEN" \
"https://api.craftedcybersolutions.com/v2/report/$JOB?format=json" | jq
Errors & troubleshooting
Symptom / Code | What it means | How to fix |
---|---|---|
401 Unauthorized | Missing/invalid access-token header | Send your API key header |
403 Quota Exceeded | Free plan monthly cap reached; | Wait for next month or upgrade |
405 Method Not Allowed | Wrong HTTP verb | Use POST for scans |
409 Conflict | Report not ready | Wait for status=done before fetching report |
413 Payload Too Large | ZIP exceeds limit | Reduce size or contact support |
422 Unprocessable Entity | Missing/invalid fields | Check body format |
429 Too Many Requests | Ratelimit | Back off and retry with jitter |
clone_failed: … | Bad repo URL, private repo, or removed repo | Use a public https:// URL that exists |
zip_slip_blocked | Unsafe archive paths | Re-create the ZIP without ../ paths |
Tip: Start with a known test repo like https://github.com/trufflesecurity/test-keys
to verify your pipeline.
Usage & Limits
Check your usage (per key/owner)
Endpoints
GET /v2/usage/self
→ totals and recent activityGET /v2/usage/self/monthly?months=12
→ per-month breakdown (last N months)
Linux/macOS (bash)
# Current usage
curl -s -H "access-token: $TOKEN" \
https://api.craftedcybersolutions.com/v2/usage/self | jq
# Monthly breakdown (last 6 months)
curl -s -H "access-token: $TOKEN" \
"https://api.craftedcybersolutions.com/v2/usage/self/monthly?months=6" | jq
Windows (PowerShell)
# Current usage
Invoke-RestMethod -Headers @{ 'access-token' = $env:TOKEN } `
-Uri 'https://api.craftedcybersolutions.com/v2/usage/self' | ConvertTo-Json
# Monthly breakdown
Invoke-RestMethod -Headers @{ 'access-token' = $env:TOKEN } `
-Uri 'https://api.craftedcybersolutions.com/v2/usage/self/monthly?months=6' | ConvertTo-Json
Response examples
{
"owner": "[email protected]",
"total_jobs": 34,
"jobs_last_24h": 3,
"jobs_last_7d": 34,
"last_job_at_epoch": 1755094117,
"last_job_at_iso": "2025-08-13T14:08:37+00:00",
"cache_ttl_seconds": 300
}
[
{ "month": "2025-08", "jobs": 34 },
{ "month": "2025-07", "jobs": 12 }
]
Batch Scans
Endpoint
POST /v2/scan/batch
- Header:
access-token: <key>
- Body: JSON
{ "repos": ["https://github.com/org/repo.git", ...] }
(up to ~50)
Linux/macOS (bash)
curl -s -H "access-token: $TOKEN" -H "Content-Type: application/json" \
-d '{"repos":["https://github.com/pallets/flask.git","https://github.com/psf/requests.git"]}' \
https://api.craftedcybersolutions.com/v2/scan/batch | jq
Windows (PowerShell)
$body = @{ repos = @(
'https://github.com/pallets/flask.git',
'https://github.com/psf/requests.git'
)} | ConvertTo-Json
Invoke-RestMethod -Headers @{ 'access-token' = $env:TOKEN } `
-Method Post -ContentType 'application/json' -Body $body `
-Uri 'https://api.craftedcybersolutions.com/v2/scan/batch'
Returns
{ "jobs": {
"https://github.com/pallets/flask.git": "job-uuid-1",
"https://github.com/psf/requests.git": "job-uuid-2"
}}
Triage (noise reduction)
Add triage=true
to JSON reports to down-rank obvious noise (playlist/JWT placeholders, low-entropy tokens, etc.).
Example
curl -s -H "access-token: $TOKEN" \
"https://api.craftedcybersolutions.com/v2/report/$JOB?format=json&triage=true" \
| jq 'group_by(.triage) | map({triage: .[0].triage, count: length})'
Triage labels
likely-fp
— obvious false positives (severity auto-downgraded tolow
)needs-review
— everything else
Rate Limits & Quotas
Per-minute throttling (burst control)
- Requests to
POST /v2/scan
andPOST /v2/scan/batch
are throttled per key/owner. - If exceeded:
429 Too Many Requests
with body:
{ "detail": "rate limit: 20/60s" }
- (Limit may vary; back off with jitter and retry.)
Monthly quota (free plan)
- Free plan: 30 scans per calendar month (per owner).
- On first exceed this month:
- You’ll receive an email notification,
- Your account is paused for the remainder of the month,
- API returns:
{ "detail": "quota exceeded: account paused until month-end", "limit_month": 30 }
-
- with HTTP 403.
- Premium and admin are not subject to the monthly quota.
- Auto-unpause at the start of a new UTC month.
How to monitor your usage
- Use
GET /v2/usage/self
andGET /v2/usage/self/monthly
(examples above).
Need higher limits or Premium access? Use the signup/contact on the main site and mention your key/email.
OpenAPI & Docs
- Live ReDoc (public routes only):
https://api.craftedcybersolutions.com/api-docs
- Filtered OpenAPI JSON (public):
https://api.craftedcybersolutions.com/openapi-public.json
Note: Only
/signup*
and/v2/*
appear in the public schema; internal/admin routes are not exposed.