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

# TypeScript SDK

> QWED TypeScript/JavaScript SDK documentation. Install via npm, yarn, or pnpm. Configure verification options and integrate with Node.js or browser apps.

The official TypeScript/JavaScript SDK for QWED.

## Installation

```bash theme={null}
npm install @qwed-ai/sdk
# or
yarn add @qwed-ai/sdk
# or
pnpm add @qwed-ai/sdk
```

## Quick start

```typescript theme={null}
import { QWEDClient, isAdmitted } from '@qwed-ai/sdk';

const client = new QWEDClient({ apiKey: 'qwed_your_key' });

try {
  const result = await client.verifyMath('2 + 2 = 4');
  console.log(result.status);      // "VERIFIED"
  console.log(result.admission);   // "ADMIT"
  console.log(isAdmitted(result)); // true
} catch (err) {
  // A network error or a non-2xx response rejects with a QWEDError.
  // Fail closed: treat the claim as not admitted.
  console.error(err);
}
```

## Configuration

```typescript theme={null}
const client = new QWEDClient({
  apiKey: 'qwed_...',
  baseUrl: 'http://localhost:8000',  // Optional, your local QWED server (the default)
  timeout: 30000,  // Optional, default 30s
});
```

## Reading a result

Every `verify*` method except `verifyAgent` returns a `VerificationResponse` that models the server's [DiagnosticResult](https://docs.qwedai.tech/advanced/diagnostics.md) envelope.

| Field | Meaning |
| - | - |
| `status` | `VERIFIED`, `UNVERIFIABLE`, or `BLOCKED`. `UNKNOWN` for a missing value or one this SDK doesn't recognize. |
| `rawStatus` | The server's status text exactly as sent, such as `SAT` where `status` is `UNKNOWN`. Never used for admission. |
| `admission` | `ADMIT` or `BLOCKED` where the endpoint makes a decision. `UNKNOWN` for a value this SDK doesn't recognize. Absent (`undefined`) where the endpoint makes no decision. |
| `proof_ref` | Set only when `status` is `VERIFIED`. |
| `attestation` | On a `VERIFIED` answer, the server's signed attestation, an ES256 JWT. Check it with `verifyAttestation`. |
| `agent_message` | Human-readable explanation. |
| `is_authoritative` | Whether the verdict is authoritative. |
| `constraint_id` | The constraint that produced the verdict. |
| `developer_fields` | Engine detail for debugging. |
| any other key | The endpoint's own fields, such as `is_valid`, `issues`, or `error`, typed as `unknown`. |

Numbers come from `JSON.parse`, so an integer above 2^53 or a long decimal is rounded. `JSON.stringify` of a parsed response writes the server's status text back as `status` when `status` is `UNKNOWN`, so a cached or logged response keeps it.

### Gate on `isAdmitted()`

Use `isAdmitted(result)` to decide whether to act on a result. Don't gate on `status` alone.

`VERIFIED` means the engine reached a deterministic answer, not that the claim holds. `2 + 2 = 5` returns `VERIFIED · BLOCKED` because it's proven false. Unsafe code returns `VERIFIED · BLOCKED` because it's proven unsafe.

`isAdmitted()` returns `true` only when all of these hold:

* `admission` is `ADMIT`
* `status` is `VERIFIED`
* `proof_ref` is a non-empty string

A missing or unknown admission is never admitted. Endpoints that make no admission decision (`/verify/logic`, `/verify/fact`, `/verify/rag`, `/verify/process`, and batch logic and fact items) are therefore never admitted. The top-level `verified` that `/verify/rag` and `/verify/process` send is their guard's own result. The parser replaces it, and the guard's value stays in `developer_fields.verified`.

```typescript theme={null}
import { isAdmitted } from '@qwed-ai/sdk';

try {
  const result = await client.verifyCode(code, { language: 'python' });
  if (isAdmitted(result)) {
    run(code);
  } else {
    // VERIFIED · BLOCKED means proven unsafe. Anything else is not verified.
    console.log('rejected:', result.status, result.admission, result.agent_message);
  }
} catch (err) {
  // A network error or a non-2xx response rejects with a QWEDError.
  // Fail closed: treat the claim as not admitted.
  console.error(err);
}
```

> [!NOTE]
>
> The `verified` field and `isVerified()` are deprecated. The parser always sets `verified` to `isAdmitted(result)`, whatever the body says, and `isVerified()` returns `isAdmitted()`.

### Parsing responses yourself

If you call the QWED API without the client, pass the parsed JSON body to `parseVerificationResponse()` or `parseBatchResponse()`. The client uses the same functions. They throw a `QWEDError` with code `QWED-SDK-PARSE-001` when a body isn't a verification result, and `QWED-SDK-PARSE-002` when a body isn't a batch job.

