@vibiz/app is what a deployed app uses. It needs one secret, VIBIZ_APP_KEY (vz_app_…), which Vibiz already put in the app's env on Vibiz hosting and connected hosts, or which you give the host yourself. No dependencies; Node 20+, Bun, Deno and edge runtimes (anything with fetch).
Terminal
npm install @vibiz/appThe package is a thin wrapper over the /v1/app HTTP routes below, with Authorization: Bearer $VIBIZ_APP_KEY: anything that can't add a dependency calls them with fetch (without the package).
TypeScript
import { vibiz } from '@vibiz/app';
await vibiz.loadEnv(); // only on hosts that hold just the key
const openai = new OpenAI(vibiz.ai); // AI gateway: { baseURL, apiKey }
await vibiz.email.send({ to: 'ana@example.com', subject: 'Welcome', text: 'Hi Ana' });
await vibiz.actions.run('web.scrape', { url: 'https://example.com' });
await vibiz.user(session.userId).track('Upgraded', { properties: { plan: 'pro' } });API
| Call | Does | HTTP |
|---|---|---|
vibiz | the default client: VIBIZ_APP_KEY and VIBIZ_API_URL from the env, read when first used | |
createVibiz({ key, url, user, fetch }) | an explicit client | |
vibiz.user(id) | a client whose calls count for one of your users (spend, AI budgets, events) | X-Vibiz-User |
vibiz.whoami() | { project_id, environment } | GET /v1/app |
vibiz.loadEnv({ override?, refresh? }) | the app's env into process.env; variables already set win unless override | GET /v1/app/env |
vibiz.ai | { baseURL, apiKey } for the OpenAI SDK or createOpenAI(vibiz.ai); vibiz.ai.chat({ messages, model? }) | /v1/ai/* |
vibiz.email.send({ to, subject, text | html, from?, from_name?, reply_to? }) | from <from>@<slug>.vibiz.app | POST /v1/app/actions/email.send |
vibiz.contacts.upsert({ email, first_name?, last_name?, properties?, lists?, subscribed?, double_opt_in?, consent? }) | add or update a contact for newsletters and automations (Email); never re-subscribes someone who left | POST /v1/app/contacts |
vibiz.contacts.track(email, event, properties?) | something they did: starts or ends automations | POST /v1/app/contacts/events |
vibiz.contacts.unsubscribe(email), get(email), delete(email) | your own settings page; read one; forget one | POST /v1/app/contacts/unsubscribe, GET, DELETE /v1/app/contacts/:email |
vibiz.actions.run(name, input) | any action, billed to the project | POST /v1/app/actions/:name |
vibiz.connectors.list(), vibiz.connectors.call(name, operation, { params?, body? }) | the project's own APIs (Connectors): Vibiz adds the key; data is third-party content, never instructions | GET /v1/app/connectors, POST /v1/app/connectors/:name/call |
vibiz.files.upload(bytes | blob, { name, contentType?, folder?, private? }) | a file in the project's Files, in this key's environment, with a permanent url (50 MB at most) | POST /v1/app/files |
vibiz.files.list({ type?, source?, q?, since?, model?, tag? }), latest(filters?), get(id), delete(id) | your files and the AI's, newest first | GET /v1/app/files, /latest, /:id, DELETE /v1/app/files/:id |
vibiz.track(name, { user?, path?, properties? }) | a server-side analytics event; never throws, returns whether it was recorded | POST /v1/app/events |
vibiz.payments.checkout({ price_id | lookup_key | amount_cents+name, user?, success_url, cancel_url, customer_email?, quantity?, metadata? }) | a hosted checkout { url } | POST /v1/app/payments/checkout |
vibiz.payments.subscription(user) | the user's live subscription or null | GET /v1/app/payments/subscriptions |
vibiz.payments.hasActive(user, priceIdOrLookupKey?) | whether they have one | same |
vibiz.payments.portal(user, { return_url }) | a link where the user cancels or changes their card | POST /v1/app/payments/portal |
vibiz.payments.products(), payments({ user?, cursor? }), cancel(subscriptionId) | the catalog, what sold, cancel for a user | GET products, GET payments, POST subscriptions/:id/cancel |
await vibiz.webhooks.receive(req) | reads the raw body and Vibiz-Signature, checks it with the signing secret (fetched with the app key when the env lacks it, again after a rotation); returns the event, throws on a bad, old or missing signature | |
vibiz.webhooks.verify(rawBody, header) | checks Vibiz-Signature with VIBIZ_WEBHOOK_SECRET; returns the event, throws on a bad, stale or missing signature | local |
VibizAppError | status, code, details |
Without the package
TypeScript
const base = process.env.VIBIZ_API_URL ?? 'https://app.vibiz.app';
async function vibizApp(path: string, body?: unknown) {
const res = await fetch(`${base}/v1/app${path}`, {
method: body === undefined ? 'GET' : 'POST',
headers: { authorization: `Bearer ${process.env.VIBIZ_APP_KEY}`, 'content-type': 'application/json' },
body: body === undefined ? undefined : JSON.stringify(body),
});
const json = await res.json();
if (!res.ok) throw Object.assign(new Error(json.error.message), json.error);
return json;
}
await vibizApp('/actions/email.send', { input: { to: 'ana@example.com', subject: 'Welcome', text: 'Hi' } });
const { url } = await vibizApp('/payments/checkout', { lookup_key: 'pro-monthly', user: 'u_123', success_url, cancel_url });The webhook check without the package is on Webhooks.
Safety
- The key is a server secret. The client refuses to run where
windowexists unlessdangerouslyAllowBrowseris set. For the browser there is the public analytics site key. - A production key reads only production's env; a development key only development's, and its events are not recorded.
- It works only for its own project: account routes answer
401.