credit layer api · now in beta

Stop rebuilding
credit logic.

Add, deduct and refund user credits in 3 API calls. Works with Stripe and Polar out of the box.

npm install tally-credits-sdk Full docs →
Add credits
Deduct credits
Check balance
Stripe webhook
// When a user pays — top up their credits instantly
const res = await fetch('https://api.usetally.dev/credits/add', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${TALLY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    user_id: 'user_abc123',
    amount: 500,
    description: 'Starter pack purchase',
  }),
});

// → { success: true, balance_after: 500 }
// When a user consumes — deduct atomically, no double-spend
const res = await fetch('https://api.usetally.dev/credits/deduct', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${TALLY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    user_id: 'user_abc123',
    amount: 50,
    description: 'AI generation',
  }),
});

// → { success: true, balance_after: 450 }
// → 402 if insufficient credits
// Check a user's current balance anytime
const res = await fetch(
  'https://api.usetally.dev/credits/balance?user_id=user_abc123',
  {
    headers: {
      'Authorization': `Bearer ${TALLY_API_KEY}`
    }
  }
);

const { balance } = await res.json();

// → { user_id: 'user_abc123', balance: 450 }
// Point your Stripe webhook here — Tally handles the rest
// No code needed on your end beyond the metadata

const paymentIntent = await stripe.paymentIntents.create({
  amount: 1000,
  currency: 'gbp',
  metadata: {
    tally_user_id: 'user_abc123', // ← who to credit
    tally_credits: '500', // ← how many credits
  },
});

// On payment success, Tally auto-credits the user.
// Idempotent — safe to retry.
POST /credits/add Top up a user's balance
POST /credits/deduct Atomic deduction with overflow protection
POST /credits/refund Reverse any transaction by ledger ID
GET /credits/balance Current balance for a user
GET /credits/history Paginated audit log per user
POST /webhooks/stripe Auto top-up on Stripe payment success

No race conditions

Atomic deductions using row-level locking. Two simultaneous requests can't double-spend the same credits.

🔁

Idempotent by default

Pass an idempotency key and Stripe webhooks will never credit a user twice, even on retries.

📒

Immutable ledger

Every event is written as an immutable row. Full audit trail for every add, deduct, and refund.

🔌

Provider agnostic

Works with Stripe and Polar, or any manual top-up flow. No lock-in.

👤

Auto user creation

Pass any user ID. Tally creates the user and initialises their balance automatically on first call.

📊

Dashboard included

See all your users, balances, and full transaction history in a clean developer dashboard.


Pay as you grow

Flat monthly pricing. No percentage of revenue. No surprises.

Free
$0/mo

Get started, no card needed

  • 3 apps
  • 100 users
  • Full ledger history
  • Community support
Starter
$9/mo

For indie developers

  • Unlimited apps
  • 1,000 users
  • Full ledger history
  • Email support
MOST POPULAR
Growth
$29/mo

For growing products

  • Unlimited apps
  • 10,000 users
  • Full ledger history
  • Priority support
Scale
$79/mo

For scaling teams

  • Unlimited apps
  • Unlimited users
  • Full ledger history
  • SLA + priority support

Flat monthly pricing. No percentage of revenue. No sales calls.