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

@qrcommunication/withallo-sdk

v0.2.10

Published

TypeScript SDK for Withallo (Allo) API — webhooks, calls, contacts, SMS and phone numbers. Isomorphic core + optional React hooks.

Readme

Withallo SDK — JavaScript / TypeScript

Version npm License: MIT Tests

SDK TypeScript isomorphe pour l'API Withallo (Allo). Core utilisable partout (Node 18+, navigateurs modernes, Deno, React Native, edge runtimes) + un sous-module /react avec des hooks prêts à l'emploi.

Couvre les 5 domaines de l'API publique : Webhooks, Calls, Contacts, SMS, Phone Numbers, plus un WebhookReceiver pour traiter les événements entrants (CALL_RECEIVED, SMS_RECEIVED, CONTACT_CREATED, CONTACT_UPDATED).


Installation

# npm
npm install @qrcommunication/withallo-sdk

# pnpm
pnpm add @qrcommunication/withallo-sdk

# yarn
yarn add @qrcommunication/withallo-sdk

# bun
bun add @qrcommunication/withallo-sdk

Prérequis / Requirements : fetch global (Node 18+, navigateurs modernes, Deno, Bun). Passez un polyfill via options.fetch si votre runtime n'en a pas.

Générez votre clé API depuis web.withallo.com/settings/api.


Table des matières / Table of contents


Démarrage rapide / Quick start

Core TypeScript (Node, browser, Deno, React Native)

import { WithalloClient, WebhookTopic } from "@qrcommunication/withallo-sdk";

const client = new WithalloClient({
  apiKey: process.env.WITHALLO_API_KEY!,
});

// Créer un webhook / Create a webhook
await client.webhooks.create({
  alloNumber: "+1234567890",
  url: "https://example.com/webhooks/allo",
  topics: [WebhookTopic.CALL_RECEIVED, WebhookTopic.SMS_RECEIVED],
});

// Envoyer un SMS (US) / Send SMS (US)
await client.sms.send({
  from: "+1234567890",
  to: "+0987654321",
  message: "Hello from Withallo SDK",
});

// Envoyer un SMS France (Sender ID vérifié requis)
await client.sms.sendFrance({
  senderId: "MyCompany",
  to: "+33612345678",
  message: "Bonjour depuis le SDK Withallo",
});

// Rechercher des appels / Search calls
const result = await client.calls.search({
  alloNumber: "+1234567890",
  size: 50,
});

// Tester la connexion / Test connectivity
await client.testConnection(); // Promise<boolean>

Utilisation avec React / React usage

Le package expose un point d'entrée séparé @qrcommunication/withallo-sdk/react avec un provider et des hooks.

import { WithalloProvider, useSendSms, useWebhooks } from "@qrcommunication/withallo-sdk/react";

// 1. Envelopper votre app
function App() {
  return (
    <WithalloProvider options={{ apiKey: process.env.NEXT_PUBLIC_WITHALLO_KEY! }}>
      <Dashboard />
    </WithalloProvider>
  );
}

// 2. Utiliser les hooks
function SendSmsForm() {
  const { run, isPending, error, isSuccess } = useSendSms();

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        const form = new FormData(e.currentTarget);
        void run({
          from: form.get("from") as string,
          to: form.get("to") as string,
          message: form.get("message") as string,
        });
      }}
    >
      <input name="from" placeholder="+1..." />
      <input name="to" placeholder="+1..." />
      <textarea name="message" />
      <button disabled={isPending}>
        {isPending ? "Sending..." : "Send SMS"}
      </button>
      {error && <p>Error: {error.message}</p>}
      {isSuccess && <p>Sent ✓</p>}
    </form>
  );
}

function WebhooksList() {
  const { webhooks, isLoading, error, refresh } = useWebhooks();

  if (isLoading) return <p>Loading…</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {webhooks.map((w) => (
        <li key={w.id ?? w.url}>
          {w.url} → {w.topics.join(", ")}
        </li>
      ))}
      <button onClick={() => void refresh()}>Refresh</button>
    </ul>
  );
}

