Skip to content

Node.js SDK

The official @rymi/node package provides convenient, strongly-typed access to the Rymi REST API from any server-side JavaScript or TypeScript application.

Installation

Install the package using your preferred package manager:

bash
npm install @rymi/node
# or
yarn add @rymi/node
# or
pnpm add @rymi/node

Initialization

Import the Rymi class and initialize it with your API key. If you omit the apiKey configuration, the SDK will automatically look for a RYMI_API_KEY environment variable.

typescript
import { Rymi } from '@rymi/node';

// Initialize with an explicit API Key:
const rymi = new Rymi({ apiKey: 'rymi_your_secret_key' });

// OR rely on process.env.RYMI_API_KEY:
const rymiEnv = new Rymi();

Agents

Create, list, and manage your AI voice personas.

typescript
// Create a new conversational Agent
const agent = await rymi.agents.create({
    name: 'Customer Support',
    system_prompt: 'You are a helpful customer support assistant for Acme Corp.',
    voice: 'Aoede',
    language: 'en-US'
});

console.log(agent.id);

// List all configured agents
const { agents } = await rymi.agents.list();

Clone an agent

typescript
const clone = await rymi.agents.clone(agent.id);
console.log(clone.id); // new agent ID, name gets " (Copy)" appended

List calls for an agent

typescript
const { calls, total } = await rymi.agents.listCalls(agent.id, { limit: 20 });

Validate before publishing

Check whether an agent's config is ready to go live. Returns a validation report without changing anything.

typescript
const report = await rymi.agents.validatePublish({ agent_id: agent.id });
if (!report.valid) {
    console.error(report.errors);
}

Apply a structured change-set

Validate and resolve a flat key/value diff against the field registry. Does not persist — follow up with update().

typescript
const result = await rymi.agents.applyChanges({
    currentConfig: { name: 'Support', voice: 'Aoede' },
    changes: [{ key: 'voice', value: 'Charon' }],
    mode: 'edit',
});
// result.config has the merged config; result.applied lists what changed
await rymi.agents.update(agent.id, result.config);

Enrich company from website

Generate a company description for the agent's persona using AI + Google Search grounding.

typescript
const info = await rymi.agents.enrichCompany({
    companyName: 'Acme Corp',
    websiteUrl: 'https://acme.example.com',
});
if (info.enriched) {
    console.log(info.companyDescription);
}

Calls

Create voice call sessions with one or more participants. Each call maps to a single room that can include WebRTC (browser) and PSTN (phone) participants.

typescript
// Start an outbound PSTN call
const call = await rymi.calls.create({
    agent_id: agent.id,
    participants: [
        {
            transport: 'pstn',
            identity: '+15551234567',
            from_number: '+15559876543'
        }
    ],
    metadata: {
        customer_name: 'Alice',
        order_status: 'shipped'
    }
});

console.log(`Call queued with ID: ${call.id}`);
console.log(`Room: ${call.room_name}`);

// Start a WebRTC call (returns a LiveKit token)
const webCall = await rymi.calls.create({
    agent_id: agent.id,
    participants: [
        { transport: 'webrtc', identity: 'browser-user-1' }
    ]
});

// Access the participant's LiveKit credentials
const participant = webCall.participants[0];
console.log(`LiveKit URL: ${participant.access?.url}`);
console.log(`Token: ${participant.access?.token}`);

// Retrieve transcript and metadata after the call ends
const details = await rymi.calls.retrieve(call.id);
const transcript = await rymi.calls.transcript(call.id);

Batch Calls

Queue up to 500 outbound PSTN recipients in one request.

typescript
const batch = await rymi.calls.batch({
    agent_id: agent.id,
    to: ['+15551234567', '+15557654321'],
    from_number: '+15559876543',
    variables: { batch_label: 'renewal-q2' }
});

console.log(batch.batch_id, batch.queued);

Phone Numbers

Register carrier-owned BYOC numbers, list registered numbers, and attach them to agents. Rymi does not purchase phone numbers; provision numbers in your carrier account first.

typescript
await rymi.numbers.register('+15559876543');

const { numbers } = await rymi.numbers.list();

await rymi.numbers.attach(numbers[0].number, agent.id);

Verifying Webhooks

When your application receives asynchronous events (like call.intelligence.ready), you must cryptographically verify the webhook signature to protect against replay attacks and spoofing.

The Node SDK includes a built-in helper to securely validate the Rymi-Signature HMAC.

typescript
import express from 'express';
import { Rymi } from '@rymi/node';

const app = express();
const rymi = new Rymi();

// Ensure you parse the body as raw TEXT or BUFFER for accurate HMAC verification
app.post('/webhook', express.text({ type: 'application/json' }), (req, res) => {
    const signature = req.headers['x-rymi-signature'] as string;
    const timestamp = req.headers['x-rymi-timestamp'] as string;
    const payload = req.body; // Raw JSON string
    
    try {
        rymi.webhooks.verifySignature(
            payload,
            signature,
            timestamp,
            process.env.RYMI_WEBHOOK_SECRET!
        );
        
        console.log('Webhook authenticated successfully!');
        
        // Process webhook event safely...
        const event = JSON.parse(payload);
        if (event.type === 'call.intelligence.ready') {
            console.log('Call intelligence:', event.data.call_id, event.data.summary);
        }
        
        res.status(200).send('OK');
    } catch (error) {
        console.error('Invalid webhook signature!', error);
        res.status(401).send('Unauthorized');
    }
});