Repository navigation
fix(events): align instant fee estimate schemas with API - #592
Merged
Merged
Conversation
vincent-pochet
approved these changes
Oct 8, 2026
groyoh
pushed a commit
that referenced
this pull request
Oct 8, 2026
## Context
OpenAPI guardian check of `wallets` (incremental run), compared with lago-api at `b02aa9a` and the SDK clients.
## Description
- `WalletUpdateInput`: added `ignore_paid_top_up_limits` to `wallet.recurring_transaction_rules[]`. The update endpoint already accepts it and the serializer already returns it, so the field was only missing from the update request schema. It is documented on the create request and on the response object. Also affects `PUT /customers/{external_customer_id}/wallets/{code}`, which shares the same schema.
<details>
<summary>Code references (lago-api at <code>b02aa9a</code>)</summary>
- Controller: [`WalletActions#update_params`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/controllers/concerns/wallet_actions.rb#L176-L201) permits `ignore_paid_top_up_limits` inside `recurring_transaction_rules`.
- Controller: [`WalletActions#input_params`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/controllers/concerns/wallet_actions.rb#L97-L133) permits the same key on create, which the spec already documents.
- Serializer: [`V1::Wallets::RecurringTransactionRuleSerializer`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/serializers/v1/wallets/recurring_transaction_rule_serializer.rb#L25) emits `ignore_paid_top_up_limits` on every rule.
</details>
- Typos: the `ignore_paid_top_up_limits` description ("allows rule to topped up wallet") is now a correct sentence, in `WalletCreateInput`, `WalletUpdateInput` and `WalletRecurringTransactionRule`.
### Needs human confirmation (not changed)
- The `connections` object is part of the public wallet contract but is absent from the whole spec. It is accepted on wallet create and update, both at the wallet level and inside each recurring transaction rule, and it is always returned by the wallet and the recurring-transaction-rule serializers. The same object also exists on subscriptions, so documenting it touches `wallets`, `subscriptions`, the docs site and the SDK clients at once. That makes it a coordinated change rather than a sweep fix, so nothing was added here. Evidence: [`WalletActions#input_params`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/controllers/concerns/wallet_actions.rb#L167-L172) and [`WalletActions#update_params`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/controllers/concerns/wallet_actions.rb#L235-L240) permit `connections` with the `payment`, `tax`, `accounting` and `crm` categories; [`V1::WalletSerializer#connections`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/serializers/v1/wallet_serializer.rb#L80-L86) and [`V1::Wallets::RecurringTransactionRuleSerializer#connections`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/serializers/v1/wallets/recurring_transaction_rule_serializer.rb#L55-L61) always add the key; [`Api::V1::SubscriptionsController`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/controllers/api/v1/subscriptions_controller.rb#L215-L220) and [`V1::SubscriptionSerializer#connections`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/serializers/v1/subscription_serializer.rb#L51-L57) do the same for subscriptions; the allowed values come from [`BillingObjectConnection::CATEGORIES`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/models/billing_object_connection.rb#L4-L9) and [`BillingObjectConnection::BEHAVIORS`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/models/billing_object_connection.rb#L11-L14), with the read side adding `inherit` in [`ConnectionResolvable#connection_routing`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/models/concerns/connection_resolvable.rb#L44-L64). On the spec side neither [`WalletObject`](https://github.com/getlago/lago-openapi/blob/e883ad3c21bbba0b7b47afd204698a9aa1d22105/src/schemas/WalletObject.yaml#L18-L188) nor [`WalletRecurringTransactionRule`](https://github.com/getlago/lago-openapi/blob/e883ad3c21bbba0b7b47afd204698a9aa1d22105/src/schemas/WalletRecurringTransactionRule.yaml#L21-L149) declares it. Should this be routed to a dedicated spec update plus an SDK update?
- `wallet.billing_entity_id` is permitted on wallet create next to `billing_entity_code` ([`WalletActions#input_params`](https://github.com/getlago/lago-api/blob/b02aa9a81f2c6c161ad978564121c18990e11ce1/app/controllers/concerns/wallet_actions.rb#L97-L115)) but only `billing_entity_code` is documented ([`WalletCreateInput`](https://github.com/getlago/lago-openapi/blob/e883ad3c21bbba0b7b47afd204698a9aa1d22105/src/schemas/WalletCreateInput.yaml#L57-L60)). Is the identifier form meant to be public, or is `billing_entity_code` the only supported way in?
<details>
<summary>Agent notes</summary>
Run: incremental, window since 2026-10-05T08:00:43Z. Queue: `wallets` (spec+api), `invoices` (api), `customers` (api), `subscriptions` (api), `alerts` (api), then the rotation. Queue reason for this resource: spec and API changes in the window. The PR limit for this run was 1, so the queue stopped here. Compatibility checker: PASS (advisory); the only findings are the two informational rows for the added optional request property, and nothing in this diff changes what callers must send or what consumers receive.
Checked and found already correct: the five wallet paths and the five wallet-transaction paths against `config/routes/shared_api.rb`; the index filters of `GET /wallets` and `GET /customers/{external_customer_id}/wallets` (`currency`, `external_customer_id`, `billing_entity_codes[]`, pagination); the `WalletObject`, `WalletTransactionObject` and `WalletRecurringTransactionRule` keys against their serializers; and the `priority` wording, which already matches the ordering in `Wallet.in_application_order`.
No SDK drift was found for this change: the Ruby, Python and Go clients already send `ignore_paid_top_up_limits` on recurring transaction rules, and the JavaScript client is regenerated from the published spec. The Rust client does not cover wallets, which is a known gap rather than drift.
**Human feedback addressed**
- No guardian PR was open at the start of this run, so there was nothing to rebase or reply to. The last four guardian PRs (#591, #592, #593, #594) were approved by a maintainer with write access and merged without change requests.
- Settled and not re-raised: the `custom_agg` ruling on #586, and the `/security_logs` gap already routed as a coordinated change in #587.
**Docs-guardian leads**
- getlago/lago-doc#693 (2026-10-05) reports no suspected spec issues.
- getlago/lago-doc#683's `/security_logs` lead was already confirmed and routed to a coordinated change in #587, so it was not re-raised.
**New lago-api surfaces seen in the window, not yet triaged**
- `Api::V1::X402::GateChecksController`, the `contracts` models, and `Api::V1::Customers::AttributedUsageController` are new in the window and have no spec presence. They were not verified this run and are queued for triage, not reported as gaps.
**Deferred to next run**
- `invoices`
- `customers`
- `subscriptions`
- `alerts`
- `fees`
- `payments`
- `organizations`
- `customer_usage`
- `billing_entities`
- `lifetime_usage`
- `credit_notes`
- `taxes`
- `payment_requests`
Rotation: next after `add_ons`.
Run started: 2026-10-08T15:58:12Z.
### Process feedback for the retro
- With one free slot, a five-resource batch guarantees four resources are reported unchecked. Capping the batch at the free-slot count would avoid building a queue that cannot be worked.
- The previous run left no run record: its last PR (#594) has no "Deferred to next run", "Rotation: next after" or "Run started" line, so this run had to rebuild the window from the workflow history and reset the rotation by hand. Worth making the record a hard step rather than a section of the last PR body.
- The same wrong sentence appeared in three schema files through copy-paste. A cross-file duplicate-description check would catch this class cheaply, alongside the three standard webhook checks.
- The "coordinated change" guardrail is written around endpoint families. `connections` is a cross-resource object on existing endpoints, which matches the intent but not the wording. Widening the wording would stop later runs from re-litigating it.
</details>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
OpenAPI guardian check of
events(full run), compared with lago-api at25b8b7band the SDK clients. A human must review and merge this PR; the agent never merges.Description
Breaking for generated clients
EventBatchEstimateInstantFeesInput.events[]:{ "event": { ... } }→{ "code", "external_subscription_id", "properties", "transaction_id" }. The API reads each item as a flat event and groups the items byexternal_subscription_id, so a body in the old shape fails with a subscription not found error. The event fields moved to the newEventEstimateInstantFeesInputObjectschema.EventEstimateInstantFeesInputnow references it, soPOST /events/estimate_instant_feesis unchanged.Code references (lago-api at
25b8b7b)EventsController#batch_estimate_instant_feesgroupsbatch_params[:events]byexternal_subscription_id.EventsController#batch_paramspermits flat event keys underevents, with noeventwrapper.Fees::EstimateInstant::BatchPayInAdvanceService#initializebuilds each event fromcode,external_subscription_id,propertiesandtransaction_id, and fails withnot_foundwhen no subscription matches.POST /api/v1/events/batch_estimate_instant_feessends flat event hashes.FeeEstimateObject.item.description:string→stringornull. It is the billable metric description, which is nullable. The field text also said it was the metric name. It now says it is the metric description. AffectsPOST /events/estimate_instant_feesandPOST /events/batch_estimate_instant_fees.Code references (lago-api at
25b8b7b)Fees::EstimateInstant::BaseService#estimate_charge_feessetsitem.descriptiontobillable_metric.description.db/structure.sqlbillable_metrics.descriptionischaracter varyingwithoutNOT NULL.Other changes
FeeEstimateObject: "It will always returns 0", "The value will alwayspending", "Values isBillableMetric".SDK drift (spec is right, needs an
sdk-clients-updaterun)POST /events/estimate_instant_feesorPOST /events/batch_estimate_instant_fees.GET /events(list with filters).EventEstimateFeesInputmissesprecise_total_amount_cents, whichFees::EstimatePayInAdvanceServicereads.Suspected lago-api bugs (spec unchanged)
amount_cents,precise_amount,precise_total_amountandprecise_unit_amountare built asBigDecimalinFees::EstimateInstant::BaseService#estimate_charge_fees, so they are rendered as strings ("40.0"), while the contract says integer and number. The request specs assertamount_cents == "40.0"(events_controller_spec.rb#L773).POST /events/estimate_feesreturns an integer for the same field.Needs human confirmation (not changed)
GET /events_enriched(beta, ClickHouse organizations only) is not in the spec. Should it be documented?external_contract_idis accepted onPOST /eventsandPOST /events/batchas an alias ofexternal_subscription_id, but is not documented. Is it a public field of the v1 API?external_subscription_idis required and non-null on events in the spec, but theEventmodel only validatestransaction_idandcode. Should the spec keep the stricter contract?Agent notes
Run: full, queue reason for this resource: full mode, group 0. Compatibility checker: BLOCK (advisory); every non-info finding is listed under Breaking (the three batch request findings are the shape fix, the two nullable findings are
item.description).The three questions under "Needs human confirmation" were first asked in #586 and have no answer yet.