← back to skillsmith

API Reference

Base URL: https://skillsmith.ch (mirrored at https://skillsmith-web.vercel.app). All responses are JSON. No SDK needed -- plain HTTP.

Pricing tiers

TierScans/dayDB lookups/dayPrice
Free55$0
Pay-as-you-go (additive)+1 per purchase+1 per purchase$0.02 / scan, $0.01 / lookup
Pro100150$5 USDC / 30 days
PremiumUnlimitedUnlimited$10 USDC / 30 days

All paid tiers are activated by sending USDC (SPL token, Solana mainnet, mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) to 2esJogvKTYDuxZaB9PEuEaHvz4U6TuQnTx3pkLcdH34N, then submitting the finalized transaction signature. Pay-as-you-go always requires an existing account (api_key) and is additive to whatever tier you're on -- it never replaces the free daily quota, it tops it up. No card, no subscription billing surprises -- Pro/Premium simply expire after 30 days.

"Database" here means the Safe Skills Database -- looking up a hash's verdict (/api/lookup), browsing clean skills (/api/registry), or fetching a published skill's actual, usable content (/api/skill). All three share the same daily lookup quota.

Authentication

Every scan/lookup call needs an api_key, either as a JSON body field, a query param (GET requests), or an Authorization: Bearer <key> header. Get one from POST /api/signup or by signing in with GitHub (/api/auth/github/start), which recovers the same key/quota on any device you sign in from again.

MCP server (for AI agents)

skillsmith runs an MCP server at https://skillsmith.ch/mcp (JSON-RPC 2.0 over HTTP) so Claude Code, Cursor, Codex, or any MCP-compatible agent can scan and reuse skills directly, without shelling out to curl. Same detection engine, same accounts/quota as the REST API and the web UI -- one account works everywhere.

Connect your agent

{
  "mcpServers": {
    "skillsmith": {
      "type": "http",
      "url": "https://skillsmith.ch/mcp"
    }
  }
}

Claude Code: add the above to .mcp.json, run /mcp, select "skillsmith". Cursor/Codex/other MCP hosts: paste the same JSON into their MCP config panel.

Get an api_key first

Every tool call needs an api_key argument. Call the skillsmith_signup tool once (free, no email) and reuse the key for every other call -- or ask your agent to "sign up for skillsmith and remember the key."

Tools

ToolArgumentsDoes
skillsmith_signup(none)Create an account, get an api_key
scan_skillapi_key, text or url, publish?Lint + security-scan a SKILL.md
lookup_hashapi_key, sha256Check if a hash was scanned before
get_skill_contentapi_key, sha256Fetch a published skill's actual text
list_safe_skillsapi_key, limit?Browse the Safe Skills Database
file_reportapi_key, sha256, verdict (malicious | false_positive | note), comment?File a community verdict report (20/day/key, shown publicly)
find_similarapi_key, sha256Skill-DNA near-duplicate search (≤12 Hamming distance, unpublished names masked)
watch_skillapi_key, url (create) | watch_id (check)Rug-pull watch: baseline a GitHub-hosted SKILL.md and re-check whether it changed after you vetted it (10 watches/day/key)
whoamiapi_keyCheck tier/quota usage
analyze_behaviorapi_key, textBehavioral sandbox: an AI analyst simulates what an agent following the skill would do (capabilities, action trace, IOCs, 0-10 severity). Slow (~30-120s); 5/day/account.

Raw JSON-RPC example (what your MCP host does under the hood):

curl -X POST https://skillsmith.ch/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"skillsmith_signup","arguments":{}}}'

Endpoints

GET/health

Liveness probe. Returns {"status": "ok", ...} with HTTP 200; no auth, no quota.

GET/badge?sha256=...&style=flat

SVG trust badge for a scanned hash (unlimited, no auth, cached 60s). Optional style: flat (default), flat-square, round. Colors follow the verdict: green (clean), amber (low), orange (medium), red (high). Usage: <img src="https://skillsmith.ch/badge?sha256=..."> in your README.

GET/feed.xml

Atom feed of recently verified-clean agent skills (automated heuristic verdicts, clean-gated). No auth; CDN-cached 5 minutes + 60s server-side micro-cache. Subscribe to stay current on new registry entries.

GET/api/public_scan?sha256=...

