API reference
Base URL: https://nexxtool.ai
All requests authenticate with Authorization: Bearer wtk_<key>. JSON bodies up to 512KB.
POST /v1/validate
Checks a single AI reply against a rule set. Returns a verdict, score, issues, and optional repair.
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer wtk_<key> |
Content-Type | Yes | application/json |
Idempotency-Key | No | Any string ≤200 chars. Replaying with the same key returns the original response. Replaying with a different body returns 409. |
Body
{
"output": string | object, // your AI output
"contract": { "type": "html"|"script"|"research"|"json"|"text", ... },
"metadata": { "feature": "...", "model": "...", ... } // optional, indexed
}
Response 200
{
"validation_id": "v_01HX...",
"pass": boolean,
"score": number, // 0.0 .. 1.0
"issues": string[], // ["filler:lorem ipsum", "schema:.price required", ...]
"repaired": null | { output: ..., _repaired: true },
"duration_ms": number,
"billed": true,
"cost_credits": number, // 1, or 2 if repair ran
"usage": {
"tier": "free"|"starter"|"pro"|"scale",
"used": number,
"cap": number,
"remaining": number,
"period_start": "YYYY-MM-DD"
}
}
Issue label prefixes
Every problem found is reported (not just the first), one entry per issue in issues. Prefixes:
blocked:<phrase>— a phrase from your ownmust_not_containlist appeared in the output.filler:<phrase>— a built-in AI-slop phrase (e.g.lorem ipsum,as an ai language model) appeared.missing:<phrase>— a requiredmust_containphrase was absent.leftover:<phrase>— the AI’s own chatter was left in the output (e.g.here's a more polishedat the start,[your name]anywhere). On by default fortextandhtmlrule sets; setblock_leftover_ai_text: falseto disable.private_data:<kinds>— the output contains a card number (card), a US social security number (ssn) or a secret key (key). Only when the rule set hasblock_private_data: true; the matched text is never included.
Note: your own blocked phrases previously appeared as filler:<phrase>; they now appear as blocked:<phrase>. Built-in slop remains filler:. Matching is case-insensitive. The score drops 0.10 per issue of these kinds (floor 0).
Response 4xx / 5xx
| Status | Error code | When |
|---|---|---|
| 400 | output_required | Missing output field |
| 400 | contract_required | Missing or non-object contract |
| 400 | invalid_contract_type | Type not in allowed set |
| 401 | missing_api_key | No Authorization header |
| 401 | malformed_api_key | Header present but not in wtk_ format |
| 401 | invalid_api_key | Key does not match any record |
| 401 | revoked_api_key | Key was revoked |
| 402 | quota_exceeded | Monthly cap hit; response includes upgrade URL |
| 409 | idempotency_key_conflict | Same key, different body |
| 413 | output_too_large | Output exceeds 256KB |
| 429 | rate_limit_exceeded | Per-key rate limit (see Errors & rate limits) |
GET /v1/usage
Current period usage snapshot.
curl -H "Authorization: Bearer wtk_..." \
https://nexxtool.ai/v1/usage
GET /v1/validations
List recent checks. Supports filtering by pass, type, from, to, and limit (max 200).
curl -H "Authorization: Bearer wtk_..." \
"https://nexxtool.ai/v1/validations?limit=20&pass=false"
GET /v1/validations/:id
Retrieve a single check by id.
GET /v1/health
Public, no authentication. Returns exactly {"ok":true} (HTTP 200) when a lightweight database query succeeds, or {"ok":false} (HTTP 503) when it does not. The result is cached for about 15 seconds. No other details are exposed.