# Scan API

One endpoint. Same rules and scores as the app and the open-source CLI, with every call saved to your history. Keys are minted at https://copylint.xyz/app/keys and need an active plan.

## Authentication
Send the key as a bearer token. Only its hash is stored, so a lost key is replaced, never recovered.

```
Authorization: Bearer cl_...
```

## POST /api/v1/scan
URL: https://copylint.xyz/api/v1/scan
JSON body. Only `text` is required. `mode` is prose, chat, or ui (default prose). `threshold` is 1 to 100 (default 40). `title` is shown in history (default "API scan"). `personal: true` applies the team voice profile.

## Response
The shape of scan_text.py --json plus a usage block. Critical findings add 20, major add 10, minor add 3, capped at 100. `pass` is true below the threshold. Fields: score, threshold, pass, personal, findings (severity, rule, detail, line, excerpt, suggestion), usage (used, limit).

## Suggested fixes
Each finding carries a `suggestion` when the catalog has a plain fix, and `null` when the phrase needs a real fact instead. `from` is the exact text to change, `at` its offset in the text you sent (UTF-16 code units, as in JavaScript), and `to` its replacement; an empty `to` deletes. `safe` is true for word-level edits that keep the meaning, and false for sentence deletions and words that also have a literal sense. Apply one fix, then scan again: the offsets of later findings move. Suggestions never change the score. AI rewrites of single sentences are in the web app, not the API.

## Errors and limits
- 400: body is not JSON, or text is missing or over 200,000 characters. Fix the body.
- 401: missing, unknown, or revoked key. Mint a new key.
- 402: no active plan on the account. Choose a plan on the billing page.
- 429: 60 calls a minute, or the monthly plan limit (2,000 Solo, 20,000 Team). Honor Retry-After, or upgrade.
- 500: the scan could not be saved. Retry; nothing was counted if this comes back.
- 503: the API is not configured on this deployment. Try the production host.

## CI recipe
The Copylint GitHub Action (https://github.com/0xNyk/copylint-action) scans the Markdown a pull request changed, annotates each finding inline, and fails the job when a file scores at or above the threshold. Store the key as a repository secret named COPYLINT_KEY. Pin `0xNyk/copylint-action@v1`.

## Voice profile
Team accounts build a profile from their own published writing on the voice page. Send `"personal": true` and the profile relaxes four stylistic budgets: exclamation marks, questions, sentence-length monotony, and the team's favorite openers. It never touches the phrase catalog, the dash rule, template artifacts, or unsupported claims, so a profiled score is never higher than the plain one.

## Parity with the CLI
The hosted scanner is a port of unmachined's scan_text.py (https://github.com/0xNyk/unmachined). Every rule is covered, and a parity check scores a fixture set through both on every commit; a difference fails the build.

Machine contract: https://copylint.xyz/openapi.json. HTML: https://copylint.xyz/docs.
