@assistiv/sdk
v0.2.0
Published
Official JavaScript SDK for the Assistiv AI Gateway. Inference, end-user management, billing, MCP, webhooks — one client.
Maintainers
Readme
@assistiv/sdk
Official TypeScript / JavaScript SDK for the Assistiv AI Gateway. One client, OpenAI-compatible, with per-user wallets, budgets, rate limits, tool calls, and webhooks built in.
npm install @assistiv/sdkimport { Assistiv } from "@assistiv/sdk";
const platform = new Assistiv({ apiKey: process.env.ASSISTIV_PLATFORM_KEY! });
// Provision one of your end-users (idempotent).
const { endUserId, apiKey } = await platform.bootstrap({
platformId: process.env.ASSISTIV_PLATFORM_ID!,
externalId: "user_42",
defaultBudget: { max_usd: 5, period: "monthly" },
});
// Use the returned sk-eu_* key to run inference for that user.
const assistiv = new Assistiv({ apiKey });
const reply = await assistiv.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Say hi" }],
});
console.log(reply.choices[0].message.content);What this gives you in three sentences: every popular LLM (OpenAI, Anthropic, Google, xAI) behind one endpoint that speaks the OpenAI schema; per-end-user spend caps, rate limits, and audit logs without building any of that yourself; and an MCP-compatible tool layer so agents can hit GitHub / Slack / Zoho / Zendesk through the same key.
For AI agents reading this file — this README is the canonical integration doc. Hit
https://www.assistiv.ai/llms-sdk.txtfor the machine-friendly feed if you want every per-feature snippet in one blob. The patterns below are sufficient for adding Assistiv to any product.
Auth model
Assistiv has two key types. Use the right one for the call:
| Key prefix | Lives where | What it can do |
|----------------|-----------------------|-------------------------------------------------------------------------------------------------|
| sk-plat_* | Your server only | Provision end-users, mint per-user keys, manage budgets, read logs, register MCP configs. |
| sk-eu_* | Server OR browser | Run inference (chat, responses, models), call tools, read own budget / rate-limit / logs. |
// Server (Node 18+ / Bun / Deno / serverless)
import { Assistiv } from "@assistiv/sdk";
const platform = new Assistiv({ apiKey: process.env.ASSISTIV_PLATFORM_KEY! });
// Browser / mobile / Electron
import { Assistiv } from "@assistiv/sdk/browser";
const assistiv = new Assistiv({ apiKey: endUserKey }); // sk-eu_* onlyThe browser entry refuses any sk-plat_* key at construction time.
Never ship a platform key to a client.
Provisioning end-users — bootstrap()
Most platforms do one operation when a new user signs up: create the
end-user record, mint their key, seed a budget, optionally set a rate
limit. bootstrap() is the one-call version of that flow.
const { endUserId, apiKey, budget, rateLimit } = await platform.bootstrap({
platformId: process.env.ASSISTIV_PLATFORM_ID!,
externalId: "user_42", // your stable user id
displayName: "Jane Smith", // optional
metadata: { plan: "pro" }, // optional, opaque JSON
defaultBudget: { max_usd: 5, period: "monthly" }, // optional
defaultRateLimit: { rpm_limit: 60, tpm_limit: 50_000 }, // optional
});
// Persist `apiKey` immediately — the raw key is shown ONCE.
// Recommended: store alongside your user row, encrypted at rest.
await db.users.update(user.id, { assistiv_key: apiKey, assistiv_end_user_id: endUserId });Idempotent on externalId — repeat calls with the same id return the
existing user and mint a fresh key. This is for retry safety, not
key retrieval. Store the key once and reuse it.
Inference
OpenAI-compatible. Anything openai.chat.completions.create accepts
works here, with the same response shape.
Non-streaming
const reply = await assistiv.chat.completions.create({
model: "gpt-4o-mini", // or claude-sonnet-4-6, gemini-2.0-flash, grok-2, …
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Plan a 3-day trip to Tokyo." },
],
temperature: 0.7,
max_tokens: 800,
});Streaming
const stream = await assistiv.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Count to 10, one number per line." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}Tool calling
const reply = await assistiv.chat.completions.create({
model: "gpt-4o-mini",
messages,
tools: [{
type: "function",
function: {
name: "get_weather",
description: "Get the current weather in a city.",
parameters: { type: "object", properties: { city: { type: "string" } } },
},
}],
});Responses API (stateful alt)
const res = await assistiv.responses.create({
model: "gpt-4o-mini",
input: "Plan a trip",
});Listing available models
const { data: models } = await assistiv.models.list();
// Only models your platform has configured providers for.Engine-native escape hatch
Anything OpenAI's npm client supports, you can do too:
const openai = Assistiv.openai(assistiv);
const parsed = await openai.beta.chat.completions.parse({ /* ... */ });Per-end-user state
From the platform side (sk-plat_*)
// Budgets — per-user spend caps with idempotent topups
await platform.budgets(platformId).create(endUserId, {
max_usd: 10,
period: "monthly",
auto_replenish: true,
replenish_amount: 10,
low_balance_threshold: 1,
});
await platform.budgets(platformId).topup(
endUserId,
{ amount_usd: 5, reason: "promo_grant" },
{ idempotencyKey: "promo-2026-q2" }, // safe to retry the same call
);
// Per-user rate-limit overrides (higher than platform default)
await platform.rateLimits(platformId).setForEndUser(endUserId, {
rpm_limit: 600,
tpm_limit: 250_000,
});
// Logs — what did this user do
const logs = await platform.logs(platformId).search({
end_user_id: endUserId,
since: new Date(Date.now() - 86_400_000).toISOString(),
});From the end-user side (sk-eu_* — used by your app frontend)
const me = await assistiv.me.budget(); // { max_usd, used_usd, remaining_usd, period }
const limits = await assistiv.me.rateLimits(); // current effective limits
const ledger = await assistiv.me.budgetTransactions({ limit: 100 });
const myLogs = await assistiv.me.logs();MCP tools (GitHub, Slack, Zoho, Zendesk, …)
Two-step model: you activate an app for your platform in the dashboard (OAuth secret never leaves the Assistiv UI), then your end-users connect their own account via OAuth from your product.
// Read which apps your platform has activated — to render
// "Connect GitHub" / "Connect Slack" UI in your product.
const { data: apps } = await platform.mcp(platformId).listApps();
// For your end-users at runtime: the agent connects directly to
// the MCP protocol endpoint with their sk-eu_* key.
// POST https://mcp.assistiv.ai/mcp
// Authorization: Bearer sk-eu_...
// { "jsonrpc": "2.0", "id": 1, "method": "tools/list" }Activating, editing, and rotating OAuth credentials for an MCP app is a dashboard task — there is no SDK or REST API for it (write-once secrets should not round-trip through your code).
Webhooks
Assistiv pushes real-time events when budgets and wallets change. Six event types you can subscribe to:
budget.topped_up— credit added to an end-user's budgetbudget.debited— any debit (inference call, manual debit, etc.)budget.low_balance— crossed your low-balance thresholdbudget.suspended— explicit suspensionbudget.unsuspended— reactivatedwallet.low_balance— platform-wallet warning
Register endpoint URLs in Dashboard → Outbound Webhooks and copy the signing secret into your server. Verify every payload:
import express from "express";
import { verifyWebhook } from "@assistiv/sdk";
const app = express();
app.post(
"/assistiv-events",
express.raw({ type: "application/json" }),
(req, res) => {
const ok = verifyWebhook(
req.body,
req.headers,
process.env.ASSISTIV_WEBHOOK_SECRET!,
);
if (!ok) return res.status(400).end();
const event = JSON.parse(req.body.toString());
switch (event.event_type) {
case "budget.low_balance":
// notify the user, top up, or pause
break;
case "wallet.low_balance":
// top up the platform wallet
break;
}
res.status(200).end();
},
);Failed deliveries retry automatically. Use the Svix portal in the dashboard to inspect or replay any event.
Errors
The SDK throws a small typed hierarchy. Catch specifically — never just
catch (e) without inspecting.
import {
AssistivAuthError, // 401 — bad / missing key
AssistivPaymentRequiredError, // 402 — wallet OR budget exhausted
AssistivForbiddenError, // 403 — key-type or scope violation
AssistivNotFoundError, // 404 — resource missing
AssistivConflictError, // 409 — idempotency clash
AssistivRateLimitError, // 429 — has `.retryAfter` seconds
AssistivServerError, // 5xx — Assistiv-side
AssistivError, // base class for everything above
} from "@assistiv/sdk";402 branching — what's actually wrong
On a 402 from chat.completions.create, branch on error.code:
try {
await assistiv.chat.completions.create({ /* … */ });
} catch (e) {
if (e instanceof AssistivPaymentRequiredError) {
switch (e.code) {
case "wallet_insufficient":
// The platform wallet is empty. Surface to platform admin;
// top up the wallet in the Assistiv dashboard.
break;
case "budget_exhausted":
// This end-user's per-user budget is empty.
// Top up with platform.budgets(pid).topup(...).
break;
case "budget_suspended":
// is_suspended=true on this user. Unsuspend manually.
break;
}
}
}Rate-limit handling
try {
await assistiv.chat.completions.create({ /* … */ });
} catch (e) {
if (e instanceof AssistivRateLimitError) {
await new Promise((r) => setTimeout(r, (e.retryAfter ?? 1) * 1000));
// retry…
}
}Every error carries .status, .code, .message, and .requestId so
you can report incidents back to Assistiv with one click.
What this SDK does NOT do
Everything below is dashboard-only (Supabase auth, not an API key) and is intentionally absent from the SDK. The dashboard URL points to the right form:
| Operation | Where |
|----------------------------------------|--------------------------------------------------|
| Top up the platform wallet (Stripe) | assistiv.ai/dashboard/settings |
| Add OpenAI / Anthropic / Google keys | assistiv.ai/dashboard/llm-configs |
| Register outbound webhook endpoints | assistiv.ai/dashboard/outbound-webhooks |
| Invite teammates | assistiv.ai/dashboard/team |
| Activate an MCP app (OAuth secrets) | assistiv.ai/dashboard/mcp |
| Upload a private skill | assistiv.ai/dashboard/skills |
| Provision a new platform | Email [email protected] (not self-serve) |
These are write-once secret operations or one-time setup steps. Putting them in the SDK would mean those secrets round-tripping through your code, which adds an unnecessary handling surface.
Versions
- SDK semver: strict semver from
1.0.0onward. Pre-1.0 minor bumps may include breaking changes; check the CHANGELOG. - API version:
v0.1.0(the URL prefix/v1is stable; the underlying schema version ships inX-Assistiv-Api-Version). - Provenance: each npm release ships a Sigstore SLSA v1 attestation
linked to its source commit.
npm install --foreground-scripts @assistiv/sdkshows the verification badge.
References
- Docs site: https://www.assistiv.ai/docs
- Per-feature feed for AI agents:
- https://www.assistiv.ai/llms-sdk.txt — SDK-flavoured (this surface)
- https://www.assistiv.ai/llms-api.txt — raw HTTP / curl flavoured
- https://www.assistiv.ai/llms-full.txt — everything
- API status: https://status.assistiv.ai
- Support: [email protected]
License
MIT — see LICENSE.
