A deterministic score (pinned formula v8, same text = same grade + reproducible hash) your code calls 4 ways: the HTTP API (grade + batch), the developer gates for CI (slop + the tenant-isolation Firewall), an MCP server for your agent, and a verifiable badge. The recipe for the score is public — the paid gate's rules stay server-side.
30 seconds: create a key at app.slopgrade.ai/keys, then POST a text to /api/grade with Authorization: Bearer sg_…. No account yet? Grade for free, no key, at app.slopgrade.ai/method (or POST /api/grade/preview).
# 1 · create a key at app.slopgrade.ai/keys # 2 · grade a text:
curl -X POST https://app.slopgrade.ai/api/grade \
-H "Authorization: Bearer sg_your_key" -H "Content-Type: application/json" \
-d '{"text": "Your draft…"}'
# → { "slopScore": 62, "grade": "C", "measurable": true, … }Every grade is one POST — deterministic, reproducible, metered against your included words.
What the score is: a deterministic measure of writing form (eight surface signals), not a calibrated verdict on truth or authorship. The same text always yields the same number, but a confident fabrication can still score well — and wrapping slop in invented facts lowers it: gate style with it, do not fact-check with it. measurable:false means the text was too short for a reliable signal, not « zero slop ». Use app.slopgrade.ai, not api.slopgrade.ai (that host is the database — it answers 401 « No API key found »).
Authentication: grading needs an account. Create a key at app.slopgrade.ai/keys and pass it as an Authorization: Bearer sg_… header (or x-api-key). No header → 401. Your monthly included words meter against the key too.
API-key scopes: every key is minted with a scope, and a call outside it returns 403 scope_denied (not a 401) — so widen the scope, do not re-issue over a 401. grade covers grade + batch; ci the hosted CI grade; mcp the hosted MCP server; deepscan and verify the taint engine, billed per file; full covers everything except deepscan and verify. Pick the narrowest scope that works and widen it at app.slopgrade.ai/keys; a broad endpoint hit with a narrow key fails closed.
Key management is dashboard-only (by design): create, rotate and revoke keys at app.slopgrade.ai/keys. There is no key-minting API — a key cannot create or revoke keys (a privilege-escalation guard). Pre-flight your remaining quota with GET /api/usage.
Rate limit: 60 requests per minute per IP. Past that the API answers 429 and you should retry after a minute — it is a burst guard, not a quota: your included words are metered separately.
Limits: /api/grade refuses a text over 50 000 characters with a 413 — never truncated. Batch takes 25 texts and trims each to 50 000, returning received / truncated so the cap never drops a piece silently. The hosted MCP and /api/ci/grade cap at 20 000.
Optional body params: save — an API key does NOT write to your history by default (send save:true to opt in; a session does save); maxScore — returns a pass/fail gate verdict. Batch also takes authors (per-piece attribution → the byAuthor rollup), batchName/clientId, stops on the first quota/auth failure (later items come back reason:"skipped-quota"), and turns a bare-URL item into a fetched + extracted grade (needs the extract tier; url-tier / url-unreachable otherwise).
Grade the whole draft, not one sentence. The score measures slop density across a text — a single sentence rarely crosses a threshold even when it is pure buzzword, so a one-line request often comes back pass. Send the paragraph, the email, the section — the unit you actually ship. This is why an agent self-correcting should pass its full draft to grade, fix the signals that cost the most, then re-grade.
curl -X POST https://app.slopgrade.ai/api/grade \
-H "Authorization: Bearer sg_your_key" \
-H "Content-Type: application/json" \
-d '{"text": "…"}'{
"slopScore": 62,
"grade": "C",
"lang": "other",
"signals": { "nominalization": …, "burstiness": …, "concreteness": …, "longword": …, "redundancy": …, "diversity": …, "entropy": … },
"markerDensity": …,
"contributions": [ { "key": "nominalization", "label": "…", "weight": …, "slopness": …, "points": … }, … ],
"sentences": [ { "text": "…", "level": "ai" }, … ],
"verdict": "…",
"formulaVersion": "v8",
"hash": "0x…",
"measurable": true
}curl -X POST https://app.slopgrade.ai/api/grade/batch \
-H "Authorization: Bearer sg_your_key" \
-H "Content-Type: application/json" \
-d '{"texts": ["First draft…", "Second draft…"], "authors": ["Alice", "Bob"], "maxScore": 60}'{
"results": [
{ "preview": "First draft…", "words": 120, "ok": true, "grade": "B", "slopScore": 34, "topSignal": "burstiness", "author": "Alice", "pass": true },
{ "preview": "Second draft…", "words": 98, "ok": true, "grade": "D", "slopScore": 66, "topSignal": "redundancy", "author": "Bob", "pass": false }
],
"count": 2, "graded": 2, "received": 2, "truncated": 0, "avgScore": 50,
"diagnosis": { … }, // dominant signal + lever + A–F distribution (null if nothing measurable)
"byAuthor": [ { "author": "Bob", "count": 1, "avgScore": 66, "avgGrade": "D", "dominant": "redundancy", "worst": 66 }, … ], // grading by writer · worst-first · [] if no authors sent
"batchId": null, "batchName": null,
"gate": { "maxScore": 60, "pass": false, "failed": 1 } // present ONLY when you send maxScore
}
// a failed / skipped item: { "preview": …, "ok": false, "reason": "quota|auth|rate|error|not-measurable|skipped-quota|url-tier|url-unreachable", "pass": false }Error responses: every failure is a JSON { error } carrying the HTTP status. The message text is localized — branch on the status and the machine fields (upgrade_url, used) plus the error value on a 402 (paid-only; account-locked + reactivate_url when the Team trial ended unpaid) / 403 (scope_denied), never on the human message string.
400 # malformed JSON, or "text" missing / not a string
401 # missing / invalid / revoked key, or no session
402 # Free monthly word quota spent → { "error": …, "upgrade_url": "/pricing", "used": 48213 }
402 # paid-only surface → { "error": "paid-only", "upgrade_url": "/pricing" }
403 # scope_denied — the key's scope does not cover this endpoint (widen it at /keys)
413 # text over 50 000 chars — refused, never truncated
429 # 60 requests / minute / IP burst guard — retry after ~60s
503 # quota / billing momentarily unreadable — fail-closed, retryableWire the gates into CI with one reusable Action — uses: maxor-tech/slopgrade-firewall@<sha>. Nothing to vendor: the deterministic engine ships with the Action, the score is computed in your runner, and by default only a fingerprint leaves it. On a public repo, the deep scan (heisen-slop) also sends the files that hold a sink; on a private repo, only if you turn it on (paid).
the slop layer — grades the changed files into an advisory A–F note (reported, never fails the check; the Firewall is the gate) · $0, keyless, deterministic
# .github/workflows/slop-gate.yml
name: slop-gate
on: { pull_request: {} }
jobs:
slop:
runs-on: ubuntu-latest
permissions:
id-token: write # OIDC — proves the repo, zero secret (the Firewall needs it)
contents: read
steps:
- uses: actions/checkout@v7
with: { fetch-depth: 0 } # required: the PR diff needs the base commit
- uses: maxor-tech/slopgrade-firewall@<commit-sha> # pin a full SHA, not a moving tag
with:
# the A–F note is advisory (reported, never blocks) — the Firewall is the gate
paths: "*.md *.ts *.py" # default "*.md"; add code globs to gate code too
firewall-mode: advisory # advisory (free, reports) | gate (blocks; public repo or paid slot)
format: sarif # text (default) | json | sarif → Action output 'results-file'Wiring it: give the job permissions: { id-token: write, contents: read } — the OIDC token proves the repo with zero secret; without it the Firewall can't prove the repo and no-ops (fails open — never blocks). Keep actions/checkout at fetch-depth: 0 (the PR diff needs the base commit), and pin the Action to a full commit SHA, not a moving tag.
Non-PR pipelines: pass files: (an explicit space-separated list) instead of relying on the PR diff — required for push / workflow_dispatch / scheduled jobs. paths: filters the PR diff; files: overrides it.
Where findings surface: the check annotates the PR; --report (or the Action's key) posts the run to your A→F climate at app.slopgrade.ai/ci; and format: sarif writes a SARIF 2.1.0 file (Action output results-file) you feed to github/codeql-action/upload-sarif for the GitHub Security tab. format: json emits the raw results.
Non-GitHub CI (GitLab / Jenkins / CircleCI): the slop gate is portable — run ci/slop-gate.ts directly (keyless, $0) or POST /api/ci/grade. The tenant-isolation Firewall is GitHub-only — it proves the repo via GitHub OIDC; elsewhere, use the slop gate.
The tenant-isolation Firewall (the paid CODE product) rides the same Action via firewall-mode: advisory|gate. Advisory is free and universal — it reports cross-tenant and cross-class leaks on every repo, never blocking. gate blocks the build on a hard finding. By default only a fingerprint leaves CI: the Action sends a structural fingerprint, the detection rules run server-side only, and only a verdict comes back — the rules never ship to the client. On a public repo, the deep scan (heisen-slop) also sends the files that hold a sink; on a private repo, only if you turn it on (paid).
Who gets to block (per repo): advisory — every repo, free, reports but never blocks · public repo or first free private repo — the cross-tenant gate and PHP echo of request input (XSS) block for free · paid slot — full file:line findings, public or private. Today, the cross-tenant gate and an unescaped PHP echo of request input (XSS) block, and on a paid slot the timing-unsafe signature check blocks too; other detector packs and XSS shapes stay advisory on every plan. An eligible pack becomes blocking automatically after measuring at least 80% blind precision on at least 10 labelled blocking rows from fresh, frozen samples. Assign a slot to owner/repo at app.slopgrade.ai/pricing; a gate-mode repo without entitlement stays advisory, enforced server-side.
What the Firewall gates: cross-tenant isolation first, then a growing set of code-security classes — SQL & NoSQL injection, SSRF, XXE, CSRF, SSTI, command & path injection, ReDoS, open redirect, secret exposure, insecure deserialization, and supply-chain / IaC drift. We publish which classes are gated, never how each is detected — the rules are the moat.
mode: prose|code, or an ext like .ts), no vendoring · $0, keyless, no sessionnode --experimental-strip-types ci/slop-gate.ts docs/*.md # → ✗ docs/pitch.md — D · 65/100 (exit 1) # → ✓ docs/readme.md — A · 20/100 (exit 0)
Generate a typed SDK: /api/openapi.json is a full OpenAPI 3.1 document — body schemas (GradeRequest, SlopResult, BatchRequest, BatchResult) plus a per-tool argument schema for every MCP tool under x-mcp-tools. Point any generator at it for a typed client with the Bearer scheme wired.
npx @openapitools/openapi-generator-cli generate \ -i https://app.slopgrade.ai/api/openapi.json \ -g typescript-fetch \ -o ./slopgrade-sdk
stdio server, zero dependency, deterministic and local. Your agent grades its draft and regenerates if it is slop — no network, no inference cost.
// mcpServers
{
"slopgrade": {
"command": "node",
"args": ["--experimental-strip-types", "/path/to/slopgrade/mcp/server.ts"]
}
}// mcpServers — hosted (Bearer key, streamable HTTP)
{
"slopgrade": {
"type": "http",
"url": "https://app.slopgrade.ai/mcp",
"headers": { "Authorization": "Bearer sg_your_key" }
}
}The hosted endpoint caps each text at 20 000 characters (tighter than the HTTP API's 50 000) and grade_batch at 10 texts per call (the HTTP batch takes 25). /mcp and /api/mcp are the same server.
The server also exposes two resources — slopgrade://formula (weights, bands, threshold) and slopgrade://signals (the eight signals) — and a prompt tighten_draft (the grade → cut → re-grade loop, ready to run).
An embeddable SVG badge linking to the receipt; the hash proves the grade has not moved. The moat: a transparent receipt no black-box detector can issue.
[](https://app.slopgrade.ai/r/ID)
Help: check service status at GET /api/health · no key yet? grade free at app.slopgrade.ai/method · create & manage keys at app.slopgrade.ai/keys.