@volara/sdk
v0.1.0
Published
Official TypeScript/JavaScript SDK for the Volara API — messaging, conversations, CRM, broadcasts, knowledge, metrics, and agency white-label. Works in Node 18+, browsers, and Cloudflare Workers.
Downloads
76
Maintainers
Readme
@volara/sdk
The official TypeScript/JavaScript SDK for Volara — send WhatsApp and omnichannel messages, manage conversations and contacts, run broadcasts, search your knowledge base, read metrics, and (for agencies) manage white-label client orgs.
One small, typed, dependency-free client that runs anywhere fetch does: Node 18+, browsers, and
Cloudflare Workers / edge.
Install
npm install @volara/sdk
# or
pnpm add @volara/sdk
# or
bun add @volara/sdkAuthentication
Create a developer API key in your Volara dashboard. The key carries your tenant and scope, so the SDK just sends it on every request.
import { Volara } from '@volara/sdk'
// Pass it explicitly...
const volara = new Volara({ apiKey: 'sk_live_xxx' })
// ...or set VOLARA_API_KEY and let the SDK pick it up.
const volara2 = new Volara()Self-hosting or using a regional endpoint? Override the base URL:
const volara = new Volara({ apiKey: '...', baseUrl: 'https://api.your-region.volara.chat' })Quickstart — send a message in under 5 lines
import { Volara } from '@volara/sdk'
const volara = new Volara({ apiKey: process.env.VOLARA_API_KEY })
await volara.messages.send({ conversationId: 'conv_123', text: 'Halo 👋' })Examples
Conversations
const { data, total } = await volara.conversations.list({ status: 'open', perPage: 20 })
const conversation = await volara.conversations.get('conv_123')
// Auto-paging — iterate every conversation without managing page numbers.
for await (const c of volara.conversations.iterate({ status: 'open' })) {
console.log(c.id, c.status)
}Messages
await volara.messages.send({ conversationId: 'conv_123', text: 'Your order shipped!' })
// Richer payloads (templates, media) pass through as the API documents them.
await volara.messages.send({
conversationId: 'conv_123',
type: 'template',
content: { name: 'order_update', language: 'id', variables: ['#A123'] },
})Contacts (CRM)
const { data } = await volara.contacts.list({ q: 'budi', perPage: 50 })
const contact = await volara.contacts.create({
name: 'Budi Santoso',
phoneNumber: '+6281234567890',
email: '[email protected]',
})
for await (const c of volara.contacts.iterate()) {
// process every contact
}Broadcasts
const { data } = await volara.broadcasts.list()
const broadcast = await volara.broadcasts.create({
title: 'Ramadan Promo',
messageContent: 'Diskon 30% minggu ini!',
})Knowledge base
const sources = await volara.knowledge.search('refund policy') // sources (default)
const faqs = await volara.knowledge.search('how to track order', { scope: 'faqs' })
// or the explicit helpers:
await volara.knowledge.sources('shipping')
await volara.knowledge.faqs('returns')Metrics
const dashboard = await volara.metrics.dashboard()Agency (white-label)
Requires an agency-scoped API key.
const clients = await volara.agency.clients.list()
const overview = await volara.agency.overview()
const newClient = await volara.agency.clients.createOrg({ name: 'Acme Retail' })Escape hatch
Need an endpoint the SDK doesn't model yet? Call it directly under /api/v1:
const tickets = await volara.request('/tickets', { query: { status: 'open' } })
const raw = await volara.raw('/something', { method: 'POST', body: { ... } }) // no envelope unwrapError handling
Every non-2xx response, timeout, and network failure throws a typed VolaraError. Always log the
requestId — it lets Volara support trace the exact call.
import { Volara, VolaraError } from '@volara/sdk'
try {
await volara.conversations.get('missing')
} catch (err) {
if (err instanceof VolaraError) {
console.error(err.status) // e.g. 404
console.error(err.code) // e.g. "VLR-..." (when the API sends one)
console.error(err.requestId) // include this in support tickets
console.error(err.message)
}
}VolaraTimeoutError and VolaraConnectionError are subclasses of VolaraError, so a single
catch (err instanceof VolaraError) covers everything.
Resilience
The client handles the boring parts for you:
- Retries on
429and5xx(and network blips) with exponential backoff + jitter, honoring theRetry-Afterheader. Tune withmaxRetries(default2). - Timeouts via
AbortSignal.timeout. Tune withtimeoutMs(default30000). - Idempotency keys auto-attached to every write (
POST/PUT/PATCH/DELETE) so retries can't double-create. Override per call by passingidempotencyKey.
const volara = new Volara({ apiKey: '...', timeoutMs: 10_000, maxRetries: 4 })Webhook verification
Verify incoming webhooks with HMAC-SHA256. The helper uses Web Crypto, so it works in Node, the browser, and Workers — and does a constant-time comparison.
import { verifyWebhook } from '@volara/sdk/webhooks'
// Read the RAW body and verify BEFORE parsing — re-serializing changes the bytes.
const raw = await request.text()
const ok = await verifyWebhook({
payload: raw,
signature: request.headers.get('x-volara-signature') ?? '',
secret: process.env.VOLARA_WEBHOOK_SECRET,
})
if (!ok) return new Response('invalid signature', { status: 401 })
const event = JSON.parse(raw)
if (event.type === 'message.received') {
// ...
}Edge / Cloudflare Workers
No configuration needed — the SDK uses the global fetch and Web Crypto, both of which Workers
provide. If your runtime lacks a global fetch, inject one:
const volara = new Volara({ apiKey: '...', fetch: myFetch })TypeScript
Ships ESM + CommonJS builds and full type declarations. Response record shapes are intentionally permissive (extra server fields never break your build); they tighten up as the Volara OpenAPI v1 spec is frozen.
License
MIT
