npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@usevelo/client

v0.1.1

Published

Official TypeScript SDK for Velo live video: server-side REST client, browser-safe room joining, and data usage reporting for metered mobile networks

Readme

@usevelo/client

npm license

Add live audio and video to a web application. This is the official TypeScript SDK for Velo: a server-side client for the REST API, a browser-safe helper for joining a call, and data usage reporting for users on metered mobile networks.

It is a thin wrapper rather than a fork. Media handling stays in livekit-client, which is a peer dependency, so upgrading the media stack is a version bump on your side.

Never put a Velo API key in a browser. A vk_... key controls your whole project. Browsers receive only a short-lived room token. See Security model.

Requirements

  • Node.js 18 or newer for server-side use
  • A browser with WebRTC support for calls
  • livekit-client 2.15 or newer as a peer dependency

Install

npm install @usevelo/client livekit-client

Get an API key

  1. Sign in at usevelo.xyz.
  2. Create a project.
  3. Create an API key for that project and store it as a server-side secret.

The API base URL is https://api.usevelo.xyz.

Quickstart

Two pieces of code. Your backend mints a token; the browser joins with it.

On your server

import { VeloClient } from "@usevelo/client";

const velo = new VeloClient({
  baseUrl: "https://api.usevelo.xyz",
  apiKey: process.env.VELO_API_KEY,
});

await velo.createRoom("consultation-42", { maxParticipants: 2 });

const token = await velo.createToken({
  room: "consultation-42",
  identity: "patient-1187",
  ttlSeconds: 3600,
});

Return token.token and token.url to the browser, and nothing else.

In the browser

import { connectToRoom, RoomEvent } from "@usevelo/client";

const { token, url } = await fetch("/api/velo-token").then((r) => r.json());
const room = await connectToRoom({ token, url });

room.on(RoomEvent.TrackSubscribed, (track) => {
  document.body.appendChild(track.attach());
});

connectToRoom re-exports the livekit-client types you need, so Room, RoomEvent, Track and the participant classes all come from this package.

Security model

A project API key grants full control: creating rooms, minting tokens for any identity, removing participants, starting recordings, reading usage. Treat it like a database password.

| | Where it runs | What it holds | | --- | --- | --- | | VeloClient | Your server only | The project API key | | VeloAdminClient | Your server only | An admin token | | connectToRoom | Browser | A room token, scoped to one identity and one room | | exchangeRoomCode | Your server | Nothing, it is unauthenticated |

The correct shape is always: browser asks your backend for a token, backend calls createToken, browser calls connectToRoom.

Joining without a token backend

A room code is a short, shareable string bound to one room and one role, so you can put someone into a call without running a token endpoint.

const code = await velo.createRoomCode("consultation-42", {
  role: "patient",
  ttlSeconds: 3600,
  maxUses: 1,
});

Redeem it from your server, not the browser. exchangeRoomCode is a standalone function rather than a VeloClient method precisely because it needs no credentials, and the API's CORS allowlist admits only the Velo console origin, so a browser call is rejected before it reaches the handler.

import { exchangeRoomCode } from "@usevelo/client";

const token = await exchangeRoomCode({
  baseUrl: "https://api.usevelo.xyz",
  code: "abcdefghjkmnpqrs",
  identity: "patient-1187",
});

Codes always expire: 24 hours by default, 30 days at most. Disabled, expired, exhausted and unknown codes all fail identically, so a caller learns nothing about the code space.

Data usage on metered networks

Mobile data is a real cost for end users. DataUsageMonitor reports what a call is actually consuming so your application can show it or react to it.

import { DataUsageMonitor, suggestAudioOnly, setAudioOnly } from "@usevelo/client";

const monitor = new DataUsageMonitor(room, { intervalMs: 5000, pricePerMbUgx: 120 });

monitor.on("update", (snapshot) => {
  showBanner(`${(snapshot.totalBytes / 1_000_000).toFixed(1)} MB`);
  if (suggestAudioOnly(snapshot)) setAudioOnly(room, true);
});

Each snapshot carries bytesSent, bytesReceived, totalBytes, sendBitrateBps, recvBitrateBps, estimatedCostUgx and durationMs.

Errors

Failures arrive as typed errors rather than raw responses.

import { VeloApiError, VeloPermissionError, VeloQuotaError } from "@usevelo/client";

try {
  await velo.removeParticipant("consultation-42", "patient-1187");
} catch (error) {
  if (error instanceof VeloQuotaError) {
    showUpgradePrompt();
  } else if (error instanceof VeloPermissionError) {
    disableButton(error.permission);
  } else if (error instanceof VeloApiError) {
    report(error.status, error.code, error.message);
  }
}

VeloQuotaError is separate because hitting a plan limit is an expected, actionable state rather than a bug. VeloPermissionError carries the specific permission that was missing, such as remove_others, so a UI can disable the control that produced it instead of parsing a message. VeloConnectionError covers transport failures.

What the client covers

VeloClient wraps the full control plane: rooms, tokens, participants and data messages, recordings, streaming, templates and roles, room codes, destinations, runtime role changes, sessions and usage, webhook endpoints and deliveries. VeloAdminClient adds project and plan administration.

Every method maps to one documented endpoint. The full reference, with request and response shapes for each, lives at usevelo.xyz/docs.

TypeScript and module formats

Types ship with the package. Both ESM and CommonJS builds are published, so import and require both work without a bundler shim.

Contributing

The SDK lives in sdks/web of the Velo repository.

npm install
npm run typecheck
npm test
npm run build

Releases are described in RELEASING.md.

License

MIT. See LICENSE.