Skip to main content

Sell a Credit Pool (and Stay Aligned With Your Own Bill)

You sell an AI agent. You want to price it the way customers like to buy: “5,000 tasks for $99 a month, then 3 cents per task after that.” That pricing shape is a credit pool. This guide walks the whole loop, start to finish:
  1. Create the pool pricing
  2. Connect it to your agent and a customer
  3. Record usage from your code
  4. Read the countdown from your code, so your product and your invoice always say the same thing
One rule to hold onto the whole way through, because everything else follows from it:
Nothing stops when the pool hits zero. MarginFront is the meter, never the breaker. A customer who uses up their pool keeps working, and the extra usage bills at your overage rate. If you want usage to stop at zero, that decision belongs in your code, and the last section shows you exactly how.

Before you start

You need:
  • A secret API key (mf_sk_...) from Build > API keys in the dashboard
  • The SDK installed if you want the SDK examples: npm install @marginfront/sdk
A note on order: usage tracking alone needs no setup (agents, signals, and customers auto-create on the first event). Credit pools are different. The pool prices a signal, so the agent and the signal have to exist before you can create the pool. That’s why this guide creates them explicitly in steps 1 and 2.

Step 1: Create the agent and the signal

The agent is your product. The signal is the unit you sell (the word your customer would use: tasks, reports, messages).
Save the id from each response. The next steps need them. (You can also create both in the dashboard, or ask the MCP server to do it.)

Step 2: Create the plan and the credit pool

A plan is the container (“Pro Plan”). The credit pool is one pricing rule inside it. You describe the pool with exactly three numbers: First the plan:
Then the pool. One call, three numbers. Pick whichever surface you’re using: SDK:
curl:
(The body is an array because this endpoint creates strategies in bulk. One strategy still goes in an array.) MCP: ask your AI assistant to run create_pricing_strategy with pricingModel: "credit_pool" and the same creditPool block. In plain English: “Add a credit pool to the Pro Plan: 5,000 tasks for $99 a month, then 3 cents each.” The server compiles those three numbers into the stored two-tier shape for you. Two rules the server enforces, with plain error messages when you hit them:
  • One pool per plan. A second active credit pool on the same plan is rejected. Put a second pool on its own plan.
  • No minimum commitment on a pool. The pool fee already is the minimum the customer pays every cycle.
Or with the SDK: await mf.pricingPlans.linkAgent("PLAN_ID", "AGENT_ID").

Step 4: Create the customer

externalId is the ID from your own system. It’s how your usage events find this customer later.

Step 5: Create the subscription

The subscription ties the customer to the plan and starts the clock. The billing period comes from startDate, and the pool’s countdown lives inside that period.
The response includes currentBillingPeriodStart and currentBillingPeriodEnd. That window is the pool’s period: 5,000 fresh units each cycle, unused units don’t roll over, and the flat $99 bills every cycle either way. The SDK doesn’t create subscriptions today (it reads them). Use curl, the dashboard, or the MCP create_subscription tool for this step.

Step 6: Record usage

This part is the normal MarginFront integration. Fire one event per business outcome, with quantity counting the signal’s units:
Each event’s quantity draws down the pool as it lands. Record 250 tasks and the pool reads 4,750 left. See Tracking Events for the full event guide.

Step 7: Read the balance from your own code

This is the step that keeps your product and your bill telling the same story. The dashboard, the API, the SDK, and the MCP server all read one shared balance path, so the number your code sees is the number the invoice math uses. SDK:
curl:
MCP: ask “how many units does Acme have left?” and the assistant runs get_credit_balances. Reads are open to any valid key for your organization. The full field list lives in the Credit Balances API reference.

The two numbers (read this once, save yourself a support ticket)

Every balance row carries two numbers that can disagree, and both are right:
  • remainingUnits is the countdown, and it counts your manual top-ups.
  • overageInProgressUnits is the invoice’s own overage math, which never sees top-ups, so the bill can never disagree with what this field told you.
Example: Acme burns through all 5,000 units, then uses 300 more, and you top them up with 1,500 goodwill units. The balance now honestly reads remainingUnits: 1200 AND overageInProgressUnits: 300. The customer has units to use, and 300 units are billing as overage this period. Both true at once. When you show a customer what they’ll owe, use overageInProgressUnits. When you show them what’s left, use remainingUnits.

