Skip to content

fix(events): align instant fee estimate schemas with API - #592

Merged
groyoh merged 1 commit into
mainfrom
openapi-guardian/2026-10-07-events
Oct 8, 2026
Merged

groyoh merged 1 commit into
mainfrom
openapi-guardian/2026-10-07-events

Conversation

@groyoh

@groyoh groyoh commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Context

OpenAPI guardian check of events (full run), compared with lago-api at 25b8b7b and 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 by external_subscription_id, so a body in the old shape fails with a subscription not found error. The event fields moved to the new EventEstimateInstantFeesInputObject schema. EventEstimateInstantFeesInput now references it, so POST /events/estimate_instant_fees is unchanged.

    Code references (lago-api at 25b8b7b)
  • FeeEstimateObject.item.description: string → string or null. 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. Affects POST /events/estimate_instant_fees and POST /events/batch_estimate_instant_fees.

    Code references (lago-api at 25b8b7b)

Other changes

  • Typos in FeeEstimateObject: "It will always returns 0", "The value will always pending", "Values is BillableMetric".

SDK drift (spec is right, needs an sdk-clients-update run)

Client Divergence
Ruby, Python, Go No method for POST /events/estimate_instant_fees or POST /events/batch_estimate_instant_fees.
Python, Go No method for GET /events (list with filters).
Go EventEstimateFeesInput misses precise_total_amount_cents, which Fees::EstimatePayInAdvanceService reads.

Suspected lago-api bugs (spec unchanged)

Needs human confirmation (not changed)

  • GET /events_enriched (beta, ClickHouse organizations only) is not in the spec. Should it be documented?
  • external_contract_id is accepted on POST /events and POST /events/batch as an alias of external_subscription_id, but is not documented. Is it a public field of the v1 API?
  • external_subscription_id is required and non-null on events in the spec, but the Event model only validates transaction_id and code. 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.

@groyoh
groyoh marked this pull request as ready for review October 8, 2026 15:05
Copilot AI balanced review requested due to automatic review settings October 8, 2026 15:05
@groyoh
groyoh merged commit c579b17 into main Oct 8, 2026
3 checks passed
@groyoh
groyoh deleted the openapi-guardian/2026-10-07-events branch October 8, 2026 15:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants