# AntiSlop — API consumer guide for AI agents & integrators

**Audience:** Any human or AI agent implementing a client that calls AntiSlop.  
**Canonical contract:** OpenAPI 3.1 at `/docs/antislop-openapi.yaml` (source: `internal/anti-slop-capilot/openapi/antislop-openapi.yaml`).  
**If docs disagree with live Flask, Flask wins:** `meeting-scheduler/routes/antislop.py`.  
**Product landing:** https://www.insightits.com/products/antislop.html  
**Whitepaper:** https://www.insightits.com/whitepapers/antislop.html  
**Public mirror of this guide:** https://www.insightits.com/docs/antislop-agent-guide.md  
**MCP descriptor:** https://www.insightits.com/mcp_tools/antislop_evaluate.json  

---

## 0. Agent bootstrap (read this first)

| Step | Action |
| --- | --- |
| 1 | `GET https://www.insightits.com/api/antislop/v1/health` — expect `{"status":"ok","service":"antislop"}` |
| 2 | `GET …/plans` — confirm Free/Pro/Enterprise verification limits |
| 3 | Fetch OpenAPI: `GET https://www.insightits.com/docs/antislop-openapi.yaml` |
| 4 | Obtain `ask_…` API key (dashboard AntiSlop → `POST /keys` with site JWT) **or** use Bearer JWT |
| 5 | Call `POST /evaluate` — **never invent** `score` / `verdict` / `why[]` |
| 6 | On errors: handle `401`, `422` (SecurityBlock), `429` (quota/demo) — see §3 |
| 7 | Surface `why[].feature` + `why[].detail` to end users |

**Product in one sentence:** AntiSlop scores content as **slop vs builder/product signal** and returns **`score` (0–10) + `verdict` + feature-backed `why[]`**. It is **not** Website Hub chat and **not** an LLM-as-judge.

---

## 1. Base URL

| Environment | Base |
| --- | --- |
| Production | `https://www.insightits.com/api/antislop/v1` |
| Local Flask | `http://localhost:5000/api/antislop/v1` |

All paths below are relative to that base.

---

## 2. Auth (same plan for REST + MCP)

| Mode | How | Quota |
| --- | --- | --- |
| **API key** (preferred for apps/agents) | Header `X-API-Key: ask_…` **or** `Authorization: Bearer ask_…` | Member Free/Pro/Enterprise monthly verifications |
| **JWT** | Site login JWT: `Authorization: Bearer <jwt>` (not starting with `ask_`) | Same membership quota |
| **Demo** (no auth) | Omit auth | **Text-only**, **10 requests / IP / UTC day** |

### Create an API key

1. User signs in at insightits.com (site JWT).
2. `POST /keys` with `Authorization: Bearer <jwt>` and optional `{"label":"my-agent"}`.
3. **201** response includes plaintext `key` **once** — store as a secret; it will not be shown again.

```json
{
  "id": "…",
  "key": "ask_…",
  "prefix": "ask_",
  "label": "my-agent",
  "message": "Store this key securely; it will not be shown again."
}
```

`GET /keys` lists metadata only (no secrets). `DELETE /keys/{key_id}` revokes.

**Plans (shared across dashboard, REST, MCP):**

| Plan | USD/mo | Verifications |
| --- | --- | --- |
| free | 0 | 25 |
| pro | 20 | 500 |
| enterprise | 200 | 5000 (5 seats) |

`GET /plans` returns `{ "plans": { "free": {…}, "pro": {…}, "enterprise": {…} } }`.  
`GET /me` (JWT) returns `{ "plan", "verifications_used", "verifications_limit", "seats" }`.

---

## 3. Primary operation: evaluate

### Request

```http
POST /evaluate
Content-Type: application/json
X-API-Key: ask_YOUR_KEY
```

```json
{
  "content_type": "text",
  "source_platform": "linkedin",
  "raw_text": "We shipped agents to prod; p99 jumped from 120ms to 890ms…",
  "include_decode": false,
  "options": {
    "decode_engine": "v2"
  }
}
```

| Field | Required | Notes |
| --- | --- | --- |
| `content_type` | **Yes** | `text` \| `image_base64` \| `url_reference` \| `pdf_base64` |
| `raw_text` | for `text` (and for `url_reference` today) | Post body, README markdown, or **fetched** page/PDF text |
| `image_data` | for `image_base64` | Base64 image bytes (data-URL prefix `data:image/…;base64,` is stripped if present) |
| `pdf_data` | for `pdf_base64` | Base64 PDF bytes |
| `source_platform` | No | Routes Method A vs B — see §3.1 |
| `include_decode` | No | Default `false`. If `true`, response may include `decode` |
| `options.decode_engine` | No | `v1` (default) or `v2` |
| `client_metadata` | No | Opaque object; ignored by scorer |

Alias: `POST /score` — identical body/response.

### 3.1 Method routing (A vs B)

Server chooses `method` — clients should set `source_platform` correctly:

| `source_platform` | Method | Use for |
| --- | --- | --- |
| `linkedin`, `twitter_x` | **A** | Social posts (PP-DETECT) |
| `github_readme`, `product_spec`, `product_description` | **B** | README / product / specs |
| `general_web` | A if ≤400 words, else B | Fallback |
| (any) + `content_type=pdf_base64` | **B** | Spec/product PDFs |

### 3.2 `url_reference` caveat

There is **no server-side URL fetch** yet. `url_reference` falls back to `raw_text` (with `digest_warnings` containing `digest_fallback_to_raw_text`).  
**Agent action:** fetch the URL yourself, put extracted text in `raw_text`, prefer `content_type: "text"` with an appropriate `source_platform`.

### Success response (200)

```json
{
  "request_id": "uuid",
  "score": 7.5,
  "verdict": "real",
  "method": "A",
  "confidence": 0.85,
  "why": [
    {
      "feature": "has_lived_failure",
      "direction": "positive",
      "detail": "Mentions production failure / constraint"
    }
  ],
  "diagnostics": {
    "summary": "…",
    "positive_signals": [],
    "red_flags": [],
    "action_recommendation": "engage"
  },
  "archetype": null,
  "layer_scores": null,
  "decode": null,
  "digest_warnings": [],
  "cost_hints": {
    "llm_calls": 0,
    "vision_used": false,
    "cached_digest": false,
    "scorer": "heuristic"
  },
  "deterministic_verification": { "manifest_hash": "…", "integer_drift_status": "PASSED_ZERO_DRIFT" },
  "security_audit": { "prismguard_status": "PASSED", "sanitization_applied": false }
}
```

| Field | Meaning |
| --- | --- |
| `score` | 0 = pure slop/wrapper … 10 = high builder/product signal |
| `verdict` | `slop` \| `weak` \| `real` \| `strong` |
| `method` | `A` (posts / PP-DETECT) or `B` (README/spec product index) |
| `why[]` | Feature-backed reasons — **use these in UX**; do not invent alternate rationales |
| `diagnostics.action_recommendation` | Hint: `skip` / `prefer_skip` / `engage` / `prioritize` / `review` |
| `archetype` / `layer_scores` | Usually set for Method B; `null` for A |
| `decode` | Only when `include_decode=true` |
| `cost_hints.scorer` | `heuristic` or trained classifier id when promoted |
| `request_id` | Pass to `POST /feedback` if the user corrects the score |

### Errors agents must handle

| HTTP | `error` (typical) | Meaning | Agent action |
| --- | --- | --- | --- |
| 400 | — | Missing `content_type` / `request_id` | Fix request |
| 401 | `Unauthorized` | Bad/missing key; or demo used for non-text | Obtain key / use text for demo |
| 422 | `SecurityBlock` | PrismGuard blocked content | Do not retry same payload; show `message` / `threat_type` |
| 429 | `QuotaExceeded` | Monthly plan limit | Tell user to upgrade; check `GET /me` |
| 429 | `RateLimitExceeded` | Demo IP daily cap | Prompt sign-in / API key |

Error body shape:

```json
{ "error": "QuotaExceeded", "message": "Monthly verification quota exceeded" }
```

422 may also include `threat_type`, `request_id`.

---

## 4. Other endpoints

