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

@ararahq/sdk

v2.0.0

Published

SDK oficial da AraraHQ para Node.js. Simples, tipado e focado no desenvolvedor.

Readme

Arara Node SDK

npm TypeScript License Docs

Official Node.js SDK for AraraHQ — the developer-first WhatsApp API. Simple, typed, and built for scale.

Installation

npm install @ararahq/sdk

Configuration

import { NodeSDK } from '@ararahq/sdk';

const sdk = new NodeSDK({
  apiKey: 'ara_live_...'
});

baseUrl defaults to https://api.ararahq.com. All options:

const sdk = new NodeSDK({
  apiKey: 'ara_live_...',
  baseUrl: 'https://api.ararahq.com',
  timeout: 10000,
  maxRetries: 3
});

The SDK automatically retries network errors, 5xx and 429 responses with exponential backoff, honoring the Retry-After header when present. Only requests that are safe to replay are retried: GET/PUT/DELETE, and POST carrying an Idempotency-Key. messages.send, messages.sendBatch and campaigns.create always send one (yours, or a UUID v4 generated per call and reused on every retry), so a retry never duplicates a send or a charge. Set maxRetries: 0 to disable.

API key permissions

GET on contacts, conversations, wallet, smart links, opt-outs and auth.me() requires an ADMIN key. Keys with only READ get AuthenticationError (403) on those reads. API keys, user profile and organization webhook are not manageable with an API key; use the dashboard.

Resources

1. Current user (sdk.auth)

const me = await sdk.auth.me(); // GET /auth/me, ADMIN key

2. Messages (sdk.messages)

receiver accepts whatsapp:+5511..., +5511... or digits only.

// Template standard
const response = await sdk.messages.send({
  receiver: "whatsapp:+5511999998888",
  templateName: "welcome",
  templateVariables: ["John"]
});

// Template com Mídia (Header de Imagem/PDF)
const mediaResponse = await sdk.messages.send({
  receiver: "whatsapp:+5511999998888",
  templateName: "invoice_ready",
  templateVariables: ["John", "January"],
  media_url: "https://your-media.com/invoice.pdf"
});

// Mensagem de Sessão (Texto Livre)
const sessionResponse = await sdk.messages.send({
  receiver: "whatsapp:+5511999998888",
  body: "Olá! Como posso ajudar?"
});

// Envio idempotente com sua própria chave (sem ela o SDK gera uma por chamada)
const idempotentResponse = await sdk.messages.send(
  {
    receiver: "whatsapp:+5511999998888",
    templateName: "welcome",
    templateVariables: ["John"]
  },
  { idempotencyKey: "order-8231-welcome" }
);

// Lote: um template, até 1000 destinatários
const batch = await sdk.messages.sendBatch({
  templateName: "welcome",
  messages: [{ receiver: "+5511999998888", variables: ["John"] }]
});

// Consulta por id
const message = await sdk.messages.get(response.id);

3. Templates (sdk.templates)

get, getStatus, delete and analytics take the template id (UUID), not the name. To find a template by name, filter the list.

const { data, pagination } = await sdk.templates.list({ page: 0, size: 50 });

const [welcome] = (await sdk.templates.list({ name: 'welcome' })).data;
const details = await sdk.templates.get(welcome.id);
const status = await sdk.templates.getStatus(welcome.id);
const analytics = await sdk.templates.analytics(welcome.id, { period: '30d' }); // deliveryRate: "97.5"
const allAnalytics = await sdk.templates.analyticsAll({ period: '7d' });

await sdk.templates.create({
  name: "promo_christmas",
  category: "MARKETING",
  language: "pt_BR",
  body: "Hi {{1}}, check our Christmas deals!",
  samples: { "1": "John" }
});

await sdk.templates.delete(welcome.id);

4. Opt-outs (sdk.optOuts)

Phones must be E.164 with the leading +; other formats throw RangeError before calling the API.

await sdk.optOuts.create({ phone: "+5511999998888", reason: "pediu pra sair" });
const { optedOut } = await sdk.optOuts.get("+5511999998888");
const { items, total } = await sdk.optOuts.list();
await sdk.optOuts.delete("+5511999998888");

5. Webhook Events

import { AraraWebhookEvent, WebhookUtils } from '@ararahq/sdk';
import express from 'express';

const app = express();

