@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()andimport - Detailed errors - Typed error classes for every HTTP status code
Installation
npm install @rastova/rastiyarn add @rastova/rastipnpm add @rastova/rastiRequirements: 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-Afterheader - 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
