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

@rastova/rasti

v0.1.5

Published

Node.js SDK for the Rasti Messaging API

Downloads

580

Readme

@rastova/rasti

The official Node.js SDK for the Rasti Messaging API. Send WhatsApp messages, SMS, manage templates, channels, media, and webhooks with full TypeScript support.

Features

  • WhatsApp & SMS - Send text, images, video, documents, locations, contacts, interactive messages, templates, and more
  • Full TypeScript - Strict types for every method, parameter, and response
  • Zero dependencies - Uses native fetch (Node.js 18+)
  • Automatic retries - Exponential backoff with jitter for transient failures
  • Idempotent requests - Safe retries for POST requests with idempotency keys
  • Dual CJS + ESM - Works with require() and import
  • Detailed errors - Typed error classes for every HTTP status code

Installation

npm install @rastova/rasti
yarn add @rastova/rasti
pnpm add @rastova/rasti

Requirements: Node.js 18 or later

Getting Your API Key

To get your Rasti API key and start sending WhatsApp and SMS messages, contact us here.

Quick Start

import Rasti from '@rastova/rasti';

const rasti = new Rasti({
  apiKey: 'rsk_your_api_key', // or set RASTI_API_KEY env var
});

// Send a WhatsApp text message
const response = await rasti.messages.sendText({
  to: '1234567890',
  text: { body: 'Hello from Rasti!' },
});

console.log(response.messageId); // => "msg_abc123"

Configuration

const rasti = new Rasti({
  apiKey: 'rsk_...',             // Default: process.env.RASTI_API_KEY
  baseURL: 'https://api.rastova.io/v1', // Default: https://api.rastova.io/v1
  timeout: 30_000,               // Default: 30 seconds (ms)
  maxRetries: 2,                 // Default: 2 retries
  fetch: customFetch,            // Default: globalThis.fetch
});

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiKey | string | RASTI_API_KEY env var | Your Rasti API key | | baseURL | string | https://api.rastova.io/v1 | API base URL (or RASTI_BASE_URL env var) | | timeout | number | 30000 | Request timeout in milliseconds | | maxRetries | number | 2 | Max retry attempts for retryable errors | | fetch | function | globalThis.fetch | Custom fetch implementation |

API Reference

Messages

Send messages across WhatsApp and SMS channels.

WhatsApp Messages

// Text
await rasti.messages.sendText({
  to: '1234567890',
  text: { body: 'Hello!', previewUrl: true },
});

// Image
await rasti.messages.sendImage({
  to: '1234567890',
  image: { link: 'https://example.com/photo.jpg', caption: 'Check this out' },
});

// Video
await rasti.messages.sendVideo({
  to: '1234567890',
  video: { link: 'https://example.com/video.mp4', caption: 'Watch this' },
});

// Audio
await rasti.messages.sendAudio({
  to: '1234567890',
  audio: { link: 'https://example.com/audio.mp3' },
});

// Document
await rasti.messages.sendDocument({
  to: '1234567890',
  document: { link: 'https://example.com/file.pdf', filename: 'invoice.pdf' },
});

// Sticker
await rasti.messages.sendSticker({
  to: '1234567890',
  sticker: { link: 'https://example.com/sticker.webp' },
});

// Location
await rasti.messages.sendLocation({
  to: '1234567890',
  location: { latitude: 37.7749, longitude: -122.4194, name: 'San Francisco' },
});

// Contacts
await rasti.messages.sendContacts({
  to: '1234567890',
  contacts: [
    {
      name: { formattedName: 'Jane Doe', firstName: 'Jane', lastName: 'Doe' },
      phones: [{ phone: '+1555123456', type: 'WORK' }],
      emails: [{ email: '[email protected]', type: 'WORK' }],
    },
  ],
});

// Reaction
await rasti.messages.sendReaction({
  to: '1234567890',
  reaction: { messageId: 'wamid.abc123', emoji: '👍' },
});

