Avo for finance teams
Run billing operations from inside your Rails app
Credits, corrections, and overdue invoices, worked from the same database the product runs on. Field-level permissions and an audit trail, instead of exports and engineering tickets.
250+ paying companies · 2.7M downloads of the avo gem
| policy allows | support | billing | controller |
|---|---|---|---|
| See invoice status | |||
| See amounts and balances | |||
| Request a credit note | |||
| Approve a credit note |
The person who raises a credit is not the person who approves it, and the policy that decides is Ruby in your repo, reviewed like any other code.
01 · the stakes
Where billing ops loses time, and trust
The numbers finance answers for live in the production database, but the tools to see and correct them usually don't. You don't have to be a fintech for this to hurt: any product that bills customers has a billing back office, and it usually runs on exports, tickets, and adjustments nobody can trace later.
-
01
Month-end runs on exports
Reconciliation happens in spreadsheets built from CSVs someone exported on request. The numbers are stale by the time they're read, and every question that comes up means asking an engineer for another export.
-
02
Every adjustment routes through engineering
Issuing a credit note, marking an invoice paid, fixing a billing record: finance knows the policy, engineering has the access. Each adjustment becomes a ticket, and the queue is longest exactly when the close deadline is nearest.
-
03
The truth is split between your app and the processor
The payment processor's dashboard knows the charges; your database knows the customers, plans, and entitlements. Neither shows the whole picture, so answering "what happened with this account" means joining two systems by hand.
-
04
Adjustments leave no trail
When a balance was corrected in a console session, there is no record of who, when, or why. That is an uncomfortable answer to give an auditor, and one you cannot reproduce the next time someone asks.
02 · the Avo version
Your models are already the hard part. Avo is the rest.
Avo mounts inside your Rails app, so invoices, subscriptions, and payments stay in your database and finance works on them directly. The adjustments become confirmed, logged actions, the close-week queues become saved views, and the dashboards read from the source of truth instead of an export. And the tool with read access to every amount and payment identifier stops sitting behind auth someone wrote in a hurry: the authorization and hardening around that access are our job to keep right.
Adjustments the moment the decision is made
Issue a credit note, mark an invoice paid, write off a balance: each is an Avo action with a confirmation step and a required reason, running one code path you wrote. The correction lands when finance decides it, not when the ticket clears.
The requester is never the approver
Avo reads your Pundit policies down to the field and the action, so raising a credit and approving one can be different roles, and amounts render only for the people cleared to see them. Who can change what is Ruby in your repo, not a rule someone remembers.
Queues for the invoices that need chasing
Overdue invoices, failed payments, credits pending approval: scopes and dynamic filters turn each into a saved view finance works down daily, oldest first, and hardest during close week, when the queue matters most.
Dashboards on live numbers
Revenue, outstanding balances, and failed-payment counts on dashboards built from your own queries against the production database. Invoice aging is your own bucketed query rendered as a card, so month-end starts from live figures instead of last Tuesday's export.
The auditor's question is a query, not a project
Pair Avo with model-level auditing and every adjustment records the acting user and the reason, in tables you can query and hand over when someone asks. On Enterprise, Avo's audit logging also records who viewed a record.
An Invoice resource with a credit note action that requires a reason:
class Avo::Resources::Invoice < Avo::BaseResource
self.includes = [:customer]
def fields
field :number, as: :text, readonly: true
field :amount, as: :number, visible: -> { current_user.finance? }
field :status, as: :select, enum: ::Invoice.statuses
field :due_on, as: :date
field :customer, as: :belongs_to
end
def actions
action Avo::Actions::IssueCreditNote
end
end
class Avo::Actions::IssueCreditNote < Avo::BaseAction
self.name = "Issue credit note"
self.confirm_button_label = "Issue credit"
def fields
field :credit_amount, as: :number, required: true
field :reason, as: :textarea, required: true
end
def handle(query:, fields:, current_user:, **)
query.each { |invoice| invoice.credit!(amount: fields[:credit_amount], reason: fields[:reason], by: current_user) }
succeed "Credited #{query.count} invoice(s)"
end
end
03 · the timeline
From first resource to the team running on it
Generate resources for Invoice, Subscription, and Payment and finance can browse live billing data with each customer's invoices and payments linked from their record.
Hook up the authentication you already use, write policies so finance edits while support reads, and ship the first action, a credit note with a required reason, so the first adjustment leaves the console.
Overdue and failed-payment queues are saved views worked daily, the revenue dashboard reads from production, and month-end starts from live numbers with a trail behind every adjustment.
case study · the build-vs-buy math
“It's almost like having another developer on the team just focused on that side of things... it's paid for itself many times over”
04 · start today
Get finance off the engineering queue this week
Avo is per app with unlimited users, so the whole team works from it without the bill scaling with headcount. When you hit an edge Avo doesn't cover, you drop to plain Rails instead of filing a feature request.
Things finance teams run from Avo
Avo for your team
Avo by industry
Frequently asked questions
- Can finance work billing from a Rails app instead of exports?
- Yes. Invoices, subscriptions, and payments are ordinary Rails models, and Avo turns them into a billing workspace on top of your existing tables: saved views for the queues, actions for the adjustments, dashboards for the numbers. Because it runs inside your app, the data is always current.
- Can we issue credits and corrections without a developer?
- Yes. Each adjustment becomes an Avo action: a button with a confirmation step and a required reason, calling the same model method your product uses. Finance acts directly, the logic stays in one reviewed code path, and nobody opens a production console.
- How do we control who can change billing records?
- Your Pundit policies enforce it, down to the field and the action, so finance can adjust amounts while support only reads statuses, and requesting a credit can be a different permission from approving one. The rules are Ruby in your repo, reviewable in a PR and versioned in git.
- Can we get revenue dashboards without a separate BI tool?
- For operational billing numbers, yes. Avo's dashboards are built from your own queries against the production database, so revenue, invoice aging, and failed-payment counts are live. For deep analytical modeling a BI tool still earns its place. For the numbers finance checks daily, the dashboard next to the data is enough.
- How do we answer an auditor's "who changed this"?
- Pair Avo with model-level auditing and every change is recorded with the acting user, in your own database, where you can query and export it. Actions requiring a reason mean the justification is captured at the moment of the adjustment, not reconstructed later. Enterprise audit logging adds who viewed a record.
- Does billing data leave our servers?
- No. Avo is a Rails engine mounted in your application, so every record stays in your database and every request is served by your app. There is no hosted platform between finance and the numbers, so the security review stays short and the audit scope stays yours.