Typed TypeScript/JavaScript SDK to discover, inspect, probe, call, and audit real-world API capabilities from your own agents and applications.
@qverisai/sdk v0.8.4 is the latest tested release. It is a thin, typed wrapper over the QVeris REST API (discover, inspect, probe, call, credits, usage, ledger). It has zero runtime dependencies — it uses the platform fetch (Node.js 18+) — and mirrors the wire semantics of the Python SDK and the MCP server.
npm install @qverisai/sdk
Requires Node.js 18+ (native fetch). The package is ESM-only.
The SDK reads your API key from the QVERIS_API_KEY environment variable:
export QVERIS_API_KEY="sk-..."
Create a key in Dashboard / API Keys. Create the client from the environment, or pass configuration explicitly:
import { Qveris } from '@qverisai/sdk';
const qveris = Qveris.fromEnv();
// or
const explicit = new Qveris({ apiKey: 'sk-...' });
Endpoint priority is explicit baseUrl > QVERIS_BASE_URL > the built-in default. API keys never select the endpoint. To target a custom endpoint, pass baseUrl explicitly or set QVERIS_BASE_URL:
const client = new Qveris({ apiKey: 'sk-...', baseUrl: 'https://qveris.ai/api/v1' });
The default workflow is discover → call, then optionally audit what happened. inspect and probe are conditional checks, not mandatory stages. All methods return promises.
For provider comparison, Inspect every candidate when current scope or a complete contract must be confirmed; a Discover summary is not confirmation. Probe every candidate when the comparison requires a current quote. Reuse may preserve an exact route, never business parameters or results: build parameters from the current request, and make a fresh Call for current, latest, today, or other time-sensitive data.
import { Qveris } from '@qverisai/sdk';
const qveris = Qveris.fromEnv();
// 1. Discover capabilities with natural language (free)
const discovered = await qveris.discover('weather forecast API', { limit: 5 });
const params: Record<string, unknown> = { city: 'London' };
const matchesType = (type: string, value: unknown) => {
if (type === 'string') return typeof value === 'string';
if (type === 'integer') return typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value);
if (type === 'number') return typeof value === 'number' && Number.isFinite(value);
if (type === 'boolean') return typeof value === 'boolean';
if (type === 'array') return Array.isArray(value);
if (type === 'object') return value !== null && typeof value === 'object' && !Array.isArray(value);
return false;
};
const supportsRequest = (candidate: (typeof discovered.results)[number]) => {
if (!candidate.params) return false;
const definitions = new Map(candidate.params.map((parameter) => [parameter.name, parameter]));
if (definitions.size !== candidate.params.length) return false;
return Object.entries(params).every(([name, value]) => {
const parameter = definitions.get(name);
return Boolean(parameter && matchesType(parameter.type, value) &&
(!parameter.enum || parameter.enum.some((allowed) => Object.is(allowed, value))));
}) &&
candidate.params.every((parameter) =>
!parameter.required || Object.prototype.hasOwnProperty.call(params, parameter.name),
);
};
let tool = discovered.results.find(supportsRequest);
// 2. Inspect only if discovery omitted the contract needed for selection
if (!tool) {
const details = await qveris.inspect(
discovered.results.slice(0, 3).map((candidate) => candidate.tool_id),
{ searchId: discovered.search_id },
);
tool = details.results.find(supportsRequest);
}
if (!tool?.params) throw new Error('No candidate exposed a current city parameter contract');
// 3. Use samples only as a template; apply this request's actual value
const missing = tool.params.filter((parameter) => parameter.required && params[parameter.name] === undefined);
if (missing.length) throw new Error(`Missing inputs: ${missing.map((parameter) => parameter.name)}`);
const result = await qveris.call(tool.tool_id, {
parameters: params,
searchId: discovered.search_id,
maxResponseSize: 20480,
});
console.log(result.success, result.result);
// 4. Audit the final charge outcome
const usage = await qveris.usage({ execution_id: result.execution_id, summary: true });
const ledger = await qveris.ledger({ summary: true, limit: 5 });
console.log(usage.total, ledger.total);
There is no connection to close — the client is stateless over fetch.
Stateless also means no hidden semantic route, schema, price, or result cache. Preserve the real search_id in the active application flow. Any host-managed reuse must isolate account/API endpoint/authorization/session, rebuild business values from the current request, and expire metadata explicitly.
An explicit empty parameter list means the tool takes no parameters; an omitted parameter list means the projection did not provide the contract. Use inspect when details needed for selection or a valid request are missing/stale, or when candidates require comparison. Use probe only when parameters need validation, a current quote is needed for a budget decision, or a preflight was explicitly requested. A quote is not a reserved price or a substitute for user authorization:
const inspected = await qveris.inspect(tool.tool_id, { searchId: discovered.search_id });
const quote = await qveris.probe(tool.tool_id, {
parameters: params,
checks: ['schema', 'quote'],
});
new Qveris(config) accepts:
| Field | Env var | Default | Description |
|---|---|---|---|
apiKey | QVERIS_API_KEY | — (required without credentialProvider) | API key, sent as Authorization: Bearer ... |
credentialProvider | — | — | Async bearer provider; mutually exclusive with apiKey |
credentialAudience | — | — | Audience forwarded to the credential provider |
credentialScopes | — | [] | OAuth scopes forwarded to the credential provider |
baseUrl | QVERIS_BASE_URL | https://qveris.ai/api/v1 | API base URL; constructor option has highest priority |
timeoutMs | — | 30000 | Default request timeout (call defaults to 120000) |
Qveris.fromEnv(overrides?) builds the client from QVERIS_API_KEY and accepts the same non-key options.
Registered confidential Agent Runtimes can use AgentDelegationCredentialProvider
to exchange a user access token at https://qveris.ai/api/v1/oauth/token.
Configure the client with the same credentialAudience and a subset of
credentialScopes. Delegation tokens stay in memory, are never refreshed, and
fail closed on audience or scope widening. Keep the confidential client secret
on a trusted server, never in browser or mobile code.
The source-generated symbol reference lists every public class, method, option, response type, and AI SDK integration exported by the current package. It is regenerated from TypeScript source and checked for drift in CI.
Qveris| Method | REST endpoint | Purpose |
|---|---|---|
discover(query, options?) | POST /search | Find capabilities; view: 'routing' returns compact routing cards (free) |
inspect(toolIds, options?) | POST /tools/by-ids | Fetch full capability metadata (free) |
probe(toolId, options?) | POST /tools/probe | Validate parameters and request a zero-cost quote |
call(toolId, options) | POST /tools/execute | Execute a capability; model records attribution and respondWith selects full, summary, or JSONPath fields |
credits() | GET /auth/credits | Current credit balance and buckets |
usage(filters?) | GET /auth/usage/history/v2 | Audit request status and charge outcome |
ledger(filters?) | GET /auth/credits/ledger | Inspect final credit balance movements |
Option shapes:
discover(query, { limit?, sessionId?, view?, lang?, timeoutMs? })inspect(toolIds, { searchId?, sessionId?, timeoutMs? }) — toolIds accepts a single string or an array; an empty array short-circuits and returns an empty response without a network request.probe(toolId, { parameters?, checks?, liveBudget?, timeoutMs? })call(toolId, { parameters, searchId?, sessionId?, model?, maxResponseSize?, respondWith?, timeoutMs?, compatibilityMode? })Projection options are opt-in. Paid calls are strict single-submit: HTTP redirects are not followed, and 429/503 and projection errors are returned without replay. The deprecated compatibilityMode: 'legacyOptionalFields' opt-in permits exactly one replay without an optional field rejected by an older service; invalid projections remain errors.
usage(...) and ledger(...) take filter objects such as start_date, end_date, summary, bucket, charge_outcome, execution_id, search_id, direction, entry_type, min_credits, max_credits, limit, page, page_size.
All methods return typed results that track the public OpenAPI contract. Unknown backend fields pass through, so newer API metadata will not break older SDK clients.
SearchResponse → results: ToolInfo[]; ToolInfo has tool_id, name, description, categories (objects or strings), capabilities, params, examples, stats, billing_rule, expected_cost, and (discover only) why_recommended.ExecuteResponse with execution_id, success, result, error_message, billing (CompactBillingStatement), cost, remaining_credits.UsageEventsResponse → items: UsageEventItem[], total, summary.CreditsLedgerResponse → items: CreditsLedgerItem[], total, summary.import type { ExecuteResponse } from '@qverisai/sdk';
function explain(result: ExecuteResponse): string {
if (!result.success) return `failed: ${result.error_message}`;
const charged = result.billing?.summary ?? 'no billing info';
return `ok (${charged}); remaining=${result.remaining_credits}`;
}
The typed client is a natural tool backend for any LLM agent framework. Tell the model to use discover then call by default and to invoke inspect only when it needs missing or refreshed detail. Because discover returns why_recommended, parameter guidance, and expected_cost when available, the model should not inspect every candidate by habit.
Expose the QVeris workflow as Vercel AI SDK tools. ai and zod are peer dependencies (import from the @qverisai/sdk/ai subpath):
npm install @qverisai/sdk ai zod
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { Qveris } from '@qverisai/sdk';
import { getQverisTools } from '@qverisai/sdk/ai';
const qveris = new Qveris({ apiKey: process.env.QVERIS_API_KEY! });
const { text } = await generateText({
model: openai('gpt-4o'),
tools: getQverisTools(qveris), // qveris_discover / qveris_inspect / qveris_call
maxSteps: 6,
prompt: 'Find a stock quote capability and quote AAPL.',
});
The adapter descriptions encode the same shortest-safe-path policy: qveris_inspect is optional, and a model may call directly from a sufficiently detailed discovery result.
The Python SDK ships adapters for LangChain/LangGraph, OpenAI Agents SDK, CrewAI, AutoGen, LlamaIndex, and Pydantic AI as well.
Every failed request throws QverisApiError — an Error subclass carrying:
| Property | Description |
|---|---|
status | HTTP status (0 network error, 408 timeout, 402 insufficient credits, …) |
details | The server-returned error body, when available |
observability | Request context (operation, endpoint, request id) for diagnostics |
cause | Lower-level transport/runtime cause, when available |
import { Qveris, QverisApiError } from '@qverisai/sdk';
const qveris = Qveris.fromEnv();
try {
await qveris.call('some.tool.v1', { parameters: {} });
} catch (err) {
if (err instanceof QverisApiError && err.status === 402) {
// insufficient credits — err.message includes the purchase link
}
}
result.success reflects the capability call only. Do not treat it as the final billing outcome — confirm charges with usage(...) / ledger(...).
>=18 (native fetch). ESM-only.Versions
0.1.xof the@qverisai/sdknpm package were an early MCP-focused SDK, since superseded by@qverisai/mcp. The typed REST client documented here starts at0.2.0.
@qverisai/sdk on npmpackages/js-sdkWas this page helpful?