Visão geral do SDK

O Vonage Cloud Runtime SDK é a principal interface para todos os provedores de plataforma. Ele está disponível para Node.js e Python.

Tempo de execução Embalagem Versão mínima Versão mais recente Notas
Node.js @vonage/vcr-sdk 2.0.0 2.1.5 O SDK >=2.1.0 requer o Node.js 22 (LTS ativo)
Python vonage_cloud_runtime 2.0.0 2.1.5

O vcr Singleton

O SDK exporta um vcr singleton que serve como ponto de entrada para todas as operações.

Node.js:

import { vcr } from '@vonage/vcr-sdk';

Python:

from vonage_cloud_runtime.vcr import VCR
vcr = VCR()

O vcr O objeto oferece os seguintes métodos:

Método Descrição
vcr.createSession(ttl?) Criar uma nova sessão com TTL opcional em segundos (padrão: 7 dias)
vcr.createSessionWithId(id) Criar uma sessão com um ID específico
vcr.getSessionById(id) Recuperar uma sessão existente pelo ID
vcr.getSessionFromRequest(req) Extrair a sessão de uma solicitação HTTP recebida
vcr.getGlobalSession() Obter a sessão global (compartilhada entre todas as solicitações)
vcr.getInstanceState() Obter o estado no nível da instância (compartilhado entre as sessões dentro da instância)
vcr.getAccountState() Obter o estado da conta (compartilhado entre todos os aplicativos da conta)
vcr.getAppUrl() Obter a URL pública do aplicativo em execução
vcr.createVonageToken(params) Criar um JWT assinado para autenticação na API da Vonage
vcr.verifyAuth(token) Verificar um token de autenticação
vcr.unsubscribe(id, provider?) Cancelar a assinatura de um provedor

Sessões

É necessário haver sessões para inicializar qualquer provedor. O construtor de cada provedor recebe uma sessão como primeiro argumento.

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:

from vonage_cloud_runtime.vcr import VCR
vcr = VCR()

session = vcr.createSession()           # default 7-day TTL
session = vcr.createSession(3600)       # 1-hour TTL
session = vcr.createSessionWithId('user-123')
session = vcr.getSessionFromRequest(req)
session = vcr.getGlobalSession()

Propriedades e métodos da sessão:

Descrição
session.id O identificador exclusivo da sessão
session.getToken() Obter o token de autenticação da sessão
session.createUUID() Gerar um novo UUID
session.log(level, message, context?) Registrar uma mensagem com contexto estruturado

Definição do escopo da sessão — Orientações essenciais

Aviso: vcr.createSession() gera um aleatório, efêmero ID da sessão sempre que for chamado. Os dados armazenados nessa sessão (por meio de new State(session)) só pode ser acessado se você tiver exatamente esse ID de sessão. Se você chamar vcr.createSession() no escopo global/módulo e usá-lo com o State, cada réplica criará uma sessão diferente, e os dados ficarão isolados e inacessíveis para outras réplicas ou solicitações. Isso compromete a arquitetura sem estado.

Regras:

  • Nunca chamada vcr.createSession() no escopo global do aplicativo para inicializar o State ou outros provedores que necessitem de acesso a dados compartilhados.
  • Para estado compartilhado entre réplicas, use vcr.getInstanceState() (ou vcr.getAccountState() (para dados entre aplicativos).
  • Para estado por solicitação ou por conversa, use vcr.createSessionWithId(id) com um determinístico ID do contexto da solicitação — como, por exemplo, conversation_uuid a partir de um retorno de chamada de voz, do número de telefone do remetente em um retorno de chamada por SMS ou de um ID de usuário. Isso garante que a mesma sessão possa ser recuperada em todas as réplicas e solicitações.
  • vcr.getGlobalSession() destina-se ao registro de assinaturas válidas para toda a instância (por exemplo, voice.onCall(), messages.onMessage()). Deveria não ser usada como uma sessão de uso geral para o 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');
});

Veja Arquitetura sem estado para obter orientações detalhadas.

Inicialização do provedor

Todos os provedores seguem o mesmo padrão: passam uma sessão para o construtor.

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:

from vonage_cloud_runtime.vcr import VCR
from vonage_cloud_runtime.providers.state.state import State
from vonage_cloud_runtime.providers.queue.queue import Queue
from vonage_cloud_runtime.providers.scheduler.scheduler import Scheduler
from vonage_cloud_runtime.providers.messages.messages import Messages
from vonage_cloud_runtime.providers.voice.voice import Voice
from vonage_cloud_runtime.providers.assets.assets import Assets

vcr = VCR()
session = vcr.createSession()

# For shared state across replicas, use getInstanceState() — not State(session)
state     = vcr.getInstanceState()
queue     = Queue(session)
scheduler = Scheduler(session)
messages  = Messages(session)
voice     = Voice(session)
assets    = Assets(session)

Análise do âmbito estadual

O VCR oferece três níveis de estado:

Âmbito Como acessar Visibilidade
Estado da sessão new State(session) Restrito à sessão específica
Estado da instância vcr.getInstanceState() Compartilhado entre todas as sessões dentro da mesma instância
Status da conta vcr.getAccountState() Compartilhado entre todas as aplicações da conta da Vonage
// 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();

Veja Arquitetura sem estado para obter orientações sobre quando usar cada escopo.

Autenticação

Verificação das solicitações recebidas

Uso vcr.verifyAuth(token) para validar tokens nas solicitações recebidas:

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' });
  }
});

Criação de tokens da Vonage

Uso vcr.createVonageToken(params) para gerar JWTs assinados para a autenticação na API da Vonage:

const token = vcr.createVonageToken({
  exp: Math.floor(Date.now() / 1000) + 3600, // 1-hour expiry
  aclPaths: { '/*/users/**': {} },            // optional ACL paths
  subject: 'user-123',                        // optional subject
});
Parâmetro Tipo Obrigatório Descrição
exp número Sim Prazo de validade do token como timestamp do Unix (segundos)
aclPaths Record<string, any> Não Restrições de caminho do ACL
subject sequência de caracteres Não Assunto do token (por exemplo, identificador do usuário)

Métodos utilitários

vcr.getAppUrl()

Retorna a URL pública da instância do VCR em execução. Equivalente a ler o VCR_INSTANCE_PUBLIC_URL variável de ambiente.

const appUrl = vcr.getAppUrl();
// e.g. "https://my-app-dev.use1.runtime.vonage.cloud"

vcr.unsubscribe(id, provider?)

Para deixar de receber chamadas de retorno de uma assinatura de provedor:

const subId = await voice.onCall('onCall');

// Later, when you want to stop receiving calls:
await vcr.unsubscribe(subId, 'voice');

Estrutura típica de uma aplicação

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):

import os
from fastapi import FastAPI, Request
from vonage_cloud_runtime.vcr import VCR
from vonage_cloud_runtime.providers.voice.voice import Voice
from vonage_cloud_runtime.providers.messages.messages import Messages
from vonage_cloud_runtime.providers.state.state import State

app = FastAPI()
vcr = VCR()

# 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.
global_session = vcr.getGlobalSession()
voice = Voice(global_session)
messages = Messages(global_session)

@app.on_event("startup")
async def startup():
    await voice.onCall("onCall")
    await messages.onMessage("onMessage",
        {"type": "sms", "number": os.environ.get("VONAGE_NUMBER")},
        {"type": "sms", "number": None}
    )

@app.post("/onCall")
async def on_call(request: Request):
    body = await request.json()
    # Scope session to the conversation using its unique ID from the callback
    session = vcr.createSessionWithId(body.get("conversation_uuid"))
    state = State(session)
    await state.set("status", "answered")
    return [{"action": "talk", "text": "Hello from VCR!"}]

@app.post("/onMessage")
async def on_message(request: Request):
    body = await request.json()
    # Scope session to the sender — deterministic, same across replicas
    session = vcr.createSessionWithId(body.get("from"))
    state = State(session)
    await state.set("lastMessage", body.get("text"))
    return {"status": "ok"}

@app.get("/_/health")
async def health():
    return {"status": "ok"}

if __name__ == "__main__":
    import uvicorn
    port = int(os.environ.get("VCR_PORT", 8080))
    uvicorn.run(app, host="0.0.0.0", port=port)

Importante: Sempre configure seu servidor para 0.0.0.0, não localhost ou 127.0.0.1. O VCR encaminha o tráfego de entrada por meio de um proxy interno, e o aplicativo não estará acessível se estiver escutando apenas na interface de loopback.