```typescript theme={null}
import { parseVerificationResponse, isAdmitted } from '@qwed-ai/sdk';

try {
  const res = await fetch('http://localhost:8000/verify/math', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-API-Key': 'qwed_...' },
    body: JSON.stringify({ expression: '2 + 2 = 4' }),
  });
  if (!res.ok) {
    throw new Error(`QWED request failed: ${res.status} ${await res.text()}`);
  }
  const result = parseVerificationResponse(await res.json());
  console.log(isAdmitted(result)); // true
} catch (err) {
  // Network error, non-2xx status, or a body that isn't a verification result
  // (QWEDError QWED-SDK-PARSE-001). Fail closed: treat the claim as not admitted.
  console.error(err);
}
```

## Verification methods

Every method returns a promise that rejects with a `QWEDError` on a network error or a non-2xx response. The short examples below leave that out; wrap your calls as the examples above and [Error handling](#error-handling) show, and treat a rejected call as not admitted.

### verify(query)

Auto-detect and verify any claim.

```typescript theme={null}
const result = await client.verify('What is 15% of 200?');
```

### verifyMath(expression)

Verify mathematical expressions.

```typescript theme={null}
const result = await client.verifyMath('x**2 + 2*x + 1 = (x+1)**2');
console.log(isAdmitted(result)); // true for a true identity
console.log(result.is_valid);    // true
```

### verifyLogic(query)

Verify logical constraints using QWED-Logic DSL.

```typescript theme={null}
const result = await client.verifyLogic('(AND (GT x 5) (LT y 10))');
// /verify/logic makes no admission decision: admission is undefined and
// isAdmitted() is false, whatever the status.
console.log(result.status, result.model);
```

### verifyCode(code, options)

Check code for security vulnerabilities.

```typescript theme={null}
const result = await client.verifyCode(code, { language: 'python' });
console.log(result.status, result.admission); // 'VERIFIED', 'BLOCKED' for unsafe code
console.log(isAdmitted(result));              // false
console.log(result.issues);                   // what was found (also in developer_fields.issues)
```

### verifyFact(claim, context)

Verify factual claims against a provided context.

```typescript theme={null}
const result = await client.verifyFact(
  'The company was founded in 2020',
  'Acme Corp was established in 2020 in San Francisco.'
);
// A heuristic match is not a proof: UNVERIFIABLE, with no admission.
console.log(result.status);                // 'UNVERIFIABLE'
console.log(result.deterministic_verdict); // 'SUPPORTED'
```

### verifySQL(query, schema)

Validate SQL queries against a schema.

```typescript theme={null}
const result = await client.verifySQL(
  'SELECT * FROM users WHERE id = 1',
  'CREATE TABLE users (id INT, name TEXT)'
);
```

### verifyProcess(reasoningTrace, options)

> [!NOTE]
>
> New in v4.0.1

Validate the structural integrity of LLM reasoning traces. Use this to ensure workflows follow deterministic process steps with IRAC pattern matching or custom milestone validation.

```typescript theme={null}
// Verify IRAC structure in a reasoning trace
const result = await client.verifyProcess(
  `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' }
);

// /verify/process makes no admission decision, so isAdmitted() is false.
// The guard's own verdict is in developer_fields.verified.
console.log(result.developer_fields?.verified); // true
console.log(result.score);                      // 1.0
console.log(result.missing_steps);              // []
```

You can also verify custom milestones:

```typescript theme={null}
const result = await client.verifyProcess(
  'Risk assessment complete. Compliance verified. Implementation planned.',
  {
    mode: 'milestones',
    keyMilestones: ['risk assessment', 'compliance', 'implementation'],
  }
);

console.log(result.process_rate);       // 1.0
console.log(result.missed_milestones);  // []
```

| Parameter | Type | Default | Description |
| - | - | - | - |
| `reasoningTrace` | `string` | — | The LLM reasoning trace to validate |
| `options.mode` | `'irac' \| 'milestones'` | `'irac'` | Verification mode |
| `options.keyMilestones` | `string[]` | — | Required milestones (milestones mode only) |

### verifyRAG(targetDocumentId, retrievedChunks, options)

> [!NOTE]
>
> New in v4.0.1

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

```typescript theme={null}
const result = await client.verifyRAG(
  'contract_nda_v2',
  [
    { id: 'c1', metadata: { document_id: 'contract_nda_v2' } },
    { id: 'c2', metadata: { document_id: 'contract_nda_v1' } }, // Wrong doc
  ],
  { maxDrmRate: '0' } // Zero tolerance
);