app.post('/webhook/arara', express.json(), (req, res) => {
    const event = req.body as AraraWebhookEvent;

    if (WebhookUtils.isMessageStatusEvent(event)) {
        const { messageId, status, receiver } = event.data;
        console.log(`Message ${messageId} to ${receiver}: ${status}`);
    }

    if (WebhookUtils.isInboundMessageEvent(event)) {
        const { from, body } = event.data;
        console.log(`New message from ${from}: ${body}`);
    }

    res.sendStatus(200);
});

6. Contacts (sdk.contacts)

const page = await sdk.contacts.list(0, 50);
const contact = await sdk.contacts.get('+5511999998888');
await sdk.contacts.update('+5511999998888', { name: "Maria", tags: ["vip"] });
await sdk.contacts.importBatch([{ name: "Maria", phone: "+5511999998888" }]);
const stats = await sdk.contacts.stats();
const history = await sdk.contacts.messages('+5511999998888', 30);

7. Conversations (sdk.conversations)

const conversations = await sdk.conversations.list();
const messages = await sdk.conversations.messages('conversation-id');
await sdk.conversations.reply({ conversationId: 'conversation-id', body: "Oi, tudo certo?" });
const windows = await sdk.conversations.windowStatus(['+5511999998888']);

8. Campaigns (sdk.campaigns)

const estimate = await sdk.campaigns.estimate('promo', 1200);

const campaign = await sdk.campaigns.create({
  name: "Black Friday",
  templateName: "promo",
  contacts: [{ to: "whatsapp:+5511999998888", variables: ["Maria"] }]
});

const detail = await sdk.campaigns.get(campaign.id);
await sdk.campaigns.cancel(campaign.id);

9. Wallet (sdk.wallet)

const transactions = await sdk.wallet.transactions(0, 20);
const autoRecharge = await sdk.wallet.getAutoRecharge();
await sdk.wallet.updateAutoRecharge({ enabled: true, threshold: 50, amount: 200 });

10. Numbers (sdk.numbers)

const { numbers, slot } = await sdk.numbers.list();
await sdk.numbers.update(numbers[0].id, { alias: "Suporte" });
const warming = await sdk.numbers.warming(numbers[0].id);

11. Smart Links (sdk.smartLinks)

const link = await sdk.smartLinks.create({
  name: "Promo",
  phoneNumber: "+5511999998888",
  defaultText: "Quero a oferta"
});
const stats = await sdk.smartLinks.stats(link.id);
const { data: links, pagination } = await sdk.smartLinks.list({ page: 0, size: 50 });

12. Raw API (sdk.api)

Escape hatch for endpoints without a typed resource yet. Inherits auth, baseUrl, timeout and retries.

const data = await sdk.api.get('/v1/some/endpoint');
await sdk.api.post('/v1/some/endpoint', { foo: "bar" });

Error Handling

Every failed request throws a typed AraraError with the parsed API error envelope:

import { AraraError } from '@ararahq/sdk';

try {
  await sdk.messages.send({ receiver: "whatsapp:+5511999998888", body: "Oi" });
} catch (error) {
  if (error instanceof AraraError) {
    console.error(error.statusCode, error.code, error.message, error.details);
    if (error.statusCode === 429 && error.retryAfter !== undefined) {
      console.error(`Retry after ${error.retryAfter}s`);
    }
  }
}

| Property | Type | Description | | --- | --- | --- | | statusCode | number \| undefined | HTTP status. undefined for network errors | | code | string | API error code (e.g. INSUFFICIENT_FUNDS). NETWORK_ERROR when the request never got a response. A 429 carries SEND_RATE_LIMITED, MARKETING_FREQUENCY_EXCEEDED, BATCH_BUSY or RATE_LIMIT_EXCEEDED | | message | string | Human-readable message from the API | | details | object \| undefined | Extra context from the API | | retryAfter | number \| undefined | Seconds to wait, from the Retry-After header |

Two subclasses narrow the common cases:

  • PlanFeatureLockedError (403 PLAN_FEATURE_LOCKED): exposes feature, currentPlan and upgradeTo.
  • AuthenticationError (401, or 403 without an error code): the key was rejected (invalid, expired, IP not allowed, missing permission). Exception: messages.get(id) on a message owned by another user answers an empty 403, which the SDK raises as a plain AraraError with code RESOURCE_FORBIDDEN.
import { PlanFeatureLockedError, AuthenticationError } from '@ararahq/sdk';

try {
  await sdk.campaigns.create(campaign);
} catch (error) {
  if (error instanceof PlanFeatureLockedError) {
    showUpgrade(error.upgradeTo);
  } else if (error instanceof AuthenticationError) {
    rotateKey();
  }
}

Migrating from 1.x

See CHANGELOG.md.

License

MIT