API

JSON in, JSON out. The caveats come along.

Four endpoints — the same four panels as the detector: the model reading, the writing measurements, the character check and the source check. Auth is optional on the first three; send Authorization: Bearer … to meter against your plan. Every detect response carries abstained, modelVersion and measuredFpr — show them wherever you show probability.

Base URLhttps://knownota.com/api

Keys look like kn_live_…, are created on the account page (Professional and above), shown once, and stored only as a hash. A key meters against the plan of the account that owns it. Cap: 200,000 characters (413 above).

POST /detect

Machine-likeness under the shipped model. Abstains under 250 words.

curl
curl -X POST https://knownota.com/api/detect \
  -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{"text": "<at least 250 words of prose>"}'
response
{
  "available": true,
  "reason": null,
  "abstained": false,
  "probability": 0.81,
  "band": "high",                 // low | moderate | high | very_high
  "verdict": "consistent_with_machine_generation",
  "threshold": 0.96,
  "measuredFpr": 0.0066,          // held-out humans ≥250 words, this checkpoint
  "modelVersion": "nota",
  "minWords": 250,
  "wordCount": 412,
  "caveat": "Consistent with machine generation under this model — not proof of authorship."
}

If abstained is true, probability, band and verdict are null — show reason. Map verdict to the human sentence; null → “Inconclusive”.

POST /evidence

Writing statistics against a human envelope. Never averaged into a score — deliberately.

curl
curl -X POST https://knownota.com/api/evidence \
  -H "Content-Type: application/json" \
  -d '{"text": "…"}'
response (abridged)
{
  "wordCount": 412, "sentenceCount": 21, "paragraphCount": 5,
  "notes": ["…"],
  "reference": { "humanCorpus": {...}, "aiCorpus": {...}, "caveat": "…" },
  "signals": [{
    "name": "sentence_length_variation",
    "label": "Sentence length variation",
    "what": "One sentence a non-expert understands.",
    "value": 12.4, "unit": "stdev words",
    "direction": "lower-suggests-machine",   // | higher-suggests-machine | context-only
    "minWords": 250, "available": true,
    "failureMode": "When this misleads — always shown.",
    "position": "toward-machine",             // | toward-human | unremarkable | no-reference
    "separation": 0.46, "separationParaphrased": 0.28,
    "hits": [], "spans": [[10, 24]]
  }]
}

If separation is below 0.20 render the row as context only, not as a lean. There is no aggregate evidence score.

POST /characters

The observed panel: hidden characters, look-alike letters, direction overrides and watermark carriers. Deterministic, position-exact, free — no model, no quota. Never enters the score.

curl
curl -X POST https://knownota.com/api/characters \
  -H "Content-Type: application/json" \
  -d '{"text": "…"}'
response (abridged)
{
  "inspected": 1475, "clean": false,
  "totals": { "obfuscation": 2, "carrier": 0, "residue": 1 },
  "findings": [{
    "tier": "obfuscation",              // | carrier | residue
    "label": ""prоposal" mixes scripts — 1 Cyrillic letter drawn like Latin",
    "codepoint": "U+043E passes for "o"",
    "count": 1,
    "samples": [{ "offset": 4, "excerpt": "The prоposal was…" }]
  }],
  "caveat": "…"
}

obfuscation is evidence the text was altered to evade tools — not of who wrote it. residue (NBSPs, soft hyphens) arrives innocently from word processors; never surface it as a flag. clean: true is not evidence of anything.

POST /overlap Individual and up

The source check: quote-searches distinctive passages on the indexed web and scholarly connector and aligns matched spans. Returns a cached result instantly for a document checked in the last 30 days, otherwise a job to poll.

curl
curl -X POST https://knownota.com/api/overlap \
  -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{"text": "…"}'
# → { "cached": true, "result": {…} }   or   { "job": { "id": "…", "status": "queued" } }
curl https://knownota.com/api/jobs -H "Authorization: Bearer <key>"
# → poll until jobs[].status is "done"; the result is on the job
result (abridged)
{
  "totalWords": 563, "matchedWords": 562, "nearWords": 0, "coverage": 0.998,
  "windowsSearched": 8, "windowsHit": 8,
  "sources": [{
    "url": "https://…", "title": "…", "domain": "…",
    "matchedWords": 562, "windowHits": 8,
    "spans": [[0, 3410]]                 // character offsets in your text
  }],
  "scope": "indexed web + scholarly connector",
  "caveat": "Pages and matched spans — never a plagiarism verdict."
}

Overlap is evidence, not a verdict: quoting, common phrasing and shared sources all produce it. Show the spans and the sources; do not print a single “plagiarism %” as the answer.

Reference

Routes

Method · pathAuthBodyNotes
POST /detectoptional{ text }413 above 200,000 chars. Abstains under 250 words. Meters Nota words when authed.
POST /evidenceoptional{ text }Signals + reference envelope.
POST /charactersoptional{ text }Hidden characters, look-alike letters, carriers — deterministic, free, not in the score.
POST /overlap · GET /jobspaid{ text }Source check: cached result, or a job to poll. 250+ words. Never a plagiarism verdict.
POST /reportpaid{ text }Signed JSON of a detect result (Ed25519). GET /report/key for the public key.
POST /keys · GET /keys · DELETE /keys/:idsession{ name }Keys are kn_live_… Bearer tokens, hashed at rest, shown once; revoking stops a key immediately. Keys cannot manage keys.
GET /mekey or sessionPlan, words used / words in period, 30-day usage — a quick way to check a key works.
Rules for integrators

Show abstained, measuredFpr and modelVersion wherever you show probability. Do not render a red/green boolean. Do not average evidence into the score. Errors are always { "error": string }.

Get a key