Skip to main content
The official TypeScript SDK for api.wavis.xyz. Strongly typed, tree-shakable, zero runtime dependencies.

Install

npm install @wavis/fhe-client
# or
pnpm add @wavis/fhe-client
# or
yarn add @wavis/fhe-client
Runtime support: Node.js 18+, Deno 1.30+, Bun 1.0+, modern browsers (when bundled), Cloudflare Workers, Vercel Edge.

Quick start

import { WavisClient } from "@wavis/fhe-client";

const client = new WavisClient({
  apiKey: process.env.WAVIS_API_KEY!,
});

const health = await client.health.check();
console.log(health.status);   // "healthy"
If apiKey is omitted, the client reads process.env.WAVIS_API_KEY (Node) or Deno.env.get("WAVIS_API_KEY") (Deno).

Trial key (no signup)

const resp = await fetch("https://api.wavis.xyz/api/v1/onboarding/temp-key", {
  method: "POST",
});
const { api_key } = await resp.json();

const client = new WavisClient({ apiKey: api_key });

Configuration

const client = new WavisClient({
  apiKey: "wvs_live_...",
  baseUrl: "https://api.wavis.xyz",  // default
  timeoutMs: 30_000,                   // default
  maxRetries: 3,                       // default — only on idempotent verbs
  fetch: customFetch,                  // optional — for testing or custom transports
});

API surface

The client groups endpoints into namespaces matching the REST surface.

client.keys.*

// Generate a CKKS key server-side
const key = await client.keys.generate({
  scheme: "rns-ckks",
  params: {
    poly_degree: 8192,
    scale_bits: 40,
    security_level: "128-bit",
    max_depth: 10,
  },
});
console.log(key.key_id);

// List
const list = await client.keys.list({ limit: 20 });
console.log(list.items, list.next_cursor);

// Get
const info = await client.keys.get(key.key_id);

// Delete
await client.keys.delete(key.key_id);

client.compute.*

const result = await client.compute.run({
  key_id: "key_...",
  operation: "add",
  ciphertext_ids: ["ct_a", "ct_b"],
});
console.log(result.compute_time_ms);

// Status / cancel
const status = await client.compute.status(result.job_id);
await client.compute.cancel(result.job_id);
Supported operations:
type ComputeOp =
  | "add" | "multiply" | "add-plain" | "multiply-plain"
  | "negate" | "rescale" | "rotate" | "matmul"
  | "poly-eval" | "bootstrap";

client.tfhe.*

For Boolean gates over TLWE ciphertexts:
// Create FHE-blind session by uploading the eval key
const session = await client.tfhe.createBlindSession({
  eval_key_b64: ekBase64,  // generated client-side
});

// Evaluate a single gate
const result = await client.tfhe.gate({
  session_id: session.session_id,
  op: "NAND",
  a: ctA_b64,
  b: ctB_b64,
});

// Batch (≥32 ops gets 30% discount)
const batch = await client.tfhe.batch({
  session_id: session.session_id,
  gates: gates.map(g => ({ op: "NAND", a: g.a, b: g.b })),
});

await client.tfhe.deleteSession(session.session_id);

client.webhooks.*

const wh = await client.webhooks.register({
  url: "https://example.com/webhook",
  events: ["compute_complete", "usage_alert"],
  secret: "your-32-char-shared-secret-here",
});

await client.webhooks.list();
await client.webhooks.update(wh.webhook_id, { events: ["limit_reached"] });
await client.webhooks.delete(wh.webhook_id);

const deliveries = await client.webhooks.deliveries(wh.webhook_id, 50);
await client.webhooks.replay(wh.webhook_id, deliveryId);
Verifying webhook signatures (Node.js):
import { createHmac } from "node:crypto";

function verifyWebhook(body: string, signatureHeader: string, secret: string) {
  const expected = createHmac("sha256", secret).update(body).digest("hex");
  return signatureHeader === `sha256=${expected}`;
}

client.billing.*

// Subscribe
await client.billing.subscribe({
  plan: "pro",
  email: "ops@example.com",
  billing_cycle: "annual",  // 17% discount
});

// Get current subscription
const sub = await client.billing.subscription();

// Set budget cap
await client.billing.setBudget({
  monthly_cap_usd: 500,
  hard_cap: true,                   // block ops when cap hit
  alert_email: "billing@example.com",
});

// List invoices
const invoices = await client.billing.invoices();

client.health.* / client.dashboard.*

const health = await client.health.check();
const usage = await client.dashboard.usage();

Type definitions

All request/response types are exported:
import type {
  KeyGenerateRequest,
  KeyGenerateResponse,
  ComputeRequest,
  ComputeResponse,
  WebhookEventType,
  HealthResponse,
  SubscribeRequest,
} from "@wavis/fhe-client";

const req: ComputeRequest = {
  key_id: "key_...",
  operation: "multiply",
  ciphertext_ids: ["ct1", "ct2"],
};
Common enums:
type FHEScheme = "ckks" | "rns-ckks";
type SecurityLevel = "testing" | "128-bit" | "192-bit" | "256-bit";
type BillingPlan = "free" | "payg" | "starter" | "pro" | "scale" | "enterprise";
type TfheGate = "NAND" | "AND" | "OR" | "XOR" | "XNOR" | "NOR" | "NOT" | "MUX";
type WebhookEventType =
  | "usage_alert" | "limit_reached"
  | "compute_complete" | "key_created" | "key_deleted" | "key_rotated"
  | "invoice_created" | "invoice_paid" | "invoice_failed"
  | "budget_alert";

Error handling

import { WavisClient, WavisApiError } from "@wavis/fhe-client";

try {
  await client.keys.generate({...});
} catch (err) {
  if (err instanceof WavisApiError) {
    console.log(err.statusCode);    // e.g. 400
    console.log(err.errorCode);     // e.g. "INSUFFICIENT_SECURITY"
    console.log(err.message);
    console.log(err.requestId);     // for support tickets
  } else {
    // Network / timeout / serialization error
    throw err;
  }
}
The client automatically retries idempotent requests (GET, DELETE) up to 3 times on 5xx errors with exponential backoff (250 ms, 500 ms, 1 s). POST and PATCH are not retried automatically — wrap them in your own retry logic if you need it.

Browser usage

<script type="module">
  import { WavisClient } from "https://esm.sh/@wavis/fhe-client";

  // Get a trial key — no API key in browser source
  const resp = await fetch("https://api.wavis.xyz/api/v1/onboarding/temp-key", {
    method: "POST",
  });
  const { api_key } = await resp.json();

  const client = new WavisClient({ apiKey: api_key });
  const health = await client.health.check();
  console.log(health);
</script>
⚠️ Never embed a wvs_live_* key in browser-shipped code. Use the trial-key flow above for demos, or proxy through your backend.

Cloudflare Workers / Edge

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const client = new WavisClient({ apiKey: env.WAVIS_API_KEY });
    const health = await client.health.check();
    return Response.json(health);
  },
};

Next Steps

Quickstart Example

Five-minute end-to-end demo

REST API

Raw HTTP — for languages without an SDK