// /verify/rag makes no admission decision, so isAdmitted() is false.
console.log(result.developer_fields?.verified); // false
console.log(result.drm_rate);                   // 0.5
console.log(result.chunks_checked);             // 2
console.log(result.mismatched_count);           // 1
```

| Parameter | Type | Default | Description |
| - | - | - | - |
| `targetDocumentId` | `string` | — | Expected source document ID |
| `retrievedChunks` | `Record<string, unknown>[]` | — | Array of chunk objects with metadata |
| `options.maxDrmRate` | `string` | — | Maximum tolerable mismatch fraction (e.g. `'0'` or `'1/10'`) |

### verifyBatch(items)

Verify multiple items at once. The response is a `BatchResponse` that mirrors the server's batch job: `job_id`, `status` (the job state: `pending`, `processing`, `completed`, `partial`, or `failed`), `progress_percent`, `total_items`, `completed_items`, `failed_items`, and `items`. There is no aggregate `summary`.

Each item has an `id`, a `status` (the item state, such as `completed` or `failed`), and a `result` holding its [`DiagnosticResult`](https://docs.qwedai.tech/advanced/diagnostics). Gate each item with `isAdmitted(item.result)`. Math, code and SQL items carry their route's admission, so an item can be admitted. Natural-language items carry one too, but it is always `BLOCKED`, because nothing proves the translation. Logic and fact items make no decision, so they're never admitted.

```typescript theme={null}
const batch = await client.verifyBatch([
  { query: '2+2=4', type: VerificationType.Math },
  { query: 'x + x', type: VerificationType.Math },
]);

console.log(batch.job_id, batch.status); // job state, such as 'completed' or 'partial'
console.log(batch.completed_items, batch.failed_items, batch.total_items);
for (const item of batch.items) {
  console.log(item.query, item.result?.status, item.result?.proof_ref, isAdmitted(item.result));
}
```

> [!NOTE]
>
> A batch SQL item needs `params.schema_ddl`. The server fails an item without it with `Missing 'schema_ddl'`. The SDK's batch items send only a query and a type, so verify SQL with `verifySQL()` instead of in a batch.

For `math` items, supply equality claims (e.g., `'x + x = 2*x'`) to receive proof results. Expressions without an `=` return `status: 'UNVERIFIABLE'` with the simplified form in `developer_fields.simplified`. The legacy `is_valid` boolean is preserved inside `developer_fields.is_valid` for backward compatibility.

## Agent verification

> [!NOTE]
>
> New in v4.0.1

The TypeScript SDK supports the QWED Agent API for registering AI agents and verifying their actions with per-tenant rate limiting and server-enforced security checks.

### registerAgent(registration)

Register an agent and receive credentials for subsequent verification calls.

```typescript theme={null}
const { agent_id, agent_token } = await client.registerAgent({
  agent: {
    name: 'research-assistant',
    type: 'supervised',
    principal_id: 'user-123',
    framework: 'langchain',
  },
  permissions: {
    allowed_engines: [VerificationType.Fact, VerificationType.Code],
  },
  budget: {
    max_daily_cost_usd: 5.0,
    max_requests_per_hour: 100,
  },
});
```

### verifyAgent(agentId, agentToken, query, options)

> [!NOTE]
>
> Updated in v5.0.0

Verify an agent action. Security checks (exfiltration detection and MCP poisoning) are enforced server-side and cannot be disabled by the client.

> [!WARNING]
>
> **Breaking change (v5.0.0):** The `checkExfiltration` and `checkMcpPoison` options have been removed. Exfiltration detection always runs. MCP poison detection runs automatically when you provide a `toolSchema`.

> [!WARNING]
>
> **Breaking change (v5.0.0):** The `context` option with `conversationId` and `stepNumber` is now required. Requests without these fields are rejected with error code `QWED-AGENT-CTX-001`.

```typescript theme={null}
const response = await client.verifyAgent(
  agent_id,
  agent_token,
  'Summarize the Q3 revenue report',
  {
    provider: 'openai',
    context: {
      conversationId: 'conv_abc123',
      stepNumber: 1,
    },
    toolSchema: {
      name: 'fetch_report',
      description: 'Fetch quarterly report data',
    },
  }
);

console.log(response.decision);  // "APPROVED" | "DENIED" | "CORRECTED"
console.log(response.verification?.risk_level);  // "low"
console.log(response.budget_remaining);
```

| Parameter | Type | Default | Description |
| - | - | - | - |
| `agentId` | `number` | — | Agent ID from registration |
| `agentToken` | `string` | — | Agent token from registration |
| `query` | `string` | — | The action or query to verify |
| `options.context` | `object` | — | Action context (required in v5.0.0) |
| `options.context.conversationId` | `string` | — | Unique conversation/session identifier |
| `options.context.stepNumber` | `number` | — | Monotonically increasing step counter (>= 1) |
| `options.provider` | `string` | — | LLM provider name |
| `options.toolSchema` | `object` | — | MCP tool definition — triggers server-side `MCPPoisonGuard` inspection |

### getAgentBudget(agentId, agentToken)

Check the remaining budget for a registered agent.

```typescript theme={null}
const budget = await client.getAgentBudget(agent_id, agent_token);
console.log(budget.cost.current_daily_usd);
console.log(budget.requests.max_per_hour);
```

## IRAC audit fields

All guard-related verification responses include IRAC-compliant audit fields for compliance reporting:

```typescript theme={null}
const result = await client.verifyProcess(reasoningTrace, { mode: 'irac' });