What zero (and negative) means

remainingUnits goes negative once the pool is overdrawn. Nothing else changes:
  • The customer’s requests keep working. MarginFront never blocks, throttles, or drops anything.
  • Every unit past the pool bills at your overageRate when the period’s invoice is generated.
  • The pool refills at the next billing period.
MarginFront is the meter, never the breaker. If your product promises “strict prepaid, no surprises,” the last section of this guide is yours.

The alert ladder: 50%, 80%, 95%, empty

As a pool drains, MarginFront emails your team at four stages: 50% used, 80% used, 95% used, and empty (empty means remaining units at or below zero). How the emails behave:
  • Who gets them: your organization’s billing email if one is set (Settings > Billing), otherwise every member with the owner or finance role.
  • One email per stage per period. A pool that crosses 80% today won’t re-email 80% tomorrow. Every stage re-arms when the next billing period starts.
  • Only the highest new stage emails. A pool that jumps from fresh to 97% in one burst gets one 95% email, not three.
  • Topping up un-crosses stages, but never rewinds an email. Add units and the used percent drops. A stage that already emailed this period stays quiet even if the pool drains back through it. Every stage re-arms when the next billing period starts.
  • Pause and resume anytime. Pausing silences the emails for one subscription and nothing else: the countdown keeps counting and the balance stays visible everywhere. Pause from the dashboard or POST /v1/subscriptions/:id/credit-alerts-pause with {"paused": true}. Send {"paused": false} to turn them back on; stages the pool crossed while paused can then fire on the next evaluation.
The emails link to the dashboard’s Needs Attention page, which brings us to:

When a pool runs dry: the Needs Attention flow

The dashboard’s Needs Attention page (the link inside every pool email) lists every pool that’s empty or under 20% remaining, with an honest banner: “Working as designed: nothing is blocked, the overage keeps billing.” Paused-alert pools still appear there, labeled Paused, because pausing emails doesn’t hide the situation. Each row has a Top up or pause button with the two resolutions a human actually reaches for:
  • Top up: add units to the pool right now.
  • Pause or resume alerts: quiet the emails for a customer you’ve already talked to.
The same section, with the same actions, lives in Money > Plans under Credit Pools. Both roads resolve identically.

Topping up

A top-up adds units to a customer’s pool mid-period. From the dashboard it’s a button; from the API:
Three things worth knowing:
  • idempotencyKey is required, and it’s your safety net. Send the same key twice (a retry, a double-click) and the SAME grant comes back instead of a second one, so a customer can never be double-credited. Reuse a key with different units, or on a different subscription, and you get a 409 telling you so. Any string unique to the top-up works; a UUID is fine.
  • Top-ups never expire. The customer paid for those units. Draw order burns the current period’s grant first, so a top-up remainder survives into the next period.
  • A top-up doesn’t shrink overage already on the books. It raises remainingUnits going forward. The overage the invoice already counted stays counted (that’s the two-numbers rule from above).
Top-ups and alert pausing need a key or dashboard user with the owner, admin, or finance role. Anything less gets a 403 (an API-key caller sees the allowed roles named in the message; a dashboard session without the role gets a generic forbidden).

If your product sells strict prepaid: the self-limiting pattern

Maybe your promise to customers is “when the credits run out, the agent stops.” MarginFront won’t stop it for you, on purpose: a billing hiccup should never take your product down, and plenty of businesses want overage revenue. Your code is the enforcement point. The balance read exists exactly so you can make that call:
Being honest about this pattern’s edges:
  • The balance read is a point-in-time snapshot with a short cache (up to 30 seconds). Requests already in flight when the pool empties will land and bill as overage. For most products a few units of slack is nothing; if it matters, check less often and keep your own local count between checks.
  • Checking before every single request adds a network read to your hot path. A common middle ground: check every Nth request, or only once remainingUnits drops under 10% of poolSizeUnits.
  • If you block at zero, tell the customer how to get going again (a top-up on your end, or their next billing period).

Next steps