Base URL
https://api.qwedai.com/v1
Health check
GET /health
Check API status. No authentication required.
curl https://api.qwedai.com/v1/health
Response:
{
"status": "healthy",
"service": "QWED Platform",
"version": "5.1.0",
"timestamp": "2024-12-20T12:00:00Z"
}
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:
{
"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 for details.
{
"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 the result cannot be treated 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 |
The status field was previously set 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.
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" |
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 |
overall_status | string | Mirrors the top-level status field |
POST /verify/math
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.
Verify mathematical expressions or equations using SymPy symbolic computation.
Request:
{
"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):
{
"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):
{
"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.
{
"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):
{
"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:
{
"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:
{
"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:
{
"status": "ERROR",
"error": "Internal verification error",
"provider_used": "openai_compat"
}
POST /verify/code
Check code for security vulnerabilities using AST analysis.
Request:
{
"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:
{
"is_safe": false,
"vulnerabilities": [
{
"type": "os.system",
"severity": "critical",
"line": 2,
"message": "Shell command execution"
}
]
}
| Field | Type | Description |
|---|
is_safe | boolean | Whether the code passed all security checks |
vulnerabilities | array | List of detected vulnerabilities with type, severity, line number, and message |
POST /verify/sql
Validate SQL queries against a provided schema.
Request:
{
"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:
{
"is_valid": true,
"message": "Query is valid against the provided schema"
}
POST /verify/fact
Verify a factual claim against a provided context.
Request:
{
"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:
{
"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
New in v4.0.0 · Updated in v5.0.0
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.
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.
Request:
{
"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:
{
"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
}
],
"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 |
meets_requirement | boolean | Whether confidence meets min_confidence |
| 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:
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:
{
"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:
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:
{
"status": "SUCCESS",
"computed_value": 52340.5,
"message": "Statistical claim verified against dataset"
}
| 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 |
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 page for details.
POST /verify/process
Verify the structural integrity of AI reasoning traces. Supports IRAC structural compliance checking and custom milestone validation with decimal scoring.
Request (IRAC mode):
{
"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):
{
"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 AI reasoning trace to validate |
mode | string | No | irac | irac or milestones |
milestones | string[] | Conditional | — | Required when mode is milestones |
Response (IRAC mode):
{
"verified": true,
"score": 1.0,
"missing_steps": [],
"mechanism": "Regex Pattern Matching (Deterministic)"
}
Response (milestones mode):
{
"verified": true,
"process_rate": 1.0,
"missed_milestones": []
}
| Status | Description |
|---|
| 400 | Invalid mode or missing milestones when mode is milestones |
POST /verify/rag
Verify that retrieved RAG chunks originate from the expected source document. Prevents Document-Level Retrieval Mismatch (DRM) hallucinations in RAG pipelines.
Request:
{
"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") |
max_drm_rate accepts only string values for symbolic precision. Use fraction notation like "1/10" instead of 0.1.
Response:
{
"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:
{
"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:
{
"job_id": "batch_abc123",
"status": "completed",
"results": [...],
"summary": {
"total": 3,
"verified": 3,
"failed": 0,
"success_rate": 100.0
}
}
GET /verify/batch/
Get the status and results of a batch verification job. Use this to poll results when processing large batches.
Response:
{
"job_id": "batch_abc123",
"status": "completed",
"results": [...]
}
| Status | Description |
|---|
| 403 | Access denied — job belongs to a different organization |
| 404 | Job not found |
Agent endpoints
POST /agents/register
Register a new AI agent with QWED for verified agentic workflows.
Request:
{
"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:
{
"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."
}
Store the agent_token immediately — it cannot be retrieved again after registration.
POST /agents//verify
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.
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.
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 for details.
Request:
{
"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 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:
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//tools/
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 for details.
Request:
{
"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//activity
Retrieve the audit log for a specific agent. Provides a full audit trail of all agent actions.
Headers:
X-Agent-Token: qwed_agent_...
Query params:
| Parameter | Type | Default | Description |
|---|
limit | integer | 20 | Maximum number of activity records to return |
Response:
{
"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/
Get an attestation by ID.
POST /attestation/verify
Verify an attestation JWT.
Request:
{
"jwt": "eyJhbGciOiJFUzI1NiIs..."
}
Observability endpoints
GET /metrics
Returns global system metrics and per-tenant breakdowns. Requires admin authentication — you must provide either a JWT token for an active user with the owner or admin role, or an API key linked to an active owner or admin user.
Headers (one of):
Authorization: Bearer <jwt_token>
X-API-Key: <api_key>
Response:
{
"global": { "total_requests": 1250, "avg_latency_ms": 82.3 },
"tenants": { "1": { "requests": 500 }, "2": { "requests": 750 } }
}
| Status | Description |
|---|
| 401 | No authentication provided |
| 403 | Authenticated but not an active admin or owner |
GET /metrics/
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 admin authentication as GET /metrics.
Headers (one of):
Authorization: Bearer <jwt_token>
X-API-Key: <api_key>
| Status | Description |
|---|
| 401 | No authentication provided |
| 403 | Authenticated but not an active admin or owner |
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:
{
"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
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/
Cryptographically verify a specific audit log entry using HMAC and hash chain validation.
GET /admin/compliance/report/soc2/
Generate a SOC 2 Type II compliance report for an organization.
GET /admin/security/threats/
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:
{
"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/
Get a badge for any verification status (e.g., VERIFIED, FAILED, CORRECTED, BLOCKED, PENDING, ERROR).
GET /badge/attestation/
Get a badge for a specific attestation by ID.
GET /badge/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 |