@qverisai/sdk
v0.9.0
Published
QVeris TypeScript SDK for connecting AI agents to professional data and tools: find services, review supported scope, call, and audit usage
Readme
@qverisai/sdk
TypeScript SDK for QVeris — the Agent External Data & Tool Harness. Discover, inspect and call 1000+ ranked external data & tool capabilities with unified billing and usage audit. No per-provider API keys required.
- Typed end to end — response types aligned with the public OpenAPI contract (categories, capabilities,
why_recommended,expected_cost, billing) - Zero dependencies — native
fetch, Node.js 18+ - Same wire semantics as the Python SDK and the MCP server
Install
npm install @qverisai/sdkQuickstart
import { Qveris } from '@qverisai/sdk';
const qveris = new Qveris({ apiKey: process.env.QVERIS_API_KEY! });
// or: const qveris = Qveris.fromEnv();
// 1. Discover — free, returns ranked capabilities
const found = await qveris.discover('stock price market data API', { limit: 5 });
for (const tool of found.results) {
console.log(tool.tool_id, '—', tool.why_recommended);
}
const parameters: Record<string, unknown> = { symbol: 'AAPL' };
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;
};
// 2. Select only a contract compatible with every supplied value
const selected = found.results.find((tool) => {
if (!tool.params) return false;
const definitions = new Map(tool.params.map((param) => [param.name, param]));
if (definitions.size !== tool.params.length) return false;
return Object.entries(parameters).every(([name, value]) => {
const param = definitions.get(name);
return Boolean(param && matchesType(param.type, value) &&
(!param.enum || param.enum.some((allowed) => Object.is(allowed, value))));
}) &&
tool.params.every((param) => !param.required || Object.prototype.hasOwnProperty.call(parameters, param.name));
});
if (!selected) throw new Error('Inspect candidates before calling: no compatible contract was returned');
// 3. Apply the current request's value instead of copying sample values
const missing = selected.params!.filter((param) => param.required && parameters[param.name] === undefined);
if (missing.length) throw new Error(`Missing inputs: ${missing.map((param) => param.name)}`);
const outcome = await qveris.call(selected.tool_id, {
searchId: found.search_id,
parameters,
});
console.log(outcome.success, outcome.result);inspect and probe are optional checks, not mandatory stages. Inspect when selection or valid request construction depends on missing/stale contract details, or candidates need comparison. Probe when parameters need validation, a current quote is needed for a budget decision, or preflight is explicitly requested. A quote is not a price reservation or user authorization.
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.
The SDK is stateless for routing: it does not persist semantic intent, schema, price, or business results. Preserve a Discover result's real search_id in the active application flow. If your host adds reuse, isolate it by account/API endpoint/authorization/session, rebuild business values from the current request, and expire schema, price, and availability metadata; never invent attribution or cache credentials/sensitive values.
Audit
// Final charge status for an execution
const usage = await qveris.usage({ execution_id: outcome.execution_id });
// Credit balance movements
const ledger = await qveris.ledger({ direction: 'consume', summary: true });
// Current balance
const credits = await qveris.credits();API reference
Construct with new Qveris({ apiKey }) or Qveris.fromEnv(overrides?) (reads
QVERIS_API_KEY and resolves the API endpoint automatically). Applications
that manage short-lived credentials can instead pass an async-capable provider:
import { Qveris, type CredentialProvider } from '@qverisai/sdk';
const credentialProvider: CredentialProvider = {
async getCredential({ resource, scopes }) {
// Resolve a bearer credential through your application's credential store.
return process.env.QVERIS_API_KEY!;
},
};
const qveris = new Qveris({ credentialProvider });The provider receives the resolved API resource, configured audience and
requested scopes. Configure either apiKey or credentialProvider, never
both. A provider does not select or change the API endpoint.
For a registered confidential Agent Runtime, exchange a user's OAuth access token for a short-lived, non-refreshable delegation token:
import {
AgentDelegationCredentialProvider,
Qveris,
type CredentialProvider,
} from '@qverisai/sdk';
const delegatedResource = 'https://api.qveris.ai/tools';
const subjectCredentialProvider: CredentialProvider = {
async getCredential() {
return loadCurrentUserAccessToken();
},
};
const credentialProvider = new AgentDelegationCredentialProvider({
tokenEndpoint: 'https://qveris.ai/api/v1/oauth/token',
clientId: process.env.QVERIS_AGENT_CLIENT_ID!,
clientSecret: process.env.QVERIS_AGENT_CLIENT_SECRET!,
subjectCredentialProvider,
resource: delegatedResource,
scopes: ['tools.inspect', 'tools.execute'],
constraints: { toolIds: ['openweathermap.weather.retrieve.v2'], maxCredits: 25 },
});
const qveris = new Qveris({
credentialProvider,
credentialAudience: delegatedResource,
credentialScopes: ['tools.execute'],
});Keep the confidential client secret on a trusted server; do not embed this provider in browser or mobile code. Delegation tokens stay in memory, are never refreshed, and fail closed when the requested audience or scopes exceed the configured ceiling.
A credential provider supplies the bearer value that authenticates requests to the QVeris API itself. It is unrelated to the data and tool providers in the service catalog: their upstream credentials are managed by the platform and never pass through the SDK.
| Method | Billed | Returns | Notes |
| --- | --- | --- | --- |
| discover(query, options?) | Free | SearchResponse | options: limit, sessionId, view, lang, timeoutMs. view: 'routing' returns compact routing cards; omitted/default is full. |
| inspect(toolIds, options?) | Free | SearchResponse | toolIds is one id or an array; options: searchId, sessionId, timeoutMs. An empty array resolves locally with no request. |
| call(toolId, options) | Credits | ExecuteResponse | Strict single-submit by default. model records model attribution. respondWith accepts full, summary, or fields:<JSONPath,...>; deprecated compatibilityMode: 'legacyOptionalFields' explicitly enables one projection replay. |
| credits() | Free | CreditsResponse | Current balance and bucket details. |
| usage(filters?) | Free | UsageEventsResponse | Request-level audit; filter by execution_id, search_id, dates, summary, limit. |
| ledger(filters?) | Free | CreditsLedgerResponse | Settled credit movements; filter by direction, dates, summary, limit. |
Read-only members: qveris.rateLimitRetryCount (see Rate limiting).
Key response fields:
SearchResponse—search_id,total,results: ToolInfo[](tool_id,name,description,params,examples.sample_parameters,stats.success_rate,stats.avg_execution_time_ms,expected_cost,why_recommended).ExecuteResponse—execution_id,success,result,billing(pre-settlement estimate; the final charge is inusage()/ledger()).
Projection options are never sent unless explicitly configured. Omitting respondWith keeps compatibility auto-delivery with a 20KB default inline limit and overflow envelope. Explicit respondWith: 'full' forces complete inline result.data, takes precedence over a finite maxResponseSize, and reports response_too_large at the platform hard limit instead of truncating. Summary delivery is independent of maxResponseSize.
Paid calls do not retry 429/503 or automatically replay a rejected optional field. The deprecated compatibilityMode: 'legacyOptionalFields' opt-in enables exactly one projection fallback when an older service returns 422 extra_forbidden; invalid projections remain errors.
All types are exported from the package root (import type { SearchResponse, ExecuteResponse, ToolInfo } from '@qverisai/sdk').
Configuration
| Option / env var | Description |
| --- | --- |
| apiKey / QVERIS_API_KEY | Required. Create one at qveris.ai |
| credentialProvider | Async-capable bearer credential source; mutually exclusive with apiKey |
| credentialAudience | Audience forwarded to the credential provider for fail-closed binding |
| credentialScopes | OAuth scopes forwarded to the credential provider |
| baseUrl / QVERIS_BASE_URL | API endpoint: constructor option > environment variable > built-in default |
| timeoutMs | Default request timeout (30s; call defaults to 120s) |
| maxRetries | Read-operation retries for rate-limited (429) / transient (503) responses (default 3; 0 disables); paid calls never inherit it |
API keys never select the endpoint. Endpoint overrides must be HTTP(S) URLs without credentials, a query string, or a fragment.
Rate limiting & retries
The client transparently retries rate-limited (429) and transient (503) responses for read/audit operations: it honors the Retry-After header when present, otherwise backs off exponentially with full jitter. Each wait is capped and retries are bounded by maxRetries; paid calls remain single-submit and HTTP redirects are not followed.
const qveris = new Qveris({ apiKey: process.env.QVERIS_API_KEY!, maxRetries: 5 });
// ... after some calls under load:
qveris.rateLimitRetryCount; // how many times it backed off (pressure, not failures)Set maxRetries: 0 to disable. Rate-limit backoff is retried pressure rather than failure — read rateLimitRetryCount to observe it instead of treating the retried 429s as errors.
Errors
All failures throw QverisApiError (an Error subclass) with status, details, and an observability object (operation, endpoint, request id) for diagnostics:
import { QverisApiError } from '@qverisai/sdk';
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
}
}Framework integrations
Expose the QVeris workflow as Vercel AI SDK tools. ai and zod are peer dependencies:
npm install @qverisai/sdk ai zodimport { generateText, stepCountIs } 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
stopWhen: stepCountIs(6),
prompt: 'Find a stock quote capability and quote AAPL.',
});Examples
Runnable scripts in examples/: the discover → call
quickstart, optional inspection, a Vercel AI SDK agent, and rate-limit/observability. Each is safe to
run without an API key (it explains how to set one), and any credit-spending
call is gated behind RUN_QVERIS_CALLS=1.
Version history note
Versions 0.1.x of this npm package were an early MCP-focused SDK, since superseded by @qverisai/mcp. The typed REST client documented here starts at 0.2.0.
Related
- QVeris CLI —
qveris discover / inspect / call / usage / ledger - QVeris MCP server — for Claude, Cursor and other MCP clients
- Python SDK
- REST API docs
License
MIT