Key-less verdict lookup for one hash — enough for badge landing pages: {sha256, name, risk_level, risk_score, lint_ok, parse_ok, seen_count, first_seen_at, last_seen_at, has_content}. Rate limited (200/day/IP, soft). No findings, no content.

GET/api/certificate?sha256=...&api_key=...

Issue an HMAC-signed verdict certificate for a scanned hash (valid 90 days). Response: {"certificate": {sha256, risk_level, security_score, issued_at, signature}}. Requires sign-in.

POST/api/certificate

Verify a certificate. Body: {"certificate": {...}} — no auth needed to verify. Returns {valid, matches_current_verdict, current_risk_level}: valid means signed by skillsmith within its 90-day window; matches_current_verdict tells you whether the hash still carries that verdict today.

GET/api/report?sha256=...

GET/api/report?sha256=...

Public community verdicts for a hash: {"total": n, "tally": {"malicious": x, "false_positive": y, "note": z}, "reports": [...]}.

POST/api/report

File a community verdict. Body: {api_key, sha256, verdict, comment?} where verdict is false_positive | malicious | note. Requires a valid api_key; 20 reports/day/key.

GET/api/similar?sha256=...&api_key=...

Skill-DNA neighbours: near-duplicate variants of the skill (64-bit simhash, hamming distance ≤ 12). Requires sign-in; free.

POST/api/hook-scan

Run the normal scan pipeline and get back a ready-to-post Discord or Slack webhook payload. Body: {api_key, url | text, format?} — url must be a github.com/raw.githubusercontent.com link; format is "discord" (default) or "slack". Consumes one scan from your quota. Response: a Discord embed object or Slack attachment object (plus a disclaimer field).

POST/api/watch