Sécurité : n'exposez jamais votre API key via NEXT_PUBLIC_* en production. Pour des apps publiques, passez par votre propre backend en proxy. Le hook sert principalement aux dashboards admin côté serveur ou à des intégrations Node.

Hooks disponibles

| Hook | Description | |------|-------------| | useWithallo() | Retourne le WithalloClient courant (lève si pas de provider) | | useSendSms() | Mutation client.sms.send() avec isPending / data / error | | useSendSmsFrance() | Mutation client.sms.sendFrance() | | useWebhooks() | Query auto-fetch de la liste des webhooks + refresh() | | useCreateWebhook() | Mutation client.webhooks.create() | | useDeleteWebhook() | Mutation client.webhooks.delete() | | useAsyncAction(fn) | Hook bas-niveau pour wrapper n'importe quelle action async |

Tous les hooks useXxxAction retournent { data, error, isPending, isSuccess, isError, run, reset }.


Référence des ressources / Resources reference

Architecture

WithalloClient
 ├─ webhooks       → WebhooksResource      (scope: WEBHOOKS_READ_WRITE)
 ├─ calls          → CallsResource         (scope: CONVERSATIONS_READ)
 ├─ contacts       → ContactsResource      (scopes: CONTACTS_READ / CONTACTS_READ_WRITE)
 ├─ sms            → SmsResource           (scope: SMS_SEND)
 ├─ phoneNumbers   → PhoneNumbersResource  (scope: CONVERSATIONS_READ)
 └─ webhookReceiver() → WebhookReceiver    (no network — parses incoming payloads)

Webhooks

await client.webhooks.list();
// => Webhook[]

await client.webhooks.create({
  alloNumber: "+1234567890",
  url: "https://example.com/hook",
  topics: [WebhookTopic.CALL_RECEIVED],
  enabled: true,
});
// => Webhook

await client.webhooks.delete("web-2NfDKEm9sF8xK3pQr1Zt");

Calls

const page = await client.calls.search({
  alloNumber: "+1234567890",   // requis
  contactNumber: "+0987654321", // optionnel
  page: 0,                     // défaut 0 (0-indexed)
  size: 10,                    // défaut 10, max 100
});
// => { results: Call[]; metadata: { pagination?: { total_pages, current_page } } }

Contacts

await client.contacts.get("cnt_abc123");
await client.contacts.search(0, 20);
await client.contacts.searchConversation("cnt_abc123", 0, 20);

await client.contacts.create({
  numbers: ["+15551234567"],
  name: "John",
  lastName: "Doe",
  emails: ["[email protected]"],
});

await client.contacts.update("cnt_abc123", {
  last_name: "Smith",
  job_title: "CTO",
  emails: ["[email protected]", "[email protected]"],
});

SMS

// US / International — envoi depuis un numéro Allo
await client.sms.send({
  from: "+1234567890",
  to: "+0987654321",
  message: "Hello",
});

// France — Sender ID vérifié requis (alphanumeric 3–11 chars OR short code)
await client.sms.sendFrance({
  senderId: "MyCompany",
  to: "+33612345678",
  message: "Bonjour",
});

Phone Numbers

const numbers = await client.phoneNumbers.list();
// => PhoneNumber[]

WebhookReceiver — réception des événements

Parse les payloads entrants et dispatch vers les handlers.

import { WebhookReceiver, WebhookTopic } from "@qrcommunication/withallo-sdk";

const receiver = new WebhookReceiver();

receiver
  .on(WebhookTopic.CALL_RECEIVED, async (event) => {
    const id = event.get<string>("id");
    const summary = event.get<string>("one_sentence_summary");
    // ... votre logique
  })
  .on(WebhookTopic.SMS_RECEIVED, async (event) => {
    const content = event.get<string>("content");
    const direction = event.get<"INBOUND" | "OUTBOUND">("direction");
  });

// Exemple: Next.js App Router Route Handler
export async function POST(request: Request): Promise<Response> {
  const rawBody = await request.text();
  try {
    await receiver.handle(rawBody);
    return new Response(null, { status: 200 });
  } catch {
    return new Response(null, { status: 400 });
  }
}

