# `@agy/billing-worker` Rolls the **usage ledger** into draft invoices — one invoice per organization per billing period. ```bash pnpm --filter @agy/billing-worker run # one pass, then exit (cron shape) pnpm --filter @agy/billing-worker dev # long-running loop ``` ## What it does For every organization, for the requested period: 1. Skip the organization if the period already has an invoice (**idempotent** — a cron job that fires twice must not bill twice). 2. Otherwise sum the usage ledger for the period. 3. Skip if the total is zero — no `$0` drafts littering the table. 4. Otherwise create a `draft` invoice carrying the ledger's total. ## What it deliberately does **not** do - **It never invents an amount.** The ledger is the source of truth; the worker only snapshots it. See `docs/architecture/overview.md` §8. - **It never moves money.** Charging a card is `BillingService.settle_payment`, behind an authenticated API call — not a background job. ## Implementation `apps/api/app/workers/billing.py`. The service wiring comes from `apps/api/app/application/container.py`, so this worker and the API compose services identically. Tests: `apps/api/tests/test_workers.py`. ## Extracting this into its own process The worker already opens its own database sessions and holds no request state, so extraction is a deployment change, not a code change: run `python -m app.workers billing --loop` as its own process with the same `DATABASE_URL`, and point the cron/loop interval at your billing cycle.