// Mark as read
await rasti.messages.markAsRead({
  messageId: 'wamid.abc123',
});

Interactive Messages

// Buttons
await rasti.messages.sendInteractiveButtons({
  to: '1234567890',
  interactiveButtons: {
    body: { text: 'Choose an option:' },
    footer: { text: 'Tap a button below' },
    buttons: [
      { id: 'btn_yes', title: 'Yes' },
      { id: 'btn_no', title: 'No' },
    ],
  },
});

// List
await rasti.messages.sendInteractiveList({
  to: '1234567890',
  interactiveList: {
    body: { text: 'Browse our menu:' },
    button: 'View Menu',
    sections: [
      {
        title: 'Drinks',
        rows: [
          { id: 'coffee', title: 'Coffee', description: '$3.50' },
          { id: 'tea', title: 'Tea', description: '$2.50' },
        ],
      },
    ],
  },
});

// Call-to-Action URL
await rasti.messages.sendCtaUrl({
  to: '1234567890',
  ctaUrl: {
    body: { text: 'Visit our website for more details' },
    button: { displayText: 'Visit Website', url: 'https://example.com' },
  },
});

// Carousel
await rasti.messages.sendCarousel({
  to: '1234567890',
  carousel: {
    body: { text: 'Check out our products:' },
    cards: [
      {
        header: { type: 'image', image: { link: 'https://example.com/product1.jpg' } },
        body: { text: 'Product A - $29.99' },
        buttons: [{ type: 'quick_reply', title: 'Buy Now', id: 'buy_a' }],
      },
      {
        header: { type: 'image', image: { link: 'https://example.com/product2.jpg' } },
        body: { text: 'Product B - $39.99' },
        buttons: [{ type: 'url', title: 'Details', url: 'https://example.com/b' }],
      },
    ],
  },
});

// Location Request
await rasti.messages.sendLocationRequest({
  to: '1234567890',
  locationRequest: { body: { text: 'Please share your location for delivery' } },
});

// Address
await rasti.messages.sendAddress({
  to: '1234567890',
  address: {
    body: { text: 'Confirm your shipping address' },
    country: 'US',
  },
});

Template Messages

await rasti.messages.sendTemplate({
  to: '1234567890',
  template: {
    name: 'order_confirmation',
    language: 'en',
    components: [
      {
        type: 'body',
        parameters: [
          { type: 'text', text: 'John' },
          { type: 'text', text: '#12345' },
        ],
      },
    ],
  },
});

SMS Messages

const sms = await rasti.messages.sendSms({
  to: '1234567890',
  body: 'Your verification code is 123456',
  senderId: 'MyApp',
});

console.log(sms.smsPartCount);    // => 1
console.log(sms.characterCount);  // => 38
console.log(sms.encoding);        // => "plain"

WhatsApp Channels

Manage your WhatsApp Business connections.

// Connect a new channel
const channel = await rasti.whatsapp.channels.connect({
  name: 'Main Business Line',
  wabaId: 'waba_123',
  businessId: 'biz_456',
  phoneNumberId: 'pn_789',
  phoneNumber: '+1234567890',
  accessToken: 'EAAx...',
});

// List all channels
const { data, meta } = await rasti.whatsapp.channels.list({ limit: 10 });

// Get current channel
const current = await rasti.whatsapp.channels.getCurrent();

// Update channel settings
await rasti.whatsapp.channels.update({
  connectionId: 'conn_123',
  displayName: 'Updated Name',
});

// Resync channel metadata
await rasti.whatsapp.channels.resync('conn_123');

// Disconnect
await rasti.whatsapp.channels.disconnect({ connectionId: 'conn_123' });

WhatsApp Templates

Create and manage message templates.

