SDK Overview
The Vonage Cloud Runtime SDK is the primary interface to all platform providers. It is available for Node.js and Python.
| Runtime | Package | Minimum version | Latest version | Notes |
|---|---|---|---|---|
| Node.js | @vonage/vcr-sdk | 2.0.0 | 2.1.5 | SDK >=2.1.0 requires Node.js 22 (Active LTS) |
| Python | vonage_cloud_runtime | 2.0.0 | 2.1.5 |
The vcr Singleton
The SDK exports a vcr singleton that is the entry point for all operations.
Node.js:
import { vcr } from '@vonage/vcr-sdk';
Python:
The vcr object provides the following methods:
| Method | Description |
|---|---|
vcr.createSession(ttl?) | Create a new session with optional TTL in seconds (default: 7 days) |
vcr.createSessionWithId(id) | Create a session with a specific ID |
vcr.getSessionById(id) | Retrieve an existing session by ID |
vcr.getSessionFromRequest(req) | Extract session from an incoming HTTP request |
vcr.getGlobalSession() | Get the global session (shared across all requests) |
vcr.getInstanceState() | Get instance-level state (shared across sessions within the instance) |
vcr.getAccountState() | Get account-level state (shared across all applications in the account) |
vcr.getAppUrl() | Get the public URL of the running application |
vcr.createVonageToken(params) | Create a signed JWT for Vonage API authentication |
vcr.verifyAuth(token) | Verify an authentication token |
vcr.unsubscribe(id, provider?) | Unsubscribe from a provider subscription |
Sessions
Sessions are required to initialize any provider. Every provider constructor takes a session as its first argument.
Node.js:
import { vcr } from '@vonage/vcr-sdk';
// New session with default 7-day TTL
const session = vcr.createSession();
// New session with a 1-hour TTL
const shortSession = vcr.createSession(3600);
// Session with a specific ID (useful for correlating with a user or call)
const userSession = vcr.createSessionWithId('user-123');
// Extract session from an incoming request
const reqSession = vcr.getSessionFromRequest(req);
// Global session — shared singleton across the entire instance
const globalSession = vcr.getGlobalSession();
Python:
Session properties and methods:
| Description | |
|---|---|
session.id | The session's unique identifier |
session.getToken() | Get the session's authentication token |
session.createUUID() | Generate a new UUID |
session.log(level, message, context?) | Log a message with structured context |
Session Scoping — Critical Guidance
Warning: vcr.createSession() generates a random, ephemeral session ID every time it is called. Data stored under that session (via new State(session)) is only accessible if you have that exact session ID. If you call vcr.createSession() at global/module scope and use it with State, each replica will create a different session, and data will be siloed and unreachable by other replicas or requests. This breaks stateless architecture.
Rules:
- Never call
vcr.createSession()at global app scope to initialize State or other providers that need shared data access. - For shared state across replicas, use
vcr.getInstanceState()(orvcr.getAccountState()for cross-app data). - For per-request or per-conversation state, use
vcr.createSessionWithId(id)with a deterministic ID from the request context — such asconversation_uuidfrom a voice callback, a sender phone number from an SMS callback, or a user ID. This ensures the same session is retrievable across replicas and requests. vcr.getGlobalSession()is intended for registering instance-wide subscriptions (e.g.,voice.onCall(),messages.onMessage()). It should not be used as a general-purpose session for State.
// WRONG — random session at global scope, state is unreachable by other replicas/requests
const session = vcr.createSession();
const state = new State(session); // Data siloed to this random session
// CORRECT — shared state across all replicas
const state = vcr.getInstanceState();
// CORRECT — per-conversation state using a deterministic ID from a callback
app.post('/onCall', async (req, res) => {
const session = vcr.createSessionWithId(req.body.conversation_uuid);
const state = new State(session); // Same conversation_uuid = same state, any replica
await state.set('step', 'greeting');
});
See Stateless Architecture for detailed guidance.
Provider Initialization
All providers follow the same pattern: pass a session to the constructor.
Node.js:
import { vcr, State, Queue, Scheduler, Messages, Voice, Assets } from '@vonage/vcr-sdk';
const session = vcr.createSession();
// For shared state across replicas, use getInstanceState() — not new State(session)
const state = vcr.getInstanceState();
const queue = new Queue(session);
const scheduler = new Scheduler(session);
const messages = new Messages(session);
const voice = new Voice(session);
const assets = new Assets(session);
Python:
State Scoping
VCR provides three levels of state:
| Scope | How to access | Visibility |
|---|---|---|
| Session state | new State(session) | Isolated to the specific session |
| Instance state | vcr.getInstanceState() | Shared across all sessions within the same instance |
| Account state | vcr.getAccountState() | Shared across all applications in the Vonage account |
// Per-user state
const session = vcr.createSessionWithId(userId);
const userState = new State(session);
// Shared across all replicas of this instance
const instanceState = vcr.getInstanceState();
// Shared across all applications in the account
const accountState = vcr.getAccountState();
See Stateless Architecture for guidance on when to use each scope.
Authentication
Verifying Incoming Requests
Use vcr.verifyAuth(token) to validate tokens on incoming requests:
app.use((req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
try {
const decoded = vcr.verifyAuth(token);
req.user = decoded;
next();
} catch (err) {
res.status(401).json({ error: 'Invalid token' });
}
});
Creating Vonage Tokens
Use vcr.createVonageToken(params) to generate signed JWTs for Vonage API authentication:
const token = vcr.createVonageToken({
exp: Math.floor(Date.now() / 1000) + 3600, // 1-hour expiry
aclPaths: { '/*/users/**': {} }, // optional ACL paths
subject: 'user-123', // optional subject
});
| Parameter | Type | Required | Description |
|---|---|---|---|
exp | number | Yes | Token expiry as Unix timestamp (seconds) |
aclPaths | Record<string, any> | No | ACL path restrictions |
subject | string | No | Token subject (e.g. user identifier) |
Utility Methods
vcr.getAppUrl()
Returns the public URL of the running VCR instance. Equivalent to reading the VCR_INSTANCE_PUBLIC_URL environment variable.
const appUrl = vcr.getAppUrl();
// e.g. "https://my-app-dev.use1.runtime.vonage.cloud"
vcr.unsubscribe(id, provider?)
Stop receiving callbacks from a provider subscription:
const subId = await voice.onCall('onCall');
// Later, when you want to stop receiving calls:
await vcr.unsubscribe(subId, 'voice');
Typical Application Structure
Node.js (Express):
import express from 'express';
import { vcr, Voice, Messages, State } from '@vonage/vcr-sdk';
const app = express();
app.use(express.json());
// Use getGlobalSession() for registering instance-wide subscriptions.
// Do NOT use vcr.createSession() here — it creates a random session that
// cannot be shared across replicas or requests.
const globalSession = vcr.getGlobalSession();
const voice = new Voice(globalSession);
const messages = new Messages(globalSession);
await voice.onCall('onCall');
await messages.onMessage('onMessage',
{ type: 'sms', number: process.env.VONAGE_NUMBER },
{ type: 'sms', number: undefined }
);
app.post('/onCall', async (req, res) => {
// Scope session to the conversation using its unique ID from the callback
const session = vcr.createSessionWithId(req.body.conversation_uuid);
const state = new State(session);
await state.set('status', 'answered');
res.json([{ action: 'talk', text: 'Hello from VCR!' }]);
});
app.post('/onMessage', async (req, res) => {
// Scope session to the sender — deterministic, same across replicas
const session = vcr.createSessionWithId(req.body.from);
const state = new State(session);
await state.set('lastMessage', req.body.text);
res.sendStatus(200);
});
app.get('/_/health', (req, res) => res.sendStatus(200));
// Must bind to 0.0.0.0 — VCR routes traffic via an internal proxy
const port = process.env.VCR_PORT || 8080;
app.listen(port, '0.0.0.0');
Python (FastAPI):
Important: Always bind your server to 0.0.0.0, not localhost or 127.0.0.1. VCR routes inbound traffic through an internal proxy and the application will not be reachable if it only listens on the loopback interface.