Appearance
TypeScript SDK
bash
npm install ovrinRequires Node 18+ (global fetch). Zero runtime dependencies.
Client
ts
import { Ovrin } from "ovrin";
const client = new Ovrin(); // api key from OVRIN_API_KEY
const client = new Ovrin({ apiKey: "ovrin_..." }); // or explicit
const client = new Ovrin({ baseUrl: "https://api.ovrin.app" });Errors are typed classes carrying the API's stable machine-readable code alongside the message:
ts
import { AuthenticationError, NotFoundError, OvrinError, QuotaError } from "ovrin";| Status | Class |
|---|---|
| 401 | AuthenticationError |
| 402 | QuotaError |
| 404 | NotFoundError |
| other | OvrinError |
Sandboxes
ts
const sandbox = await client.sandboxes.create({
template: "codex",
timeout: 3600, // seconds, minimum 60
env: { OPENAI_API_KEY: key },
secrets: [{ name: "github-prod", hosts: ["api.github.com"] }],
payments: true, // inject the agent-payments key
idempotencyKey: "run-42", // safe retries
});
const result = await sandbox.run('codex "find and fix the failing test"');
console.log(result.stdout);
for await (const event of sandbox.stream("npm test")) {
if (event.type === "stdout") process.stdout.write(event.text);
else if (event.type === "stderr") process.stderr.write(event.text);
}
await sandbox.pause();
await sandbox.resume();
await sandbox.renew(new Date(Date.now() + 24 * 3600 * 1000));
await sandbox.kill();
await client.sandboxes.list();
await client.sandboxes.get(sandbox.id);Files
Binary-safe by construction — text travels as UTF-8, bytes as base64:
ts
await sandbox.files.write("/workspace/run.js", source);
await sandbox.files.writeBytes("/workspace/blob.bin", pngBytes); // Uint8Array
const text = await sandbox.files.read("/workspace/run.js");
const bytes = await sandbox.files.readBytes("/workspace/logo.png"); // Uint8Array
await sandbox.files.list("/workspace", 1);
await sandbox.files.stat("/workspace/run.js");
await sandbox.files.search("/workspace", "TODO");
await sandbox.files.move("/workspace/a.js", "/workspace/b.js");
await sandbox.files.mkdir("/workspace/pkg");
await sandbox.files.delete("/workspace/b.js");Stateful code
On a code-interpreter sandbox, variables persist across calls within a context:
ts
let res = await sandbox.runCode("x = 41");
res = await sandbox.runCode("console.log(x + 1)"); // same context
res.context_id; // reuse explicitly
const ctx = await client.sandboxes.createContext(sandbox.id);Endpoints
Public HTTP URL for a port inside the sandbox:
ts
const { url } = await sandbox.endpoint(8080);HTTP today; WebSockets need the ingress gateway (roadmap).
Snapshots
ts
await client.sandboxes.snapshot(sandbox.id, "golden");
const restored = await client.sandboxes.restore(snapshotId, "python");
await client.sandboxes.snapshots();
await client.sandboxes.deleteSnapshot(snapshotId);Memory
See Persistent Memory for scoping semantics.
ts
await client.memory.add("prefers vitest over jest");
const hits = await client.memory.search("test framework", 10);
const all = await client.memory.list({ agentId: "support" });
await client.memory.delete(memoryId);Secrets
ts
await client.secrets.create("github-prod", "ghp_...");
await client.secrets.list(); // names only — values are never returned
await client.secrets.delete("github-prod");Keys
Scopes and expiry keep blast radius small; rotation swaps atomically. See API Keys & Scopes.
ts
const key = await client.keys.create({
name: "ci",
scope: "sandboxes", // all | sandboxes | memory | secrets
expiresAt: "2026-12-01T00:00:00Z",
});
const fresh = await client.keys.rotate(key.id ?? 1);
await client.keys.revoke(keyId);Usage & templates
ts
const usage = await client.usage.get();
const templates = await client.templates.list();Payments
BYO Stripe restricted key; see Agent Payments.
ts
await client.payments.setCredentials("rk_live_...");
await client.payments.credentialsStatus();
await client.payments.clearCredentials();
await client.payments.agentKeyStatus();
await client.payments.createAgentKey(); // the persistent payments-scoped key
const customer = await client.payments.createCustomer("Acme", "billing@acme.com");
await client.payments.createInvoice(customer.id as string);
await client.payments.createPaymentLink("price_1", 1);