Quickstart
Measurement first. This gets you real cost and margin per customer without changing how a single request behaves. Nothing here can break production: the call you add cannot throw and cannot block.
- 01
Install
npm install marginfuseZero dependencies in every language. Server side only: the SDK carries a secret API key, so there is no browser or mobile build. See SDKs for what each one requires and where its source lives.
- 02
Get a project API key
Create a project in MarginFuse and copy its key from Settings, then put it in your environment as
MARGINFUSE_KEY. Keys start withmf_live_ormf_test_, and only the hash is stored, so a lost key is rotated rather than recovered. - 03
Report each AI call
Add this after the provider call returns. It sends metadata only: token counts, the model, your customer id. There is no field for the prompt.
import { MarginFuse } from "marginfuse"; const mf = new MarginFuse({ apiKey: process.env.MARGINFUSE_KEY! }); // After the AI call returns. Metadata only: there is no field for the prompt. mf.track({ customerId: "cus_8x2m91", // your own customer id, or the Stripe customer id feature: "ai_chat", provider: "openai", model: "gpt-4.1", usage: { inputTokens: 1204, outputTokens: 388 }, });Pass
costUsdinstead of, or alongside, token counts when your provider reports the real charge. MarginFuse prefers a figure you observed over one it estimated.Naming the model
Send
providerandmodelexactly as the provider names them. Nothing needs rewriting to suit MarginFuse, and rewriting is how a model stops being priced.- Anthropic.
provider: "anthropic"with the id its API takes, which is always dated:claude-sonnet-4-5-20250929,claude-haiku-4-5-20251001. A dated id is priced at that model’s published rate, the one in force on the day the call ran rather than today’s. - OpenAI.
provider: "openai"withgpt-4.1,gpt-4.1-mini,o3, or a pinned snapshot likegpt-4.1-2025-04-14, which prices the same way. - Through a gateway. Send the gateway’s own id and its reported cost. See Gateways.
A model MarginFuse has no published rate for is not guessed at. The event is still recorded and still attributed to your customer, its cost reads zero, and every figure built on it is tagged
PARTIALnaming the model, so a gap in the catalogue looks like a gap rather than a cheap customer. - Anthropic.
- 04
Add revenue when you want margin
Cost alone is not margin. Connect Stripe for web billing or RevenueCat for App Store and Google Play proceeds in Settings. You can also declare plan prices without a billing connection; that revenue remains labeled unverified, not collected.
This step can wait. Until a revenue source is connected, every customer id you report becomes a customer on its own, so cost per customer and per-day or calendar-month cost policies work from step 3 alone. For margin without a billing connection, declare what your plans charge in Settings and tell MarginFuse which plan each customer is on - by hand, or with
mf.identify({ customerId, plan })from your own code. Ids that turn out to be Stripe customer ids are joined in place when you connect. See Customers and plans. - 05
Watch the first numbers arrive
Events appear within seconds, and so does cost per customer. Margin per customer needs both sides, so it fills in when customer revenue and usage are available. Every figure is labeled with how it was arrived at, so you always know whether you are looking at something observed or something estimated.
Configuration
Everything is optional except the key.
import { MarginFuse } from "marginfuse";
const mf = new MarginFuse({
apiKey: process.env.MARGINFUSE_KEY!,
// Point at your own deployment in development.
baseUrl: "https://api.marginfuse.com",
// How long decide() waits before giving up and allowing the request.
timeoutMs: 1500,
// The SDK never throws into your code. Transport failures arrive here.
onError: (error, context) => {
logger.warn({ err: error, context }, "marginfuse degraded");
},
});timeoutMsis the budget for a decision, not fortrack(). When it expires the request is allowed through.onErroris the only place transport failures surface. The SDK swallows them so they cannot become your outage; without this hook they are silent.
Then what
Measuring is the whole free tier and it is worth stopping there for a week. Once you can see which customers lose money, protection is the same SDK with the call moved to before the request.
NextProtectionDecide before the call runs, and act on the verdict.