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

@builtbyecho/reverbin

v0.1.3

Published

Node.js ESM SDK for Reverbin agent inboxes, threads, messages, approvals, webhooks, credentials, billing, and account lifecycle APIs.

Readme

@builtbyecho/reverbin

Zero-dependency Node.js ESM SDK for the Reverbin API: agent inboxes, threads, messages, approvals, signed webhooks, API keys, billing, and account lifecycle operations.

Install

Install the public Node.js package from npm:

npm install @builtbyecho/reverbin

The SDK requires Node.js 20 or newer, uses Node's built-in fetch, and supports ESM imports only. Browser runtimes are not supported; never put a Reverbin API key in client-side code.

Authenticated client

import { ReverbinClient } from '@builtbyecho/reverbin';

const apiKey = process.env.REVERBIN_API_KEY;
const inboxId = process.env.REVERBIN_INBOX_ID;
if (!apiKey || !inboxId) throw new Error('REVERBIN_API_KEY and REVERBIN_INBOX_ID are required');

const reverbin = new ReverbinClient({
  baseUrl: process.env.REVERBIN_BASE_URL ?? 'https://api.reverbin.com',
  apiKey,
  timeoutMs: 30_000,
});

const threads = await reverbin.inboxes.threads(inboxId);
const latest = threads.data[0];
if (latest) {
  await reverbin.threads.reply(latest.id, {
    text: 'Received — I am handling this from the agent workflow.',
  });
}

Signup already creates the first inbox. Use the returned REVERBIN_INBOX_ID; call reverbin.inboxes.create only when the workflow needs another inbox and the account has quota.

Self-serve signup

The public signup method does not require an API key. It does require a stable caller-owned idempotency key:

import { randomUUID } from 'node:crypto';
import { ReverbinClient } from '@builtbyecho/reverbin';

const reverbin = new ReverbinClient();
const result = await reverbin.signups.create({
  idempotency_key: randomUUID(),
  requester_email: '[email protected]',
  agent_name: 'Support Agent',
  agent_use_case: 'Handle customer support replies and escalate unusual requests.',
  preferred_inbox_name: 'support-agent',
});

if (result.credentials_returned) {
  // Store result.api_key.token now; it is returned once.
  console.log(result.inbox.email_address);
} else {
  // Safe replay: identifiers and a recovery message, but no credentials.
  console.log(result.message);
}

Keep the original idempotency key with the request outcome. Reuse it only with the identical request body after a lost response.

Core method groups

| Group | Methods | | --- | --- | | signups | create | | inboxes | create, list, get, threads | | messages | compose, list | | threads | get, messages, reply, forward | | approvals | list, approve, reject | | webhooks | create, list, deliveries, rotateSecret, revoke | | apiKeys | create, list, rotate, revoke | | billing | plans, checkout, portal | | account | export, requestDeletion, cancelDeletion | | auditLogs | list | | signupRequests | create, list, update for legacy operator-assisted workflows |

See https://reverbin.com/docs/api for request and response contracts.

Pagination

Collection methods return { data, next_cursor, has_more }. Pass next_cursor back unchanged as cursor to the same method and parent resource:

const first = await reverbin.inboxes.list({ limit: 25 });
const second = first.has_more
  ? await reverbin.inboxes.list({ limit: 25, cursor: first.next_cursor })
  : null;

Cursors are opaque and tenant-, collection-, and parent-bound.

Timeouts, aborts, and errors

Requests time out after 30 seconds by default. Configure timeoutMs on the client or per request, and pass a caller AbortSignal in method request options. The client does not retry requests automatically.

import {
  ReverbinApiError,
  ReverbinClient,
  ReverbinResponseError,
  ReverbinTimeoutError,
} from '@builtbyecho/reverbin';

const controller = new AbortController();
try {
  await reverbin.inboxes.list(undefined, {
    signal: controller.signal,
    timeoutMs: 5_000,
  });
} catch (error) {
  if (error instanceof ReverbinApiError) {
    console.error(error.status, error.code, error.requestId, error.retryAfterSeconds, error.quota);
  } else if (error instanceof ReverbinTimeoutError) {
    console.error('Timed out after', error.timeoutMs);
  } else if (error instanceof ReverbinResponseError) {
    console.error('Malformed API response', error.status, error.requestId);
  }
}

API error details are recursively credential-redacted. Even so, do not log one-time signup, API-key, or webhook-secret responses.

Webhook signatures

Register only a real reachable HTTPS endpoint, then store the returned secret immediately:

const webhookUrl = process.env.REVERBIN_WEBHOOK_URL;
if (!webhookUrl) throw new Error('REVERBIN_WEBHOOK_URL is required');

const webhook = await reverbin.webhooks.create({
  url: webhookUrl,
  events: ['email.received', 'email.sent', 'email.failed'],
});
// Store webhook.secret now; it is returned once.

Verify the signature against the exact raw request bytes before parsing or acting on a webhook:

import { verifyWebhookSignature } from '@builtbyecho/reverbin/webhook-signatures';

const valid = verifyWebhookSignature(rawBody, signatureHeader, webhookSecret);

During credential-rotation grace, verify x-echo-email-signature-previous separately with the previous secret. The stable x-echo-email-* header prefix is retained for wire compatibility.

After signature verification succeeds, atomically claim the unique x-echo-email-delivery value before performing side effects, using a durable processing lease and completed state. Commit transactional business changes and the completed state together. For external side effects, enqueue a transactional outbox operation keyed by the delivery ID. A completed claim returns success without re-executing; an expired processing lease may be retried. This deduplicates concurrent and repeated deliveries without losing retryability.

Retry and idempotency boundaries

The SDK never retries automatically.

  • Signup, Stripe Checkout/Portal, API-key rotation, and webhook-secret rotation require explicit idempotency_key inputs. Reuse the same key only with the identical body after a lost response.
  • Do not blindly retry compose, reply, forward, approval decisions, inbox creation, webhook creation/revocation, API-key creation/revocation, or account mutations after an ambiguous response. Inspect resource, thread, audit, or delivery state first.
  • Signup replay prevents duplicate provisioning but intentionally omits one-time credentials.

Launch limitations

Outbound attachments are not supported in the launch SDK. Compose, reply, and forward accept text plus optional HTML. Inbound attachments remain available in the authenticated human mail console.