> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qwedai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API endpoints

> Complete QWED API endpoint reference covering health checks, verification, batch operations, agent endpoints, observability, and admin routes.

## Base URL

```text theme={null}
https://api.qwedai.com/v1
```

## Health check

### GET /health

Check API status. No authentication required.

```bash theme={null}
curl https://api.qwedai.com/v1/health
```

**Response:**

```json theme={null}
{
  "status": "healthy",
  "service": "QWED Platform",
  "version": "5.1.0",
  "timestamp": "2024-12-20T12:00:00Z"
}
```

### GET /health/identity

Challenge-response proof that the server holds a given secret set. `qwed init` calls this endpoint before bootstrapping an API key to confirm it's talking to the right local server. See [`qwed init`](/advanced/cli#qwed-init-onboarding-and-bootstrap). No authentication required.

**Query parameters:**

| Parameter | Type | Required | Description |
| - | - | - | - |
| `nonce` | string | Yes | Caller-generated random value, 1–128 characters. Use a fresh nonce for every request. |

```bash theme={null}
# Fresh random value per request, e.g. Python's secrets.token_urlsafe(24)
curl http://localhost:8000/health/identity?nonce=Vh8Qm2xT-0pLw6KdYs3rZbNf_5JtAe1X
```

**Response:**

```json theme={null}
{
  "identity": {
    "jwt_hmac": "9f2c...e41a",
    "lookup_hmac": "b07d...5c13"
  }
}
```

| Field | Description |
| - | - |
| `identity.jwt_hmac` | Hex HMAC-SHA256 under `QWED_JWT_SECRET_KEY` of the message `qwed-server-identity-v1:jwt:<nonce>` |
| `identity.lookup_hmac` | Hex HMAC-SHA256 under `QWED_API_KEY_LOOKUP_SECRET` of the message `qwed-server-identity-v1:lookup:<nonce>` |

To verify, compute the same HMACs with your local secrets and compare them in constant time. The secrets themselves are never returned.

**Error responses:**

| Status | Detail | Cause |
| - | - | - |
| `400` | `nonce is required (max 128 characters)` | `nonce` is missing, empty, or longer than 128 characters |
| `503` | `server secrets not configured` | The server has no `QWED_JWT_SECRET_KEY` or `QWED_API_KEY_LOOKUP_SECRET` set |

Servers released before this endpoint existed return `404`.

***

## Verification endpoints

### POST /verify/natural\_language

Main entry point for verifying natural language queries. Routes through the QWED Control Plane with multi-tenancy support.

**Request:**