console.log(result['irac.issue']);       // What was evaluated
console.log(result['irac.rule']);         // The rule being enforced
console.log(result['irac.application']); // How the rule was applied
console.log(result['irac.conclusion']);   // Final verdict
```

## Risk codes

Security-related responses may include a `risk` field indicating the type of threat detected:

| Risk code | Description |
| - | - |
| `DOCUMENT_RETRIEVAL_MISMATCH` | RAG chunks from wrong source document |
| `EXFILTRATION_ATTEMPT` | Data exfiltration to unauthorized endpoint |
| `MCP_POISONING` | Poisoned MCP tool definition detected |
| `TOXIC_CHAIN` | Unsafe reasoning chain detected |
| `SOVEREIGNTY_VIOLATION` | Data residency policy violation |
| `FABRICATED_REASONING` | Fabricated reasoning path detected |

## TypeScript types

```typescript theme={null}
import {
  VerificationType,
  VerificationStatus,
  Admission,
  VerificationResponse,
  BatchResponse,
  BatchItemResult,
  isAdmitted,
  parseVerificationResponse,
  parseBatchResponse,
  AgentRegistration,
  AgentVerificationResponse,
  AgentDecision,
} from '@qwed-ai/sdk';
```

### VerificationType enum

```typescript theme={null}
enum VerificationType {
  NaturalLanguage = 'natural_language',
  Math = 'math',
  Logic = 'logic',
  Stats = 'stats',
  Fact = 'fact',
  Code = 'code',
  SQL = 'sql',
  Image = 'image',
  Reasoning = 'reasoning',
  Process = 'process',
  RAG = 'rag',
  Security = 'security',
}
```

### VerificationStatus enum

`VerificationStatus` is the diagnostic status of a verification (`DiagnosticResult.status`).

```typescript theme={null}
enum VerificationStatus {
  Verified = 'VERIFIED',
  Unverifiable = 'UNVERIFIABLE',
  Blocked = 'BLOCKED',
  Unknown = 'UNKNOWN',
}
```

| Status | Meaning |
| - | - |
| `VERIFIED` | The engine reached a deterministic answer and `proof_ref` is set. The claim may still be proven false or unsafe. Check `admission`. |
| `UNVERIFIABLE` | The claim couldn't be proven |
| `BLOCKED` | Verification couldn't be attempted (parse error, policy, missing dependency) |
| `UNKNOWN` | A missing status, or one this SDK version doesn't recognize. The server's text is in `rawStatus`. Never admitted. |

`AgentVerificationResponse.verification.status` uses `AgentVerificationStatus`, which adds the math engine's `DISPROVEN`, `INCONCLUSIVE`, and `SYNTAX_ERROR`.

### Admission enum

`Admission` is the server's decision at its enforcement boundary. It's absent where the endpoint makes no decision.

```typescript theme={null}
enum Admission {
  Admit = 'ADMIT',     // The boundary may proceed with the verdict
  Blocked = 'BLOCKED', // The boundary must not admit
  Unknown = 'UNKNOWN', // A decision this SDK version doesn't recognize. Never admitted.
}
```

## Error handling

```typescript theme={null}
import { QWEDError, QWEDAuthError, QWEDRateLimitError } from '@qwed-ai/sdk';

try {
  const result = await client.verify('test');
} catch (error) {
  if (error instanceof QWEDRateLimitError) {
    console.log(error.retryAfter);  // seconds until retry
  } else if (error instanceof QWEDAuthError) {
    console.log('Invalid API key');
  } else if (error instanceof QWEDError) {
    console.log(error.code);     // "QWED-001"
    console.log(error.message);  // "Verification failed"
  }
}
```


## Related topics

- [QWED Tax integration for AI payroll and tax workflows](https://docs.qwedai.tech/tax/integration.md)
- [Changelog](https://docs.qwedai.tech/changelog.md)
- [QWED Protocol releases: SDK versions and installs](https://docs.qwedai.tech/releases.md)
- [Changelog Archive](https://docs.qwedai.tech/changelog-archive.md)
- [QWED Tax guards for AI payroll and tax verification](https://docs.qwedai.tech/tax/guards.md)


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