// Create a template
const template = await rasti.whatsapp.templates.create({
  name: 'welcome_message',
  language: 'en',
  category: 'MARKETING',
  components: [
    { type: 'HEADER', format: 'TEXT', text: 'Welcome, {{1}}!' },
    { type: 'BODY', text: 'Thanks for joining us. Your code is {{1}}.' },
    { type: 'FOOTER', text: 'Reply STOP to unsubscribe' },
    {
      type: 'BUTTONS',
      buttons: [
        { type: 'QUICK_REPLY', text: 'Get Started' },
        { type: 'URL', text: 'Visit Site', url: 'https://example.com/{{1}}' },
      ],
    },
  ],
});

// List templates (with filters)
const { data } = await rasti.whatsapp.templates.list({
  status: 'APPROVED',
  category: 'MARKETING',
  limit: 20,
});

// Get a specific template
const tmpl = await rasti.whatsapp.templates.get('tmpl_abc123');

// Edit a template
await rasti.whatsapp.templates.edit('tmpl_abc123', {
  components: [{ type: 'BODY', text: 'Updated body text with {{1}}.' }],
});

// Delete a template
await rasti.whatsapp.templates.delete('tmpl_abc123');

// Sync templates from WhatsApp
const sync = await rasti.whatsapp.templates.sync();
console.log(`Synced: ${sync.synced}, Created: ${sync.created}`);

WhatsApp Media

Retrieve, download, and delete media files.

// Get media metadata
const media = await rasti.whatsapp.media.get('media_abc123');
console.log(media.mimeType); // => "image/jpeg"
console.log(media.fileSize); // => 45230

// Download media (returns raw Response)
const response = await rasti.whatsapp.media.download('media_abc123');
const buffer = Buffer.from(await response.arrayBuffer());

// Delete media
await rasti.whatsapp.media.delete('media_abc123');

WhatsApp Business Profile

// Get profile
const profile = await rasti.whatsapp.profile.get('conn_123');

// Update profile
await rasti.whatsapp.profile.update('conn_123', {
  about: 'We help businesses grow',
  description: 'Leading provider of business solutions',
  email: '[email protected]',
  websites: ['https://example.com'],
  vertical: 'PROF_SERVICES',
  address: '123 Main St, San Francisco, CA',
});

SMS Channels

// Connect an SMS channel
const smsChannel = await rasti.sms.channels.connect({
  providerName: 'whysms',
  senderId: 'MyApp',
});

// Get current SMS channel
const current = await rasti.sms.channels.getCurrent();

Webhooks

Subscribe to real-time events.

// Create a webhook
const webhook = await rasti.webhooks.create({
  url: 'https://example.com/webhooks/rasti',
  events: ['message.received', 'message.delivered', 'message.failed'],
});

console.log(webhook.signingSecret); // Save this to verify webhook signatures

// List webhooks
const { data } = await rasti.webhooks.list({ limit: 10 });

// Delete a webhook
await rasti.webhooks.delete('wh_abc123');

Available Webhook Events

| Category | Events | |----------|--------| | Messages | message.received, message.sent, message.delivered, message.read, message.failed | | Account | account.violation, account.restriction, account.ban, account.review, account.alert | | Phone | phone_number.quality_update, phone_number.name_update | | Business | business.capability_update | | Templates | template.status_update, template.quality_update | | Security | security.alert | | SMS | sms.sent, sms.delivered, sms.failed |

You can access the full list programmatically:

import { WEBHOOK_EVENT_TYPES } from '@rastova/rasti';

Advanced Usage

Idempotent Requests

Pass an idempotencyKey to safely retry message sends without duplicates:

await rasti.messages.sendText({
  to: '1234567890',
  text: { body: 'Important notification' },
  idempotencyKey: 'unique-key-abc-123',
});

Specifying a Connection

If you have multiple WhatsApp connections, pass connectionId to target a specific one:

await rasti.messages.sendText({
  to: '1234567890',
  text: { body: 'Hello!' },
  connectionId: 'conn_my_second_line',
});

Replying to Messages

Use the context field to reply to a specific message:

await rasti.messages.sendText({
  to: '1234567890',
  text: { body: 'Got it, thanks!' },
  context: { messageId: 'wamid.original_message_id' },
});

Media by ID or URL

Media methods accept either a hosted URL (link) or a Rasti media ID (id):

// By URL
await rasti.messages.sendImage({
  to: '1234567890',
  image: { link: 'https://example.com/photo.jpg' },
});

// By media ID (previously uploaded)
await rasti.messages.sendImage({
  to: '1234567890',
  image: { id: 'media_abc123' },
});

Pagination

List endpoints return paginated results with metadata:

const result = await rasti.whatsapp.templates.list({ limit: 10, offset: 0 });

console.log(result.data);           // TemplateResponse[]
console.log(result.meta.pagination); // { total: 42, limit: 10, offset: 0 }

Error Handling

All API errors throw typed error classes with rich metadata:

import { Rasti, AuthenticationError, RateLimitError, APIError } from '@rastova/rasti';

try {
  await rasti.messages.sendText({ to: '123', text: { body: 'Hi' } });
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log('Rate limited. Retry after:', error.headers.get('retry-after'));
  } else if (error instanceof AuthenticationError) {
    console.log('Invalid API key');
  } else if (error instanceof APIError) {
    console.log(error.status);    // HTTP status code
    console.log(error.code);      // API error code
    console.log(error.message);   // Human-readable message
    console.log(error.requestId); // x-request-id header
    console.log(error.details);   // Additional error details
  }
}

Error Classes

| Class | Status | Description | |-------|--------|-------------| | BadRequestError | 400 | Invalid request parameters | | AuthenticationError | 401 | Invalid or missing API key | | PermissionDeniedError | 403 | Insufficient permissions | | NotFoundError | 404 | Resource not found | | ConflictError | 409 | Resource conflict | | UnprocessableEntityError | 422 | Validation failed | | RateLimitError | 429 | Too many requests | | InternalServerError | 500 | Server error | | BadGatewayError | 502 | Bad gateway | | ServiceUnavailableError | 503 | Service unavailable | | APIConnectionError | - | Network/connection failure | | APIConnectionTimeoutError | - | Request timed out |

Automatic Retries

The SDK automatically retries on transient errors (408, 429, 500, 502, 503, 504) with exponential backoff. POST requests are only retried when an idempotencyKey is provided.

  • Backoff formula: min(0.5 * 2^attempt, 8s) with random jitter
  • 429 responses: Respects the Retry-After header
  • Default: 2 max retries (configurable via maxRetries)

TypeScript

All parameter and response types are exported for full type safety:

import type {
  // Client
  ClientOptions,
  RequestOptions,
  PaginationParams,
  ListResponse,

  // Messages
  SendTextParams,
  SendImageParams,
  SendVideoParams,
  SendAudioParams,
  SendDocumentParams,
  SendStickerParams,
  SendLocationParams,
  SendContactsParams,
  SendReactionParams,
  SendInteractiveButtonsParams,
  SendInteractiveListParams,
  SendCtaUrlParams,
  SendCarouselParams,
  SendLocationRequestParams,
  SendAddressParams,
  SendTemplateParams,
  SendSmsParams,
  MarkAsReadParams,
  WhatsAppMessageResponse,
  SmsMessageResponse,

  // WhatsApp
  ConnectChannelParams,
  UpdateChannelParams,
  ChannelResponse,
  CreateTemplateParams,
  EditTemplateParams,
  ListTemplatesParams,
  TemplateResponse,
  MediaResponse,
  UpdateProfileParams,
  ProfileResponse,

  // SMS
  ConnectSmsChannelParams,
  SmsChannelResponse,

  // Webhooks
  CreateWebhookParams,
  WebhookResponse,
  WebhookEventType,
} from '@rastova/rasti';

CommonJS

const { Rasti } = require('@rastova/rasti');

const rasti = new Rasti({ apiKey: 'rsk_...' });

License

MIT