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

@empyre/ledger-sdk

v1.1.0

Published

Ledger — the financial operating system for autonomous companies: double-entry accounting, revenue ingestion, runway, an autonomous CFO, deterministic agent spending controls, and a `ledger` CLI.

Downloads

265

Readme

@empyre/ledger-sdk

The financial operating system for autonomous companies — Ledger by Empyre.

Send your product's revenue into real double-entry books, read cash and runway, ask a CFO what changed, and give your AI agents spending limits they cannot exceed.

Zero runtime dependencies. Node 18+, Deno, Bun, browsers, edge runtimes.

npm install @empyre/ledger-sdk

Two things to know first

1. Every amount is an integer number of minor units. 1999 is $19.99. There is no float anywhere in this SDK, and there should be none in your code either — 0.1 + 0.2 !== 0.3, and a financial system adds numbers millions of times. Use toMinor() where you touch human input and integers everywhere else.

2. A test key writes data that never reaches the books. ldg_test_… events are stored and visible, but produce no journal entry. That isn't a courtesy: developing an integration against production revenue is how a founder's P&L fills with fake sales during a sprint.


Quick start

import { Ledger, toMinor } from "@empyre/ledger-sdk";

const ledger = new Ledger({ apiKey: process.env.LEDGER_API_KEY });

// Record a sale. With an idempotency key, retrying is free — the same order
// can never be counted twice.
await ledger.commerce.record({
  kind: "sale",
  amountMinor: toMinor("49.00"),
  currency: "USD",
  customerRef: "cus_42",
  idempotencyKey: order.id,
});

Get a key at ledger.empyre.dev → Developers. It's shown once; Ledger stores only a hash and cannot show it again.


Money

The single most common way to get finance code wrong is to multiply by 100.

import { toMinor, fromMinor } from "@empyre/ledger-sdk";

toMinor("19.99")            // 1999
toMinor(19.99)              // 1999  — not 1998, which `19.99 * 100` gives you
toMinor("1000", "JPY")      // 1000  — yen has no minor unit
toMinor("1.234", "KWD")     // 1234  — dinar has three
toMinor("2.345")            // 235   — half-up, as invoicing and tax specify
toMinor("-2.345")           // -235  — away from zero, both directions

fromMinor(1999)             // "19.99"
fromMinor(1000, "JPY")      // "1000"

toMinor shifts the decimal by string manipulation, never by multiplication — multiplying is precisely the step that reintroduces the error it exists to prevent.


Reading the books

const dash = await ledger.dashboard();
dash.headline.cash_minor          // 4_827_00
dash.headline.runway_months       // 7.2, or null when profitable
dash.headline.confidence          // "low" | "medium" | "high"

Always read confidence. A projection built on six weeks of volatile history is a different object from one built on two years, and Ledger tells you which you have rather than presenting both identically.

const pnl = await ledger.profitAndLoss(entityId, { detailed: true });
pnl.gross_margin_bps              // 6200 = 62.0%, or null if there's no revenue

A margin of null means no revenue to divide by — not zero. A pre-revenue company doesn't have a 0% margin; charting it as one misrepresents it as catastrophically unprofitable.

Check the books actually close

const tb = await ledger.trialBalance(entityId);
if (!tb.balanced) {
  // Every figure derived from these books is suspect. Ledger returns the
  // verdict rather than leaving you to compare the totals yourself.
}

Runway and what to do about it

const runway = await ledger.runway();
runway.level          // "healthy" | "watch" | "at_risk" | "critical"
runway.cash_out_date  // "2026-11-04", or null when profitable

const change = await ledger.requiredChange({ targetRunwayMonths: 6 });
console.log(change.summary);
// "To maintain 6 months of runway without external financing, monthly
//  contribution profit must increase by approximately 31.2% within the next
//  60 days, or monthly cash expenses must decline by approximately $41,700."

options returns several independent levers — cut costs, grow contribution, raise price, raise capital — each with a certainty, because they are not interchangeable. method states the arithmetic so you can check it.

Requires the Pro plan.


The CFO

const answer = await ledger.askCfo("Which customers are losing me money?");
console.log(answer.answer);
console.log(answer.sources);  // [{ name: "get_customer_profitability", ok: true }]

Every figure in the answer is computed by Ledger from the books. The model decides which questions to ask and how to explain them — it never decides what the numbers are. sources lists the tools that produced them, which is what makes the answer checkable rather than merely fluent.


Agent spending

const result = await ledger.requestSpend({
  amountMinor: toMinor("240.00"),
  purpose: "Renew monitoring subscription",
  vendorName: "Datadog",
  requesterKind: "agent",
  agentRole: "cto",
});

result.decision   // "auto_approved" | "approved" | "needs_approval" | "denied"
result.reason     // why, in words
result.checks     // every limit evaluated, and where this request stood

The decision is arithmetic over your policies, not a model's judgement. An agent can request money; it can never decide it may have it. An agent with no policy is denied outright — that is the point of the feature, not an edge case.


Webhooks

import { verifyWebhook } from "@empyre/ledger-sdk";

app.post("/webhooks/ledger", async (req, res) => {
  const ok = await verifyWebhook({
    payload: req.rawBody,                        // the RAW bytes, not a re-serialised object
    header: req.headers["ledger-signature"],
    secret: process.env.LEDGER_WEBHOOK_SECRET,
  });
  if (!ok) return res.status(400).end();
  // …
});

Two things this handles that a naive check misses:

  • The timestamp is inside the signed material. Signing the body alone lets anyone who captured one delivery replay it forever. A delivery older than the tolerance (default 5 minutes) is refused even though its signature is genuine.
  • Comparison is constant-time. A fast === leaks, byte by byte, how much of a forged signature was correct.

Pass the raw body. Re-serialising a parsed object changes the bytes and the signature will not match.


Errors

import { LedgerError, LedgerPaywallError } from "@empyre/ledger-sdk";

try {
  await ledger.runway();
} catch (error) {
  if (error instanceof LedgerPaywallError) {
    // Not a failure — an unbought capability.
    console.log(`Available on ${error.requiredPlanName}`);
  } else if (error instanceof LedgerError && error.retryable) {
    // Timeout, 429 or 5xx. The SDK already retried twice with backoff.
  }
}

A 4xx is never retried — the request was wrong, and sending it again will be wrong the same way. Retryable failures use exponential backoff with jitter, so a fleet of agents recovering from an incident doesn't arrive in lockstep and knock the service over again.


Configuration

| Option | Default | | |---|---|---| | apiKey | LEDGER_API_KEY | required | | baseUrl | LEDGER_BASE_URL or https://api.empyre.dev | | | timeoutMs | 30000 | per request | | maxRetries | 2 | retryable failures only | | fetch | global fetch | override for tests |


Links

MIT © EmpyreDev, Inc.