How to force a wallet ongoing balance refresh after a pricing fix on self-hosted Lago
Last updated: July 2, 2026
After correcting a pricing error, such as restoring a deleted billable metric value mapping, affected wallet ongoing balances do not recalculate automatically. Lago's 5-minute refresh clock only processes customers whose awaiting_wallet_refresh flag is set, and that flag is normally set by event ingestion, not by a pricing change. A pricing fix alone leaves the flag unset, so the clock job skips those customers until their next event arrives.
How to confirm the cause
The customer has an active wallet, the pricing fix is confirmed correct, but the wallet's ongoing balance still reflects the old, incorrect calculation. No new events have come in since the fix, so nothing has triggered the automatic refresh.
How to resolve the issue
Force the refresh from the Rails console using Customers::RefreshWalletsService. Always clear the customer's charge usage cache first: the refresh reads through a cache keyed on the charge's last-updated time, and a fix made at the billable metric level (its aggregation, expression, a filter, a rounding rule, or a backfilled event) doesn't touch that key, so the refresh would otherwise recompute from the same stale numbers.
To target the customers affected by one billable metric, which is preferable to refreshing every active wallet:
organization = Organization.find_by(id: 'ORGANIZATION_ID')
metric = organization.billable_metrics.find_by(code: 'METRIC_CODE')
plan_ids = organization.charges.where(billable_metric: metric).select(:plan_id)
customer_ids = organization.subscriptions.active.where(plan_id: plan_ids).select(:customer_id)
organization.customers.where(id: customer_ids).joins(:wallets).merge(Wallet.active).distinct.find_each do |customer|
customer.active_subscriptions.each { |sub| Subscriptions::ChargeCacheService.expire_for_subscription(sub) }
result = Customers::RefreshWalletsService.call(customer:)
puts "#{customer.external_id}: #{result.success? ? "ok" : result.error}"
end
For a fix that affects every active wallet in the organization, drop the metric, plan_ids, and customer_ids lines and iterate organization.customers.joins(:wallets).merge(Wallet.active).distinct.find_each directly. Either way, use .call and check result.success? rather than .call!: .call! raises on the first customer that fails, silently dropping the rest of the batch, which happens in practice for customers with tax-provider errors.
To refresh a single customer:
customer = organization.customers.find_by(external_id: 'CUSTOMER_EXTERNAL_ID')
customer.active_subscriptions.each { |sub| Subscriptions::ChargeCacheService.expire_for_subscription(sub) }
Customers::RefreshWalletsService.call!(customer:)
Look the customer up through organization.customers rather than Customer.find_by(external_id:) directly. External IDs are only unique within an organization, so an unscoped lookup on a multi-org instance can return the wrong customer.
After running, the wallet ongoing balances will reflect the corrected pricing.
Field reference
The automatic flag-driven refresh (the 5-minute clock) requires a premium license and a Redis or Memcached cache store. On a free self-hosted instance without those, wallet ongoing balances never refresh automatically, regardless of pricing fixes. The console workaround in this article has no license gate and works the same way on both tiers.
This article reflects guidance drawn from customer case resolutions. It is not officially supported documentation and may not apply to all situations.