Watch a GitHub-hosted SKILL.md for rug-pulls. Body: {api_key, url, webhook_url?} where url is a github.com blob or raw.githubusercontent.com URL (only those hosts are fetched — no SSRF). Optional webhook_url: a Discord (https://discord.com/api/webhooks/...) or Slack (https://hooks.slack.com/services/...) webhook that automatically receives a rug-pull alert when the content changes (provider-allowlisted, best-effort). Stores the content hash as baseline. 10 watches/day/key. Response: {watch_id, baseline_sha256}.

GET/api/watch?watch_id=...&api_key=...

On-demand rug-pull check: re-fetches the watched URL and compares the hash. Status is unchanged, changed (= the skill was modified after you vetted it) or unreachable. Response includes baseline/current sha256, check count and timestamps.

DELETE/api/watch?watch_id=...&api_key=...

Remove a watch you own (hard delete incl. all stored versions; foreign/unknown ids return 404 without oracle). Shares the 200/day watch-operation cap.

List your watches: GET /api/watch?list=1&api_key=... returns all watches owned by your key ({count, watches:[{watch_id,url,last_status,checks,created_at,changed_at}]}, max 100; 20 lists/day/key).

GET/api/stats

Aggregate global counters: {"total_scans": n, "by_risk": {...}}. Public, cached 60s.

POST/api/signup

Create an anonymous account. No email/password.

curl -X POST https://skillsmith.ch/api/signup
{"api_key": "sk_...", "free_daily_limit": 5, "pro_daily_limit": 100, "pro_price_usdc": 5.0, "pro_duration_days": 30}

GET/api/signup?api_key=...

Check your account's current tier and quota usage.

curl "https://skillsmith.ch/api/signup?api_key=sk_..."
{"tier": "free", "limit": 5, "used": 2, "remaining": 3, "pro_price_usdc": 5.0, "pro_duration_days": 30}

GET/api/auth/github/start

Redirects to GitHub OAuth. On success, redirects back to /#key=sk_... with your (possibly pre-existing) account key.

POST/api/scan

Lint + security-scan one SKILL.md. Consumes 1 unit of your scan quota.

curl -X POST https://skillsmith.ch/api/scan \
  -H "Content-Type: application/json" \
  -d '{"api_key":"sk_...","text":"---\nname: x\ndescription: y\n---\n\nbody"}'

# or scan a GitHub link instead of pasting text:
curl -X POST https://skillsmith.ch/api/scan \
  -d '{"api_key":"sk_...","url":"https://github.com/owner/repo/blob/main/SKILL.md"}'

Response includes parse_ok, lint_ok, lint_issues[], findings[], risk_score, risk_level, security_score (0-100), sha256, scan_history, disclaimer, and quota. For a hash scanned before, the response also carries trend (direction: improved/declined/unchanged, delta vs. the previous scan, previous_security_score) and explanation[] — plain-language translations of each finding with advice.

POST/api/scan_pro

Two things, based on what you send:

1. Activate Pro or Premium:

curl -X POST https://skillsmith.ch/api/scan_pro \
  -d '{"api_key":"sk_...","activate_payment_signature":"<tx>"}'
# add "tier":"premium" (and send $10 instead of $5) for the unlimited tier

2. Batch-scan up to 25 files (once Pro/Premium is active; consumes 1 unit of your scan quota per call, not per file):

curl -X POST https://skillsmith.ch/api/scan_pro \
  -d '{"api_key":"sk_...","files":[{"name":"a/SKILL.md","text":"..."},{"name":"b/SKILL.md","text":"..."}]}'

GET/api/lookup?sha256=...&api_key=...

VirusTotal-style hash lookup: has this exact file been scanned before? Requires sign-in; consumes 1 unit of your daily DB-lookup quota (separate from the scan quota -- see pricing table above).

curl "https://skillsmith.ch/api/lookup?sha256=4807c1a3de60...&api_key=sk_..."
{"found": true, "record": {"sha256": "...", "name": "date-formatter", "seen_count": 3, "risk_level": "clean", ...}, "quota": {...}}

GET/api/registry?api_key=...&limit=50

Browse the public Safe Skills Database: skills that scanned clean + lint-ok, newest first. Each entry has a has_content flag -- true if the submitter published the actual content (see below). Also consumes 1 unit of your DB-lookup quota. Explicitly an automated-heuristic result, not a manual audit.

curl "https://skillsmith.ch/api/registry?api_key=sk_..."

GET/api/skill?sha256=...&api_key=...

Fetch the actual, usable SKILL.md content for a hash -- the "use the skill" endpoint, distinct from /api/lookup (verdict/metadata only). Only works if the submitter explicitly published it by ticking "Publish to Safe Skills DB" on a clean scan (POST /api/scan with "publish": true). Returns 404 if not published. Consumes 1 unit of your DB-lookup quota.

curl "https://skillsmith.ch/api/skill?sha256=4807c1a3de60...&api_key=sk_..."
{"sha256": "...", "text": "---\nname: date-formatter\n...", "quota": {...}}

POST/api/buy_credit (pay-as-you-go, additive)

Buy one extra scan or lookup without a Pro/Premium subscription. Always requires an existing account and on-chain USDC payment; tops up whatever tier you're already on, never replaces it.

curl -X POST https://skillsmith.ch/api/buy_credit \
  -d '{"api_key":"sk_...","payment_signature":"<tx>","kind":"scan"}'    # $0.02, +1 scan
curl -X POST https://skillsmith.ch/api/buy_credit \
  -d '{"api_key":"sk_...","payment_signature":"<tx>","kind":"lookup"}'  # $0.01, +1 lookup

Errors

StatusMeaning
400Bad request (missing/invalid field, malformed hash, etc.)
401Sign in required, or your api_key is unknown/invalid
402Payment required or not yet verified on-chain
429Daily quota exceeded for your current tier
503Storage temporarily unavailable (rare) — retry later; honor the Retry-After header

Source

Full implementation: github.com/Larslllllll/skillsmith-web (this web app) and github.com/Larslllllll/skillsmith (the underlying CLI/library and detection engine, also usable standalone or as a GitHub Action).

Behavioral Analysis

Submit raw skill content and an AI analyst inside an isolated Vercel container simulates what an agent following it would do: capabilities, action trace, IOCs and a severity verdict ("any.run for agent skills"). The content is never executed against real systems.

POST /api/sandbox-run
Content-Type: application/json

{ "text": "--- name: my-skill ... (raw SKILL.md content, max 100 KB)" }

Response: report with analysis_id, static_iocs, ai_analysis (simulated_actions, capabilities_summary, severity 0-10, recommendation). Rate limit: 10/day/IP.

Shareable permalink: GET /api/analysis?id=<analysis_id>

Interactive UI: /analysis.html