```json theme={null}
{
  "query": "What is 15% of 200?",
  "provider": "openai"
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | string | Yes | Natural language claim to verify |
| `provider` | string | No | Preferred LLM provider (e.g., `openai`, `anthropic`) |

**Response:**

The response follows the `VerificationResult` schema. For math queries, the top-level `status` is `INCONCLUSIVE` because the LLM translation step is not formally verified — see [Trust boundary](/engines/math#trust-boundary) for details.

```json theme={null}
{
  "status": "INCONCLUSIVE",
  "user_query": "What is 15% of 200?",
  "translation": {
    "expression": "0.15 * 200",
    "claimed_answer": 30.0,
    "reasoning": "15% as decimal is 0.15, multiply by 200",
    "confidence": 0.95
  },
  "verification": {
    "calculated_value": 30.0,
    "is_correct": true,
    "diff": 0.0
  },
  "trust_boundary": {
    "query_interpretation_source": "llm_translation",
    "query_semantics_verified": false,
    "verification_scope": "translated_expression_only",
    "deterministic_expression_evaluation": true,
    "formal_proof": false,
    "translation_claim_self_consistent": true,
    "provider_used": "openai_compat",
    "overall_status": "INCONCLUSIVE"
  },
  "final_answer": 30.0,
  "provider_used": "openai_compat",
  "latency_ms": 245.3
}
```

**Status values for natural language math verification:**

| Status | Meaning |
| - | - |
| `INCONCLUSIVE` | Expression evaluation succeeded, but the LLM translation step is non-deterministic, so you cannot treat the result as a proven verdict on the original user query |
| `ERROR` | The translated expression had a syntax error or the engine could not evaluate it |
| `NOT_MATH_QUERY` | The query was not recognized as a mathematical question |
| `BLOCKED` | Request blocked by security policy |

<Note>
  Previous versions set the `status` field to the inner engine result (e.g., `VERIFIED`). It now returns `INCONCLUSIVE` when the inner engine returns `VERIFIED` or `CORRECTION_NEEDED`, because the natural language pipeline involves a non-deterministic LLM translation step. Use the `trust_boundary` object to inspect the detailed verification breakdown. For fully deterministic results, use `POST /verify/math` directly.
</Note>

**`trust_boundary` fields:**

| Field | Type | Description |
| - | - | - |
| `query_interpretation_source` | string | Always `"llm_translation"` |
| `query_semantics_verified` | boolean | Always `false` — QWED cannot verify that the LLM correctly interpreted the user's intent |
| `verification_scope` | string | Always `"translated_expression_only"` — the attestation and enforcement below bind to the translated expression, not the natural-language `user_query` |
| `deterministic_expression_evaluation` | boolean | `true` when the inner engine status was `VERIFIED` or `CORRECTION_NEEDED` |
| `formal_proof` | boolean | Always `false` |
| `translation_claim_self_consistent` | boolean | Whether the translated expression matched its own claimed answer |
| `provider_used` | string | LLM provider used for translation |
| `trust_enforced` | string | Status returned by the mandatory trust-boundary enforcement step. Matches `overall_status`. |
| `attestation_policy` | string | Always `"mandatory"` — attestation admission is always required, never advisory |
| `attestation_error` | string | Present only when the underlying attestation failed to sign. Machine-readable code (e.g. `SIGNING_FAILURE`, `CRYPTO_UNAVAILABLE`) explaining why the response was downgraded to `BLOCKED`. |
| `overall_status` | string | Final status set from the enforced decision — never from the raw verifier verdict. Mirrors the top-level `status` field. |

#### Mandatory attestation admission

The control plane treats attestation as an admission gate, not a decoration:

* The final `status` and `trust_boundary.overall_status` are set by `enforce_trust_decision(..., require_attestation=True)` after signing, not by the raw verifier verdict. A `VERIFIED` math result is only surfaced once `create_verification_attestation()` returns `ISSUED`.
* If attestation signing fails for any reason, the response is downgraded to `BLOCKED` (fail-closed). The attestation error code is echoed under `trust_boundary.attestation_error` and the top-level response omits the attestation token.
* The attestation `qwed.query_hash` binds to the translated expression that the deterministic engine actually evaluated — not to the natural-language `user_query`. This matches the disclosed `verification_scope: "translated_expression_only"`: the natural-language query still appears in `response.user_query` for display, but downstream consumers verifying the attestation should hash the translated expression, not the user prose.

See [Cryptographic attestations](/advanced/attestations) for the full `AttestationResult` contract and [Trust boundary](/engines/math#trust-boundary) for the math-engine discussion.

***

### POST /verify/math

<Info>Updated in v5.1.0: `verify_identity()` numerical sampling fallback now returns `BLOCKED` with `is_equivalent: false` instead of `UNKNOWN`. This fail-closed behavior ensures that sampling-only agreement is never mistaken for a verified identity.</Info>

Verify mathematical expressions or equations using SymPy symbolic computation.

**Request:**

```json theme={null}
{
  "expression": "x**2 + 2*x + 1 = (x+1)**2",
  "context": {
    "domain": "real"
  }
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `expression` | string | Yes | Mathematical expression or equation. Use `=` for equations |
| `context` | object | No | Optional context (e.g., `{"domain": "real"}` to restrict to real numbers) |

**Response (equation):**

```json theme={null}
{
  "is_valid": true,
  "result": true,
  "left_side": "x**2 + 2*x + 1",
  "right_side": "(x + 1)**2",
  "simplified_difference": "0",
  "message": "Identity is true"
}
```

**Response (expression):**

```json theme={null}
{
  "is_valid": true,
  "value": 4.0,
  "simplified": "4",
  "original": "2 + 2"
}
```

Symbolic expressions that cannot be evaluated to a numeric value include `is_symbolic: true`. Expressions involving complex numbers include `is_complex: true`.

**Ambiguous expressions:** Expressions with implicit multiplication after division (e.g., `1/2(3+1)`) are rejected with `is_valid: false`, `result: false`, and `status: "BLOCKED"`. The response includes `warning: "ambiguous"` and a `message` explaining why. Rewrite the expression with explicit parentheses or a `*` operator to remove the ambiguity.

```json theme={null}
{
  "is_valid": false,
  "result": false,
  "status": "BLOCKED",
  "warning": "ambiguous",
  "message": "Expression may be ambiguous due to implicit multiplication after division",
  "simplified": "..."
}
```

**Error cases:** Division by zero, `log(0)`, and square root of negative numbers in the real domain return `is_valid: false` with a descriptive `error` and `message`.

**Tolerance bounding:** When verifying an expression against an expected value with a `tolerance` parameter, the engine enforces a deterministic upper bound on the tolerance. The maximum allowed tolerance is `max(0.01, abs(calculated_value) * 0.01)`. If the requested tolerance exceeds this bound, the request returns a `BLOCKED` status. Invalid tolerance values (negative, `NaN`, `Infinity`, or non-numeric) are also rejected with `BLOCKED`.

**Response (tolerance exceeded):**

```json theme={null}
{
  "is_correct": false,
  "error": "Tolerance exceeds deterministic verification bound",
  "requested_tolerance": "1000",
  "max_allowed_tolerance": "0.02000000",
  "calculated_value": "2.000000",
  "precision_mode": "decimal",
  "status": "BLOCKED"
}
```

| Field | Type | Description |
| - | - | - |
| `requested_tolerance` | string | The tolerance value that was submitted |
| `max_allowed_tolerance` | string | The maximum tolerance the engine allows for the computed result |
| `calculated_value` | string | The engine's computed result |
| `precision_mode` | string | `"decimal"` or `"float"` depending on the computation path |

***

### POST /verify/logic

Verify logical constraints. Routes through the QWED Control Plane.

**Request:**

```json theme={null}
{
  "query": "(AND (GT x 5) (LT y 10))",
  "provider": "openai"
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | string | Yes | Logical constraint in DSL or natural language |
| `provider` | string | No | Preferred LLM provider |

**Response:**

```json theme={null}
{
  "status": "SAT",
  "model": {"x": "6", "y": "9"},
  "provider_used": "openai_compat"
}
```

The `provider_used` field indicates which LLM provider handled the translation step. This field is included in both success and error responses, which helps with debugging provider routing issues.

| Status | Meaning |
| - | - |
| `SAT` | Satisfiable — a solution exists |
| `UNSAT` | Unsatisfiable — no solution possible |
| `BLOCKED` | Request blocked by security policy (returns HTTP 403) |
| `ERROR` | Internal verification error |

**Error response:**

```json theme={null}
{
  "status": "ERROR",
  "error": "Internal verification error",
  "provider_used": "openai_compat"
}
```

***

### POST /verify/code

<Info>Updated in v7.0.0 (breaking)</Info>

Check code for security vulnerabilities using AST analysis. Returns the unified [`DiagnosticResult`](/advanced/diagnostics) shape plus a separate `admission` decision.

**Request:**

```json theme={null}
{
  "code": "import os\nos.system('rm -rf /')",
  "language": "python"
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `code` | string | Yes | Source code to analyze |
| `language` | string | No | Programming language (default: `python`) |

**Response (unsafe code — proven unsafe, not admitted):**

```json theme={null}
{
  "status": "VERIFIED",
  "agent_message": "The code failed security verification and is not safe to use.",
  "developer_fields": {
    "constraint_id": "code_verifier.code_unsafe",
    "is_safe": false,
    "is_valid": false,
    "language": "python",
    "critical_count": 1,
    "issues": [
      {
        "severity": "CRITICAL",
        "type": "os.system",
        "line_number": 2,
        "description": "Shell command execution"
      }
    ]
  },
  "proof_ref": "sha256:...",
  "is_authoritative": true,
  "admission": "BLOCKED"
}
```

| Field | Type | Description |
| - | - | - |
| `status` | string | `VERIFIED` when the scan completed (safe **or** unsafe code); `BLOCKED` when verification itself failed (empty code, unsupported language, internal error) |
| `admission` | string | `ADMIT` or `BLOCKED` — the fail-closed admission decision. `ADMIT` only when `status` is `VERIFIED` **and** `developer_fields.is_valid` is `true` |
| `developer_fields.is_valid` | boolean | The safety gate — `true` only when no critical issue was found |
| `developer_fields.issues` | array | Detected vulnerabilities with severity, type, line number, and description |
| `proof_ref` | string | Present on `VERIFIED` results (including verified-unsafe); `null` on `BLOCKED` |

<Warning>
  **Breaking change (v7.0.0):** proven-unsafe code now returns HTTP 200 with `status: "VERIFIED"` (previously `BLOCKED`). `VERIFIED` answers "was the code checked?", not "is it safe to execute?". Consumers that gated on `status` for safety must switch to the `admission` field or `developer_fields.is_valid`.
</Warning>

***

### POST /verify/sql

<Info>Updated in v7.0.0 (breaking)</Info>

Validate SQL queries against a provided schema. Returns the unified [`DiagnosticResult`](/advanced/diagnostics) shape plus a separate `admission` decision.

**Request:**

```json theme={null}
{
  "query": "SELECT * FROM users WHERE id = 1",
  "schema_ddl": "CREATE TABLE users (id INT, name TEXT)",
  "dialect": "sqlite"
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | string | Yes | SQL query to validate |
| `schema_ddl` | string | Yes | DDL schema definition (e.g., `CREATE TABLE` statements) |
| `dialect` | string | No | SQL dialect (default: `sqlite`) |

**Response (safe query):**

```json theme={null}
{
  "status": "VERIFIED",
  "agent_message": "The SQL query passed verification and is safe to execute.",
  "developer_fields": {
    "constraint_id": "sql_verifier.sql_valid",
    "is_valid": true,
    "malicious_classification": false
  },
  "proof_ref": "sha256:...",
  "is_authoritative": true,
  "admission": "ADMIT"
}
```

A proven-malicious query returns `status: "VERIFIED"` with `developer_fields.is_valid: false`, `developer_fields.malicious_classification: true`, and `admission: "BLOCKED"` — proving malice is a successful proof, so the truth verdict is preserved while admission is denied. `BLOCKED` status is reserved for verification failures: `sql_verifier.parse_error`, `sql_verifier.schema_parse_error`, `sql_verifier.complexity_limit_exceeded`, and `sql_verifier.execution_error`. See the [SQL engine](/engines/sql) page for the full contract.

***

### POST /verify/fact

Verify a factual claim against a provided context.

**Request:**

```json theme={null}
{
  "claim": "Paris is the capital of France",
  "context": "France is a country in Western Europe. Its capital is Paris.",
  "provider": "anthropic"
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `claim` | string | Yes | The factual statement to verify |
| `context` | string | Yes | Reference text to verify the claim against |
| `provider` | string | No | Preferred LLM provider |

**Response:**

```json theme={null}
{
  "verdict": "SUPPORTED",
  "confidence": 0.98,
  "reasoning": "The context explicitly states that the capital of France is Paris"
}
```

| Verdict | Meaning |
| - | - |
| `SUPPORTED` | Claim is supported by the context |
| `REFUTED` | Claim contradicts the context |
| `INCONCLUSIVE` | Not enough evidence in the context |

***

### POST /verify/consensus

<Info>New in v4.0.0 · Updated in v5.0.0</Info>

Multi-engine consensus verification. Runs the query through multiple verification engines and requires agreement above a confidence threshold. This endpoint is rate-limited per API key.

Mathematical expressions are parsed and compared using `Decimal` arithmetic internally, which avoids floating-point drift when engines cross-check results.

<Note>The fact engine is excluded from automatic engine selection during consensus verification. Fact-based verification requires external context and will return an error if invoked without it, preventing self-referential consensus loops.</Note>

**Request:**

```json theme={null}
{
  "query": "The square root of 144 is 12",
  "verification_mode": "high",
  "min_confidence": 0.95
}
```

| Parameter | Type | Required | Default | Description |
| - | - | - | - | - |
| `query` | string | Yes | — | The claim to verify |
| `verification_mode` | string | No | `single` | `single`, `high`, or `maximum` |
| `min_confidence` | float | No | `0.95` | Minimum confidence threshold, 0–1 |

**Verification modes:**

| Mode | Engines | Use case |
| - | - | - |
| `single` | 1 | Fast, single engine verification |
| `high` | 2 | Higher confidence for important claims |
| `maximum` | 3+ | Critical domains (medical, financial) |

**Response:**

```json theme={null}
{
  "final_answer": "12",
  "confidence": 98.5,
  "engines_used": 2,
  "agreement_status": "UNANIMOUS",
  "verification_chain": [
    {
      "engine": "math",
      "method": "symbolic",
      "result": "12",
      "confidence": 99.0,
      "latency_ms": 12.5,
      "success": true,
      "status": "VERIFIED"
    }
  ],
  "total_latency_ms": 45.2,
  "meets_requirement": true
}
```

| Field | Type | Description |
| - | - | - |
| `confidence` | float | Confidence as a percentage (0–100) |
| `verification_chain` | array | Detailed results from each engine |
| `verification_chain[].status` | string | Trust-boundary status from that engine — `VERIFIED`, `UNVERIFIABLE`, or `BLOCKED` |
| `meets_requirement` | boolean | True only when consensus is `VERIFIED` at the trust boundary **and** `confidence` meets `min_confidence`. Confidence alone is never sufficient. |

<Note>
  Only unanimous `VERIFIED` engine outcomes can produce a `VERIFIED` consensus. Advisory-only sub-engines (statistical computation, code execution) always return `UNVERIFIABLE`, so a query answered solely by those paths never satisfies `meets_requirement`. See the [Consensus engine](/engines/consensus#sub-engine-status-contract) page for the full sub-engine contract.
</Note>

| Status | Description |
| - | - |
| 400 | Invalid verification mode |
| 422 | Consensus confidence below the requested `min_confidence` threshold |
| 503 | Secure Docker sandbox unavailable — required for Python engine in `high`/`maximum` mode |

***

### POST /verify/image

Verify claims about image content. Accepts multipart form data with an image file (max 10 MB). Supported formats: PNG, JPEG, GIF, WebP.

**Request:**

```bash theme={null}
curl -X POST https://api.qwedai.com/v1/verify/image \
  -H "X-API-Key: qwed_your_key" \
  -F "image=@photo.jpg" \
  -F "claim=The image is 800x600 pixels"
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `image` | file | Yes | Image file (max 10 MB; PNG, JPEG, GIF, WebP) |
| `claim` | string | Yes | The claim about the image to verify |

**Response:**

```json theme={null}
{
  "verdict": "SUPPORTED",
  "confidence": 0.95,
  "reasoning": "Image dimensions match the claimed resolution",
  "methods_used": ["metadata_analysis", "pixel_inspection"]
}
```

| Verdict | Meaning |
| - | - |
| `SUPPORTED` | Claim is confirmed by image analysis |
| `REFUTED` | Claim contradicts image analysis |
| `INCONCLUSIVE` | Cannot determine from available methods |
| `VLM_REQUIRED` | Visual Language Model needed for deeper analysis |

***

### POST /verify/stats

Verify statistical claims against CSV data. Accepts multipart form data with a CSV file.

Statistical code execution requires the secure Docker sandbox. If Docker is unavailable, the endpoint returns HTTP 503. If the verification is blocked by a security policy, it returns HTTP 403.

**Request:**

```bash theme={null}
curl -X POST https://api.qwedai.com/v1/verify/stats \
  -H "X-API-Key: qwed_your_key" \
  -F "file=@data.csv" \
  -F "query=The average salary is above 50000"
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `file` | file | Yes | CSV data file |
| `query` | string | Yes | Statistical claim to verify |

**Response (execution succeeded):**

```json theme={null}
{
  "status": "UNVERIFIABLE",
  "agent_message": "Statistical analysis completed, but the claim could not be deterministically verified.",
  "developer_fields": {
    "constraint_id": "stats_verifier.claim_not_verified",
    "is_valid": false,
    "observed_result": 52340.5,
    "columns": ["salary"],
    "dataset_sha256": "9f2c...",
    "sandbox_type": "docker",
    "execution_time_ms": 412.7
  },
  "proof_ref": null,
  "is_authoritative": false
}
```

<Warning>
  **Breaking change (v7.0.0):** the endpoint returns the unified [`DiagnosticResult`](/advanced/diagnostics) shape instead of the legacy `{"status": "SUCCESS" | "ERROR" | "BLOCKED", "result": ..., "code": ...}` shape. Successful execution now reports `status: "UNVERIFIABLE"` with the observed value in `developer_fields.observed_result` — execution success alone is never presented as a proven claim, so `VERIFIED` is not emitted from this endpoint.
</Warning>

Failure states return `status: "BLOCKED"` with a `constraint_id` of `stats_verifier.validation_error` (translation or security validation failed), `stats_verifier.execution_failure` (sandbox execution failed), or `stats_verifier.runtime_unavailable` (Docker unavailable). Blocked results carry no `proof_ref`.

| Status | Description |
| - | - |
| 403 | Verification blocked by security policy (e.g., generated code failed AST safety checks) |
| 503 | Secure Docker sandbox unavailable — statistical verification requires Docker |

<Note>
  Statistical verification requires a running Docker daemon. If Docker is unavailable, the endpoint returns HTTP 503 instead of falling back to in-process execution. See the [Stats engine](/engines/stats) page for details.
</Note>

***

### POST /verify/process

<Info>New in v4.0.1</Info>

Verify the structural integrity of LLM reasoning traces. Supports IRAC structural compliance checking and custom milestone validation with decimal scoring.

**Request (IRAC mode):**

```json theme={null}
{
  "trace": "The issue is whether the contract was breached. The rule is Article 2 of the UCC. Applying this rule, the defendant failed to deliver on time. In conclusion, breach occurred.",
  "mode": "irac"
}
```

**Request (milestones mode):**

```json theme={null}
{
  "trace": "Risk assessment complete. Compliance check passed. Implementation timeline defined.",
  "mode": "milestones",
  "milestones": ["risk assessment", "compliance check", "implementation"]
}
```

| Parameter | Type | Required | Default | Description |
| - | - | - | - | - |
| `trace` | string | Yes | — | The LLM reasoning trace to validate |
| `mode` | string | No | `irac` | `irac` or `milestones` |
| `milestones` | string\[] | Conditional | — | Required when `mode` is `milestones` |

**Response (IRAC mode):**

```json theme={null}
{
  "verified": true,
  "score": 1.0,
  "missing_steps": [],
  "mechanism": "Regex Pattern Matching (Deterministic)"
}
```

**Response (milestones mode):**

```json theme={null}
{
  "verified": true,
  "process_rate": 1.0,
  "missed_milestones": []
}
```

| Status | Description |
| - | - |
| 400 | Invalid mode or missing `milestones` when `mode` is `milestones` |

***

### POST /verify/rag

<Info>New in v4.0.1</Info>

Verify that retrieved RAG chunks originate from the expected source document. Prevents Document-Level Retrieval Mismatch (DRM) hallucinations in RAG pipelines.

**Request:**

```json theme={null}
{
  "target_document_id": "contract_nda_v2",
  "chunks": [
    { "id": "c1", "metadata": { "document_id": "contract_nda_v2" } },
    { "id": "c2", "metadata": { "document_id": "contract_nda_v1" } }
  ],
  "max_drm_rate": "0"
}
```

| Parameter | Type | Required | Default | Description |
| - | - | - | - | - |
| `target_document_id` | string | Yes | — | Expected source document ID |
| `chunks` | object\[] | Yes | — | Array of chunk objects with metadata |
| `max_drm_rate` | string | No | `"0"` | Maximum tolerable mismatch fraction as a `Fraction`-compatible string (e.g. `"0"`, `"1/10"`) |

<Note>`max_drm_rate` accepts only string values for symbolic precision. Use fraction notation like `"1/10"` instead of `0.1`.</Note>

**Response:**

```json theme={null}
{
  "verified": false,
  "risk": "DOCUMENT_RETRIEVAL_MISMATCH",
  "drm_rate": 0.5,
  "chunks_checked": 2,
  "mismatched_count": 1
}
```

| Status | Description |
| - | - |
| 400 | Invalid request payload (empty `target_document_id`, empty `chunks`, or invalid `max_drm_rate` value) |

***

### POST /verify/batch

Verify multiple items in a single request. Processes all items concurrently and returns aggregated results. Maximum 100 items per batch.

**Request:**

```json theme={null}
{
  "items": [
    {"query": "What is 2+2?", "type": "natural_language"},
    {"query": "(AND (GT x 5) (LT y 10))", "type": "logic"},
    {"query": "x**2 + 2*x + 1 = (x+1)**2", "type": "math"}
  ]
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `items` | array | Yes | Array of verification items (max 100) |
| `items[].query` | string | Yes | The claim to verify |
| `items[].type` | string | No | Verification type: `natural_language`, `logic`, `math`, `code`, `fact`, `sql` (default: `natural_language`) |
| `items[].params` | object | No | Additional parameters for the verification |

**Response:**

Each entry in `items` is a per-item response. For math items the verdict is the [`DiagnosticResult`](/advanced/diagnostics) record nested under the item's `result` — the same three-layer verdict shape used by `POST /verify/math` — so batch and single-item math verifications share one contract:

* `result.status` — `VERIFIED`, `UNVERIFIABLE`, or `BLOCKED`.
* `result.agent_message` — short human-readable summary safe to surface to the caller.
* `result.developer_fields` — engine detail (including the legacy `is_valid` flag for backward compatibility).
* `result.proof_ref` — cryptographic hash of the retained proof artifact. Present only when `result.status == VERIFIED`; `null` otherwise. **This is the authority bit** — downstream gates must reject any item whose `proof_ref` is `null`.

```json theme={null}
{
  "job_id": "batch_abc123",
  "status": "completed",
  "progress_percent": 100.0,
  "total_items": 2,
  "completed_items": 2,
  "failed_items": 0,
  "items": [
    {
      "id": "batch_abc123-0",
      "query": "x**2 + 2*x + 1 = (x+1)**2",
      "type": "math",
      "status": "completed",
      "result": {
        "status": "VERIFIED",
        "agent_message": "Identity verified",
        "developer_fields": {
          "query": "x**2 + 2*x + 1 = (x+1)**2",
          "type": "math",
          "is_valid": true,
          "diff": "0"
        },
        "proof_ref": "sha256:9f2c…",
        "is_authoritative": true
      },
      "latency_ms": 12.3
    },
    {
      "id": "batch_abc123-1",
      "query": "x + x",
      "type": "math",
      "status": "completed",
      "result": {
        "status": "UNVERIFIABLE",
        "agent_message": "Expression simplified, but no equality or proof claim was provided",
        "developer_fields": {
          "query": "x + x",
          "type": "math",
          "is_valid": false,
          "simplified": "2*x"
        },
        "proof_ref": null,
        "is_authoritative": false
      },
      "latency_ms": 8.1
    }
  ]
}
```

#### Math items: proof vs. simplification

Math items are routed through the trust boundary (`enforce_trust_decision(require_attestation=True)` on VERIFIED, with a signed attestation issued via `create_verification_attestation`). Three outcomes are possible:

| Result | `status` | `proof_ref` | When |
| - | - | - | - |
| Verified identity | `VERIFIED` | `sha256:...` | Equality claim (e.g., `x**2 + 2*x + 1 = (x+1)**2`) simplifies to `0` **and** attestation signing succeeds. |
| Not equal | `UNVERIFIABLE` | `null` | Equality claim where the two sides do not simplify to the same expression. `developer_fields.is_valid` is `false` and `developer_fields.diff` shows the non-zero difference. |
| Simplification only | `UNVERIFIABLE` | `null` | Expression without an equality (e.g., `x + x`). `developer_fields.simplified` contains the simplified form. |

If attestation signing fails on an otherwise-verified identity, the item is downgraded to `UNVERIFIABLE` with `developer_fields.constraint_id = "api.attestation.signing_error"` — a math result is never returned as `VERIFIED` without a signed proof.

<Warning>
  Bare expressions (no `=`) always return `UNVERIFIABLE`. A successful symbolic simplification is not a verified claim. To get a `VERIFIED` outcome from a math item, submit an equality (e.g., `x + x = 2*x`).
</Warning>

<Info>
  The pre-v6.0 top-level `is_valid` field is preserved inside `developer_fields.is_valid` for backward compatibility, but new code should gate on `result.status == "VERIFIED"` and the presence of `result.proof_ref`. The batch response does not include a pass/fail aggregate because a boolean cannot represent the three-status verdict — count `items[].result.status == "VERIFIED"` entries directly if you need a success ratio.
</Info>

### GET /verify/batch/{job_id}

Get the status and results of a batch verification job. Use this to poll results when processing large batches.

**Response:**

```json theme={null}
{
  "job_id": "batch_abc123",
  "status": "completed",
  "items": [...]
}
```

| Status | Description |
| - | - |
| 403 | Access denied — job belongs to a different organization |
| 404 | Job not found |

***

## Verification Context endpoints

<Info>New in v7.1.0</Info>

These endpoints work with [Verification Context v1.0](/specs/verification-context) documents — the standardized JSON record of a verification. All three require an API key and are rate-limited per key.

### POST /verification-context/from-diagnostic

Convert a [`DiagnosticResult`](/advanced/diagnostics) into a schema-valid Verification Context document. Use this when you have an engine verdict and need the standardized VC record for auditing or downstream gating.

**Request:**

```json theme={null}
{
  "query": "x**2 + 2*x + 1 = (x+1)**2",
  "verifier": "MathVerifier",
  "diagnostic": {
    "status": "VERIFIED",
    "agent_message": "Identity verified",
    "developer_fields": {"is_valid": true},
    "proof_ref": "sha256:9f2c…"
  },
  "verifier_version": "7.1.0",
  "attestation_token": "<ES256 JWT attestation>"
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | string | Yes | The formal statement that was verified |
| `verifier` | string | Yes | Name of the engine that produced the diagnostic |
| `diagnostic` | object | Yes | The `DiagnosticResult` record (`status` / `agent_message` / `developer_fields` / `proof_ref`) |
| `verifier_version` | string | No | Engine version. Defaults to the installed `qwed` package version |
| `attestation_token` | string | No | Signed attestation for the diagnostic. Required to keep a `VERIFIED` status |

**Response:** the full Verification Context document (`spec_version`, `object`, `context`, `verdict`).

**Fail-closed behavior:**

* A malformed `diagnostic` payload is converted to a `BLOCKED` document instead of being rejected, so the audit trail records the failure.
* A `VERIFIED` diagnostic without an `attestation_token` fails closed to `BLOCKED` (`trust_gate.mandatory_attestation_missing`). A supplied invalid token fails closed to `BLOCKED` (`trust_gate.invalid_attestation_token`); a valid token with mismatched claims uses the corresponding `trust_gate.claims_*` code. Attestation is enforced through the same trust boundary as `/verify/*`.

| Status | Description |
| - | - |
| 422 | The resulting document failed Verification Context validation |
| 500 | Audit persistence failed |

### POST /verification-context/validate

Validate a Verification Context document against the v1.0 JSON Schema, including the verdict/`proof_ref` invariants.

**Request:**

```json theme={null}
{
  "document": {
    "spec_version": "1.0",
    "object": {"formal_statement": "x**2 + 2*x + 1 = (x+1)**2"},
    "context": {
      "interpretation": {"theory": "real-closed field", "logic": "first-order logic"},
      "proof": {"verifier": "qwed", "verifier_version": "7.1.0"},
      "evidence": {"evidence": {"status": "VERIFIED"}, "proof_ref": "sha256:9f2c…"},
      "decision": {"admission": "ADMIT"}
    },
    "verdict": "VERIFIED"
  }
}
```

**Response:**

```json theme={null}
{"valid": true}
```

An invalid document returns `{"valid": false, "error": "validation_failed"}` with HTTP 200. Gate on the `valid` field.

### POST /verification-context/resolve

Resolve the `proof_ref` evidence commitment of a Verification Context document. The server removes the stored `proof_ref`, re-derives the SHA-256 commitment over the canonical (RFC 8785) bound payload, and compares it to the stored value.

**Request:**

```json theme={null}
{
  "document": {
    "spec_version": "1.0",
    "object": {"formal_statement": "x**2 + 2*x + 1 = (x+1)**2"},
    "context": {
      "interpretation": {"theory": "real-closed field", "logic": "first-order logic"},
      "proof": {"verifier": "qwed", "verifier_version": "7.1.0"},
      "evidence": {"evidence": {"status": "VERIFIED"}, "proof_ref": "sha256:9f2c…"},
      "decision": {"admission": "ADMIT"}
    },
    "verdict": "VERIFIED"
  }
}
```

**Response:**

```json theme={null}
{"resolved": true}
```

`resolved` is `true` only when the document has `verdict: "VERIFIED"`, validates against the schema, and the stored `proof_ref` matches the re-derived commitment. Any other outcome (non-`VERIFIED` verdict, schema failure, mismatched or missing commitment) returns `{"resolved": false}` — a `proof_ref` that cannot be resolved confers no authority.

***

## Agent endpoints

### POST /agents/register

Register a new AI agent with QWED for verified agentic workflows.

**Request:**

```json theme={null}
{
  "name": "FinanceBot",
  "agent_type": "semi_autonomous",
  "description": "Financial analysis agent",
  "permissions": ["math", "logic", "code"],
  "max_cost_per_day": 50.0
}
```

| Parameter | Type | Required | Default | Description |
| - | - | - | - | - |
| `name` | string | Yes | — | Agent display name |
| `agent_type` | string | No | `autonomous` | `autonomous`, `semi_autonomous`, or `assistant` |
| `description` | string | No | — | Agent description |
| `permissions` | array | No | `[]` | Allowed verification engine names (e.g., `math`, `logic`, `code`) |
| `max_cost_per_day` | float | No | `100.0` | Daily budget cap in USD |

**Agent types:**

| Type | Description |
| - | - |
| `autonomous` | Fully autonomous agent (AutoGPT-style) |
| `semi_autonomous` | Requires approval for critical actions |
| `assistant` | Human-in-the-loop |

**Response:**

```json theme={null}
{
  "agent_id": 42,
  "agent_token": "qwed_agent_...",
  "name": "FinanceBot",
  "type": "semi_autonomous",
  "status": "active",
  "max_cost_per_day": 50.0,
  "message": "Agent registered successfully. Store the agent_token securely."
}
```

<Warning>Store the `agent_token` immediately — it cannot be retrieved again after registration.</Warning>

***

### POST /agents/{agent_id}/verify

<Info>Updated in v5.0.0</Info>

Verify a claim using an agent token. Creates an auditable record tied to the agent. Security checks are enforced server-side — exfiltration detection always runs, and MCP poisoning detection runs automatically when a `tool_schema` is provided.

<Warning>**Breaking change (v5.0.0):** The `security_checks` request field has been removed. Security checks are now mandatory and enforced server-side. You no longer need to (or can) opt in to exfiltration or MCP poison checks.</Warning>

<Warning>**Breaking change (v5.0.0):** The `context` field with `conversation_id` and `step_number` is now required for all agent action verification requests. Requests without these fields are rejected with error code `QWED-AGENT-CTX-001`. See [conversation controls](/advanced/agent-verification#conversation-controls) for details.</Warning>

**Request:**

```json theme={null}
{
  "query": "What is 15% of 200?",
  "provider": "openai",
  "context": {
    "conversation_id": "conv_abc123",
    "step_number": 1,
    "pre_action_state_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "state_source": "db_snapshot"
  },
  "tool_schema": {
    "name": "fetch_report",
    "description": "Fetch quarterly report data"
  }
}
```

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | string | Yes | The claim to verify |
| `provider` | string | No | LLM provider preference |
| `context` | object | Yes | Action context — see below |
| `context.conversation_id` | string | Yes | Unique identifier for the conversation/session |
| `context.step_number` | integer | Yes | Monotonically increasing step counter (>= 1) |
| `context.pre_action_state_hash` | string | Conditional | SHA-256 hex digest (64 lowercase hex characters) of the world state before the action. Must be provided together with `state_source`. Enables [LOOP-004](/specs/agent#9-4-progress-aware-doom-loop-detection-loop-004) detection |
| `context.state_source` | string | Conditional | Declares how `pre_action_state_hash` was derived. One of: `file_tree`, `db_snapshot`, `conversation_digest`, `git_tree`, `custom`. Must be provided together with `pre_action_state_hash` |
| `tool_schema` | object | No | MCP tool definition to scan. When provided, `MCPPoisonGuard` runs automatically |

**Headers:**

```text theme={null}
X-Agent-Token: qwed_agent_...
```

**Security behavior:**

* **Exfiltration check:** Always runs on every agent verification request. If the query payload is flagged, the request is rejected with a `403`.
* **MCP poison check:** Runs automatically when `tool_schema` is present. If the tool definition is flagged, the request is rejected with a `403`.

**Error responses:**

| Status | Description |
| - | - |
| 401 | Invalid agent token |
| 403 | Agent budget exceeded or request blocked by security checks |
| 500 | Internal agent verification error |

***

### POST /agents/{agent_id}/tools/{tool_name}

Submit an agent tool call for risk evaluation before execution. Unknown tools (not on the safe or dangerous operations list) are denied by default, regardless of risk score. See [tool approval policy](/advanced/agent-verification#tool-approval-policy) for details.

**Request:**

```json theme={null}
{
  "tool_params": {
    "query": "SELECT * FROM users",
    "dialect": "postgresql"
  }
}
```

**Possible outcomes:**

| Outcome | When |
| - | - |
| Approved | Tool is on the safe operations list |
| Blocked (manual approval required) | Tool is on the dangerous operations list |
| Blocked (default-deny) | Tool is not on either list |

***

### GET /agents/{agent_id}/activity

Retrieve the audit log for a specific agent. Provides a full audit trail of all agent actions.

**Headers:**

```text theme={null}
X-Agent-Token: qwed_agent_...
```

**Query params:**

| Parameter | Type | Default | Description |
| - | - | - | - |
| `limit` | integer | `20` | Maximum number of activity records to return |

**Response:**

```json theme={null}
{
  "agent_id": 42,
  "agent_name": "FinanceBot",
  "total_activities": 5,
  "current_cost_today": 0.05,
  "max_cost_per_day": 50.0,
  "activities": [
    {
      "type": "verification_request",
      "description": "Query: What is 15% of 200?",
      "status": "success",
      "cost": 0.01,
      "timestamp": "2026-03-20T12:00:00Z"
    }
  ]
}
```

| Status | Description |
| - | - |
| 401 | Invalid agent token |

***

## Attestation endpoints

### GET /attestation/{id}

Get an attestation by ID.

### POST /attestation/verify

Verify an attestation JWT.

**Request:**

```json theme={null}
{
  "jwt": "eyJhbGciOiJFUzI1NiIs..."
}
```

***

## Observability endpoints

### GET /metrics

Returns global system metrics and per-tenant breakdowns. This is a cross-tenant, platform-operator capability: the caller must be a user ID listed in `QWED_METRICS_OPERATOR_USER_IDS` (comma-separated, fail-closed when unset). Organization roles (`owner` / `admin`) do **not** grant access — they are scoped to a single organization.

**Headers (one of):**

```text theme={null}
Authorization: Bearer <jwt_token>
X-API-Key: <api_key>
```

An API key resolves through its owning user, so the key must belong to an active user on the allowlist. Expired or revoked keys are not usable credentials; if a request carries both an API key and a JWT, the JWT can still authorize the read.

**Response:**

```json theme={null}
{
  "global": { "total_requests": 1250, "avg_latency_ms": 82.3 },
  "tenants": { "1": { "requests": 500 }, "2": { "requests": 750 } }
}
```

| Status | Description |
| - | - |
| 401 | No usable authentication provided (no credentials, or only an expired API key) |
| 403 | Authenticated, but the user is not in `QWED_METRICS_OPERATOR_USER_IDS` |

### GET /metrics/{organization_id}

Returns metrics scoped to a specific organization. Tenants can only view their own metrics.

| Status | Description |
| - | - |
| 403 | You can only view metrics for your own organization |

### GET /metrics/prometheus

Returns metrics in Prometheus text format for scraping by monitoring infrastructure. Requires the same platform-operator authentication as `GET /metrics`.

**Headers (one of):**

```text theme={null}
Authorization: Bearer <jwt_token>
X-API-Key: <api_key>
```

| Status | Description |
| - | - |
| 401 | No usable authentication provided (no credentials, or only an expired API key) |
| 403 | Authenticated, but the user is not in `QWED_METRICS_OPERATOR_USER_IDS` |

### GET /logs

Returns verification logs for the authenticated tenant, ordered by most recent first.

**Query params:**

| Parameter | Type | Default | Description |
| - | - | - | - |
| `limit` | integer | `10` | Maximum number of logs to return |

**Response:**

```json theme={null}
{
  "organization_id": 1,
  "organization_name": "Acme Corp",
  "total_logs": 3,
  "logs": [
    {
      "id": 42,
      "query": "What is 2+2?",
      "is_verified": true,
      "domain": "MATH",
      "timestamp": "2026-03-20T12:00:00Z"
    }
  ]
}
```

***

## Admin endpoints

<Info>New in v4.0.0</Info>

These endpoints require the `admin:all` API key scope.

### GET /admin/compliance/export/csv

Export the full audit trail as a CSV file.

### GET /admin/compliance/verify/{log_id}

Cryptographically verify a specific audit log entry using HMAC-SHA256 and hash-chain validation.

The response reports three independent checks: payload hash, HMAC signature (compared in constant time), and chain linkage to the prior entry within the same organization. Genesis entries must have a `null` `previous_hash`; non-genesis entries must link to a non-empty prior entry hash. Entries with malformed stored result payloads cause verification to fail closed with `SecurityError` instead of returning `"valid": true`.

Hash matching accepts either the current canonical payload (which includes `raw_llm_output`) or the legacy canonical payload, so entries written before that field was covered remain verifiable after the upgrade.

**Response:**

```json theme={null}
{
  "valid": true,
  "checks": {
    "hash_valid": true,
    "signature_valid": true,
    "chain_valid": true
  },
  "errors": [],
  "log_id": 42,
  "timestamp": "2026-03-20T12:00:00Z"
}
```

<Note>
  `AuditLogger` requires `QWED_AUDIT_SECRET_KEY` to be set. If the key is missing or persisted chain continuity cannot be loaded, initialization raises `SecurityError` and verification endpoints fail closed.
</Note>

### GET /admin/compliance/report/soc2/{org_id}

Generate a SOC 2 Type II compliance report for an organization.

### GET /admin/security/threats/{org_id}

Returns a real-time threat summary for an organization, including blocked injection attempts and anomalous patterns.

### POST /admin/keys/rotate

Rotate an API key. Available to admin and member roles.

**Request:**

```json theme={null}
{
  "key_id": "key_abc123"
}
```

***

## Badge endpoints

All badge endpoints return SVG images (`image/svg+xml`). You can embed them directly in Markdown or HTML.

### GET /badge/verified

Get a verified or failed badge SVG.

| Parameter | Type | Default | Description |
| - | - | - | - |
| `verified` | boolean | `true` | Whether to show a verified or failed badge |

### GET /badge/status/{status}

Get a badge for any verification status (e.g., `VERIFIED`, `FAILED`, `CORRECTED`, `BLOCKED`, `PENDING`, `ERROR`).

### GET /badge/attestation/{attestation_id}

Get a badge for a specific attestation by ID.

### GET /badge/engine/{engine}

Get a badge for a specific verification engine.

| Parameter | Type | Default | Description |
| - | - | - | - |
| `verified` | boolean | `true` | Whether to show a verified or failed badge |

### GET /badge/custom

Generate a custom badge with configurable label, message, color, and logo.

| Parameter | Type | Default | Description |
| - | - | - | - |
| `label` | string | `QWED` | Left side label |
| `message` | string | `verified` | Right side message |
| `color` | string | — | Hex color (e.g., `#00C853`) |
| `logo` | boolean | `true` | Include QWED logo |

***

## Webhook endpoints

### POST /webhooks/secret-scanning

Receive signed secret-leak notifications from GitHub secret scanning (or any GitHub-compatible partner scanner). The endpoint is the intake side of the leaked-QWED-key revocation pipeline: authenticated reports of leaked tokens are handed to the revocation sink after signature verification succeeds.

The endpoint is **intentionally unauthenticated** — it must be reachable by external scanners that have no QWED credentials. The ECDSA signature over the raw request body is the only credential; nothing in the payload is parsed, logged, or persisted until the signature is verified against GitHub's published public keys.

**Request headers:**

| Header | Required | Description |
| - | - | - |
| `Github-Public-Key-Identifier` | Yes | Key identifier (`key_identifier`) of the signing key from `https://api.github.com/meta/public_keys/secret_scanning` |
| `Github-Public-Key-Signature` | Yes | Base64-encoded ASN.1/DER ECDSA-NIST-P256V1-SHA256 signature over the raw request body |

**Request body:**

A non-empty JSON array of match objects. Extra fields on each item are ignored so scanners can add informational fields over time.

```json theme={null}
[
  {
    "token": "qwed_live_KEY",
    "type": "qwed_live_api_key",
    "url": "https://github.com/acme-corp/billing-api/blob/main/config.py",
    "source": "commit"
  }
]
```

| Field | Type | Required | Description |
| - | - | - | - |
| `token` | string | Yes | The leaked token (1–512 characters). Never logged. |
| `type` | string | Yes | Scanner-declared token type (1–128 characters) |
| `url` | string | No | Location where the token was found (max 4096 characters) |
| `source` | string | No | Scanner-declared source (max 64 characters). Defaults to `"unknown"` |

**Envelope caps:**

* Request body: 1 MiB. The stream is bounded incrementally — an over-cap body is rejected the moment the cap is exceeded and never fully buffered.
* Matches per request: 500.
* Signature header: 1024 characters before base64 decode.

**Response (200):**

```json theme={null}
{"received": 3}
```

A 200 acknowledges a **verified receipt**, not that downstream revocation has finished. Verified matches are handed to a background sink after the response is sent.

**Background handling of verified matches:**

* **Active key:** revoked immediately (`is_active=False`, `revoked_at` set). QWED writes a `SECRET_LEAK_AUTO_REVOKED` security event that carries only the `key_preview`, never the token.
* **Owner email:** sent to the key's user, or to the organization's first active owner or admin as a fallback. SMTP has a 10-second timeout. A failed notification never rolls back the revocation.
* **Already-revoked key or unknown token:** no revocation or owner notification. Redeliveries are idempotent and never send a second email. When false-positive feedback is enabled (next bullet), QWED still reports a label for the match — for an unknown token, `false_positive`.
* **Test keys:** registered scanner patterns cover `qwed_live_` keys only, so `qwed_test_` keys are not reported. The revocation sink itself does not filter by prefix.
* **False-positive feedback (opt-in):** when `QWED_LEAK_FEEDBACK_ENABLED=true`, QWED sends a `true_positive` or `false_positive` label per match to `QWED_LEAK_FEEDBACK_URL` after the revocation attempt. Items carry SHA-256 token hashes unless `QWED_LEAK_FEEDBACK_SEND_RAW=true` is set with an HTTPS URL.

See [Automatic revocation of leaked keys](/advanced/security-hardening#automatic-revocation-of-leaked-keys) and [False-positive feedback reporter](/advanced/security-hardening#false-positive-feedback-reporter) for details.

**Status codes:**

| Status | Meaning |
| - | - |
| 200 | Signature verified; matches queued for background handling |
| 400 | Malformed envelope (non-JSON body, wrong shape, or a match object failed validation) |
| 401 | Missing `Github-Public-Key-Identifier` or `Github-Public-Key-Signature` header |
| 403 | Signature invalid, forged, tampered, or signed by an unknown key against a fresh trust anchor |
| 413 | Body exceeds 1 MiB or the batch exceeds 500 matches |
| 503 | Signing-key service unavailable, or the key that signed the request cannot be resolved after a rotation retry — the scanner should redeliver |

All signature-failure responses share a single generic detail (`"invalid webhook signature"`) so the failing verification step is not observable to the caller.

<Note>
  Configure your scanner (or a GitHub partner-program registration) to POST to `https://<your-qwed-host>/webhooks/secret-scanning`. No API key or bearer token is provisioned on QWED's side — the signature over the raw body is the sole credential, and QWED fetches the public keys from GitHub on demand. See [Security hardening — Secret-scanning webhook intake](/advanced/security-hardening#6-secret-scanning-webhook-intake) for the trust model and operator configuration.
</Note>


## Related topics

- [QWED compliance guide](/advanced/compliance.md)
- [Changelog](/changelog.md)
- [Prompt injection defense and QWED security hardening](/advanced/security-hardening.md)
- [Changelog Archive](/changelog-archive.md)
- [Python SDK](/sdks/python.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.