# Billing & top-ups

> The three plans (Starter, Pro, Studio), annual-only billing, checkout and portal, manual top-ups from $50, automatic top-ups, and the credit conversion formula.

Figgshield has three paid plans, billed **annually only**. Prices are displayed per month; the charge is always twelve times the monthly sticker, once a year. Credits are granted monthly regardless. There is no free tier.

## Plans

`GET /api/billing/plans/` — public, no authentication.

```bash
curl https://api.figgshield.ai/api/billing/plans/
```

```json
[
  {
    "slug": "pro",
    "name": "Pro",
    "price_monthly_cents": 2900,
    "price_yearly_cents": 34800,
    "monthly_credits": 1100,
    "parallel_limit": 5,
    "features": ["1,100 credits a month", "5 generations in parallel"],
    "highlight": true
  }
]
```

The three plans:

| Plan | Sticker | Charged annually | Credits / month | Parallel jobs | ≈ images or clips |
| --- | --- | --- | --- | --- | --- |
| Starter | $12/month | $144/year | 400 | 3 | 50 images / 11 clips |
| Pro (highlighted) | $29/month | $348/year | 1,100 | 5 | 137 images / 31 clips |
| Studio | $75/month | $900/year | 3,000 | 8 | 375 images / 85 clips |

`price_monthly_cents` is the display sticker; `price_yearly_cents = 12 × sticker` is the only amount ever charged. `highlight` marks the plan to present as the default (Pro). Capacity figures assume 8-credit Nano Banana 2 images and 35-credit Kling clips.

## Billing status

`GET /api/billing/status/`

```bash
curl https://api.figgshield.ai/api/billing/status/ \
  -H "Authorization: Bearer $FIGGSHIELD_API_KEY"
```

```json
{
  "plan": { "slug": "pro", "name": "Pro", "parallel_limit": 5 },
  "status": "active",
  "interval": "yearly",
  "current_period_end": "2027-07-01T00:00:00Z",
  "cancel_at_period_end": false,
  "processor": "stripe",
  "has_payment_method": true
}
```

`interval` is always `"yearly"`. `plan` is `null` while the account has no active subscription — in that state generation creation returns `402 subscription_required`. `has_payment_method` tells you whether automatic top-ups can be enabled.

## Checkout

`POST /api/billing/checkout/` with `{"plan": "<slug>"}` — returns a Stripe Checkout URL to complete in a browser. Billing is annual; an `interval` field, if sent, is ignored.

```bash
curl https://api.figgshield.ai/api/billing/checkout/ \
  -X POST \
  -H "Authorization: Bearer $FIGGSHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "plan": "pro" }'
```

```json
{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_..." }
```

`POST /api/billing/portal/` returns a `portal_url` to the Stripe billing portal for managing the payment method, invoices and cancellation.

## Top-ups

Top-ups buy extra credits between grants, at your plan's rate. The conversion formula:

```text
credits = floor(amount_cents × plan.monthly_credits ÷ plan.price_monthly_cents)
```

For example, a $50 top-up on Pro: `floor(5000 × 1100 ÷ 2900)` = **1,896 credits**. The minimum top-up is **5000 cents ($50)** — below that the request fails with `400 validation_error`. Top-ups require an active subscription.

### Manual top-up

`POST /api/billing/topup/` with `{"amount_cents": <int ≥ 5000>}` — returns a Stripe Checkout URL (one-off payment). The credits land as a ledger entry of type `topup` once payment completes.

```bash
curl https://api.figgshield.ai/api/billing/topup/ \
  -X POST \
  -H "Authorization: Bearer $FIGGSHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount_cents": 5000 }'
```

### Automatic top-ups

`GET /api/billing/topup/settings/` / `PATCH` the same URL:

```bash
curl https://api.figgshield.ai/api/billing/topup/settings/ \
  -X PATCH \
  -H "Authorization: Bearer $FIGGSHIELD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "auto_topup_enabled": true,
    "auto_topup_amount_cents": 5000,
    "auto_topup_threshold_credits": 50
  }'
```

| Field | Meaning |
| --- | --- |
| `auto_topup_enabled` | Enabling requires an active subscription and a payment method on file (`has_payment_method`). |
| `auto_topup_amount_cents` | Amount charged per automatic top-up; minimum 5000 ($50). |
| `auto_topup_threshold_credits` | Balance level that triggers a top-up; must be ≥ 0. |
| `preview_credits` | Read-only: how many credits the configured amount buys at your current plan rate. |

When enabled, an automatic top-up fires in two situations, server-side and atomically:

1. **On generation create**, if your balance cannot cover the job's cost: the saved payment method is charged off-session, credits are granted, and the generation proceeds. If the payment fails, the request returns `402 insufficient_credits`.
2. **After any burn** that leaves the balance below `auto_topup_threshold_credits`.

At most **one automatic top-up per hour** fires, as a guard against loops.

## Webhooks

`POST /api/billing/webhooks/stripe/` (signature-verified) receives payment events from Stripe and fast-acknowledges with `200`. It is called by Stripe, not by API clients — you never need to call it. Affiliate commission payouts run through Stripe Connect off the same `invoice.paid` events.