| Method | Path | Auth | Purpose |
| --- | --- | --- | --- |
| GET | `/health` | none | `{ "status": "ok", "service": "antislop", "version": "1.0.0" }` |
| GET | `/plans` | none | Free/Pro/Enterprise amounts + verification limits |
| GET | `/me` | JWT | Plan + `verifications_used` / `verifications_limit` / `seats` |
| POST | `/keys` | JWT | Create API key — **returns plaintext key once** (201) |
| GET | `/keys` | JWT | List key metadata (no secrets) |
| DELETE | `/keys/{key_id}` | JWT | Revoke |
| GET | `/events` | JWT | Recent score events (`?limit=1..100`) |
| POST | `/feedback` | JWT or API key | Gold signal for a prior `request_id` |
| POST | `/draft-comment` | JWT or API key | Optional PrismPost-style draft (**never auto-posts**) |

### Feedback

```json
POST /feedback
{
  "request_id": "<from evaluate>",
  "override_verdict": "slop",
  "feedback": "optional free text — alias of override_verdict"
}
```

Response: `{ "ok": true, "request_id": "…" }`.

### Draft comment

Requires auth. Body needs `raw_text` (and optional `score` / `verdict` / `decode`). Returns a draft object — **do not post to LinkedIn/X automatically**.

---

## 5. MCP

Descriptor: `/mcp_tools/antislop_evaluate.json`  
Tool name: `antislop_evaluate`  
Implementation: HTTP `POST /evaluate` with the same JSON body and auth.  
**Quota:** each tool call counts as one verification on the same plan.

MCP hosts should configure:

- URL: `https://www.insightits.com/api/antislop/v1/evaluate`
- Header: `X-API-Key: ask_…`
- Input schema: see descriptor `inputSchema` (same fields as EvaluateRequest)

---

## 6. Hard rules for implementing agents

1. **Never invent a score** — only display API `score` / `verdict` / `why[]`.  
2. **Never treat AntiSlop as chat** — call evaluate; do not route Website Hub conversation into this product.  
3. **Prefer API keys** for server-side / MCP hosts; store `ask_…` as a secret.  
4. **Same plan** for dashboard, REST, and MCP — do not invent a second SKU.  
5. **Demo is not production** — no images/PDFs without auth.  
6. On `why[]`, surface `feature` + `detail` to users; that is the product differentiator.  
7. Optional decode is off by default; only set `include_decode=true` when the product needs intent/wedge narrative.  
8. OpenAPI + this guide are the contract; do not scrape HTML for API semantics.  
9. **Do not rely on server URL fetch** for `url_reference` — extract text client-side.  
10. On `422 SecurityBlock`, do not retry the identical payload in a loop.

---

## 7. Minimal clients

### curl smoke test

```bash
curl -sS https://www.insightits.com/api/antislop/v1/health

curl -sS -X POST https://www.insightits.com/api/antislop/v1/evaluate \
  -H "Content-Type: application/json" \
  -H "X-API-Key: ask_YOUR_KEY" \
  -d '{"content_type":"text","source_platform":"linkedin","raw_text":"We hit a demo break when RAG mixed v1/v2 policy. Added supersession rules."}'
```

### Python

```python
import requests

BASE = "https://www.insightits.com/api/antislop/v1"
headers = {"Content-Type": "application/json", "X-API-Key": "ask_YOUR_KEY"}

r = requests.post(
    f"{BASE}/evaluate",
    headers=headers,
    json={
        "content_type": "text",
        "source_platform": "github_readme",
        "raw_text": "# Product\nEvals, hybrid search, Docker, SSO, audit logs.",
        "include_decode": False,
    },
    timeout=60,
)
r.raise_for_status()
data = r.json()
print(data["score"], data["verdict"], data["why"])
```

### JavaScript (fetch)

```js
const BASE = 'https://www.insightits.com/api/antislop/v1';
const res = await fetch(`${BASE}/evaluate`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.ANTISLOP_API_KEY,
  },
  body: JSON.stringify({
    content_type: 'text',
    source_platform: 'linkedin',
    raw_text: 'Shipped to prod; p99 rose after the third loop…',
  }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
// Use data.score, data.verdict, data.why — never invent them
```

---

## 8. Related docs

| Doc | Use |
| --- | --- |
| `/docs/antislop-openapi.yaml` | Machine schema (OpenAPI 3.1) |
| `/whitepapers/antislop.html` | Human/API overview |
| `/mcp_tools/antislop_evaluate.json` | MCP tool descriptor |
| `CHROME_EXTENSION_HANDOFF.md` | Extension consumer notes |
| `GOLD_TRAINING_PLAYBOOK.md` | How InsightITS improves the model (not required to call the API) |

*Last updated: 2026-08-11*