// Exemple: Express
app.post("/webhooks/allo", async (req, res) => {
  const rawBody = JSON.stringify(req.body);
  await receiver.handle(rawBody);
  res.status(200).end();
});

WebhookEvent expose :

  • .topicWebhookTopic
  • .data — payload typé (TData générique)
  • .raw — enveloppe JSON complète
  • .isCall() / isSms() / isContactCreated() / isContactUpdated()
  • .get<T>(path, fallback?) — accès dot-notation

Gestion d'erreurs / Error handling

WithalloError
├── ApiError (httpStatus, responseBody, getErrorCode(), getDetails())
│   ├── AuthenticationError  → 401 (API_KEY_INVALID)
│   ├── ForbiddenError       → 403 (requiredScopes(): string[])
│   ├── ValidationError      → 400/422 (errors(): Record<string, string>)
│   ├── NotFoundError        → 404
│   └── RateLimitError       → 429 (retryAfterSeconds: number | null)
└── InvalidWebhookPayloadError → payload webhook malformé (parse)
import { ForbiddenError, RateLimitError } from "@qrcommunication/withallo-sdk";

try {
  await client.webhooks.create({ /* ... */ });
} catch (err) {
  if (err instanceof ForbiddenError) {
    console.error("Scope manquant :", err.requiredScopes());
  } else if (err instanceof RateLimitError) {
    await new Promise((r) => setTimeout(r, (err.retryAfterSeconds ?? 1) * 1000));
    // retry...
  } else {
    throw err;
  }
}

Sécurité des webhooks / Webhook security

Important : au moment de la publication de ce SDK (avril 2026), la documentation publique de Withallo ne spécifie aucun en-tête de signature HMAC permettant de vérifier cryptographiquement l'origine des webhooks. Ce SDK valide la forme du payload mais ne peut pas authentifier l'expéditeur.

Hardening recommandé :

  • Servez votre endpoint webhook en HTTPS uniquement.
  • Utilisez une URL secrète et non devinable (token dans le path).
  • Whitelist des IPs egress Withallo au firewall si publiées.
  • Rejetez les payloads dont allo_number ne fait pas partie de vos propres numéros.
  • Répondez 200 OK en moins de 30 secondes.

Si Withallo publie un schéma de signature, une méthode WebhookReceiver.verifySignature(rawBody, signature, secret) sera ajoutée sans breaking change.


Documentation & exemples / Documentation & examples

| Fichier | Contenu | |---|---| | docs/openapi.yaml | Spécification OpenAPI 3.1 complète de l'API Withallo (endpoints, schémas de payloads, erreurs, sécurité) | | docs/ARCHITECTURE.md | Schémas mermaid : layers, request lifecycle, pipeline webhook, classes d'erreurs, extension points | | docs/examples/nextjs-webhook-route.ts | Route Handler Next.js App Router pour réception webhook | | docs/examples/express-server.ts | Serveur Express : endpoint SMS + endpoint webhook | | docs/examples/react-dashboard.tsx | Dashboard React complet avec les hooks du SDK | | docs/examples/error-handling.ts | Gestion d'erreurs exhaustive (rate-limit retry, scope, validation…) | | docs/examples/live-smoke-test.ts | Test end-to-end contre l'API réelle | | llms.txt | Index au format llmstxt.org pour les LLMs / agents IA | | CLAUDE.md / AGENTS.md | Instructions pour outils IA (Claude, Cursor, Copilot, Codex) | | skill/SKILL.md | Descripteur de skill Claude Code |

Développement / Development

pnpm install
pnpm build        # ESM + CJS + .d.ts via tsup
pnpm test         # vitest run
pnpm test:watch   # vitest --watch
pnpm typecheck    # tsc --noEmit
pnpm lint         # eslint

Release process

Ce package suit le Semantic Versioning. Les versions sont automatisées par release-please :

  1. Merger une PR sur main avec des commits au format Conventional Commits (feat:, fix:, etc.)
  2. release-please crée une PR "chore(release): X.Y.Z" avec le changelog auto-généré
  3. Merger cette PR tag la release et publie sur npm via le workflow release.yml

Licence / License

MIT © 2026 QrCommunication


Liens / Links