TypeScript SDK
@vadyl/sdkInstall
npm install @vadyl/sdkyarn add @vadyl/sdkpnpm add @vadyl/sdkbun add @vadyl/sdkCreate the client
Configure the client once. Same object reaches every product surface — entities, workflows, agents, realtime.
import { createClient } from "@vadyl/sdk"; export const vadyl = createClient({ apiUrl: "https://api.vadyl.app/v1", token: process.env.VADYL_TOKEN!, tenant: "acme", project: "billing", // optional — pin to a feature branch branch: process.env.VADYL_BRANCH, });
List, filter, paginate
Typed filter expressions, cursor pagination, eager relation loading.
// Typed filter, sort, pagination, and relation expansion const recent = await vadyl.orders.list({ filter: { status: { in: ["paid", "fulfilled"] }, total: { gt: 100 }, createdAt: { gte: "now-30d" }, }, sort: [{ field: "createdAt", direction: "desc" }], pageSize: 50, include: ["customer", "items.product"], }); // recent.data: Order[] // recent.page: { number, size, totalCount?, next?, prev? } console.log(recent.data.length, recent.page.next);
Create, update, delete with idempotency
Every mutation supports idempotency keys. Retries are safe by default.
// Idempotent create — safe to retry const customer = await vadyl.customers.create(( { email: "ada@example.com", name: "Ada Lovelace" }, { idempotencyKey: `signup:${session.id}` }, )); // Update with optimistic concurrency const updated = await vadyl.customers.update( customer.id, { name: "Ada L." }, { ifMatch: customer.concurrencyToken }, ); // Delete (soft delete on entities with that lifecycle) await vadyl.customers.delete(customer.id);
Subscribe to entity changes
Field names only — never values. Re-read for values through CRUD with proper access enforcement.
// WebSocket subscription with the same filter AST as queries const sub = vadyl.orders.subscribe({ filter: { status: { eq: "paid" } }, kinds: ["created", "updated"], }); for await (const evt of sub.events()) { // evt: { kind, entityId, changedFields, occurredAt } // Field NAMES only — never values (defense-in-depth) const order = await vadyl.orders.read(evt.entityId); console.log("paid:", order); }
Start and signal durable workflows
Long-running product behavior that survives process restart. Same client interface; canonical durability underneath.
// Start a durable workflow const run = await vadyl.workflows.fulfillOrder.start({ orderId: "ord_abc", }); // Send a signal (e.g. from a webhook) await vadyl.workflows.fulfillOrder.signal(run.id, "shipped", { trackingNumber: "1Z...", }); // Query run state const state = await vadyl.workflows.fulfillOrder.query(run.id);
Run agents against the product model
Typed plan IR, capability-aware model routing, token accounting — all behind one method.
// Run an agent — typed plan IR, capability-aware model routing const run = await vadyl.agents.SupportAgent.run({ prompt: "Refund order #12345 due to defect", userId: session.userId, budget: { maxTokens: 50_000, maxToolCalls: 30 }, }); // Stream the run for await (const step of run.stream()) { console.log(step.kind, step.summary); } // Recall memory for an agent const facts = await vadyl.agents.SupportAgent.memory.recall({ kind: "preference", subject: session.userId, });
Typed error contract
Stable error codes, stable reason codes, correlation IDs, and explainability links.
import { VadylError, VadylAccessDeniedError, VadylValidationError, VadylConflictError, VadylQuotaExceededError, } from "@vadyl/sdk"; try { await vadyl.orders.update(id, { status: "refunded" }); } catch (err) { if (err instanceof VadylAccessDeniedError) { console.warn(err.reasonCode, err.correlationId); } else if (err instanceof VadylConflictError) { // optimistic-concurrency mismatch — re-read and retry } else if (err instanceof VadylError) { console.error(err.code, err.message); } else { throw err; } }
Define handlers, workflows, agents
The same package you use as a client also powers your authored runtime — defineCoreHandler, defineWorkflow, defineAgent.
import { handler } from "@vadyl/sdk"; export default handler.core(async (ctx, input: { orderId: string }) => { const order = await ctx.entities.Order.read(input.orderId); const charge = await ctx.connections.stripe.createCharge({ amount: order.total, currency: order.currency, idempotencyKey: `charge:${order.id}`, }); await ctx.entities.Order.update(order.id, { status: "paid", chargeId: charge.id, }); await ctx.events.emit("order.paid", { orderId: order.id }); return { ok: true, chargeId: charge.id }; });
What you get
Fully typed entities
Every entity, field, relation, enum, and operation is strictly typed. Zero runtime type assertions.
Manifest verification
The SDK refuses to talk to a server whose generated-format version it does not support. Fail closed.
Realtime built-in
WebSocket and SSE subscriptions with the same predicate AST as queries. Reconnect-safe.
Authoring runtime
The same package powers your handlers and workflows. defineCoreHandler, defineWorkflow, defineAgent.
Universal runtime
Run anywhere from Node and Bun to edge runtimes. ESM and CJS. No native deps.
Branch-aware
Work against feature branches by setting branch in your client config. Sandbox testing without leaving prod.
Observability hooks
Hook into the canonical envelope. Emit operational trails from the client when needed.
Agent primitives
vadyl.agents exposes the Agent plane: definitions, runs, memory, MCP.
Connections
Typed governed-connection clients per registered governed connection.
Deep-dive documentation
Build with the TypeScript SDK in minutes.
Install the package and work with your product model through typed entities, workflows, agents, and events.