Skip to content

feat: Add the API fields missing from wallets, customers and more - #431

Merged
groyoh merged 16 commits into
mainfrom
feat/ing-771-sync-openapi-fields
Oct 29, 2026
Merged

groyoh merged 16 commits into
mainfrom
feat/ing-771-sync-openapi-fields

Conversation

@groyoh

@groyoh groyoh commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Context

The Python client could not send or read several fields that the Lago API accepts and returns today, as documented in the OpenAPI spec. Users had to fall back to raw HTTP calls to set them, and the values the API returned were dropped when a response was parsed. The gaps were in wallets, wallet transactions, payments, customers, subscriptions, billing entities, plans, the organization and the alert webhooks.

Description

The models now carry the missing fields:

  • Wallets accept transaction_priority, and wallet transactions accept priority and voided_transaction_id.
  • Payment responses expose the customer, payable and provider fields, status and next_action.
  • Customers send and read account_type and external_salesforce_id, and expose sequential_id and slug. Integration customers get code, is_default and category.
  • Subscriptions send and read progressive_billing_disabled, and send top-level usage_thresholds.
  • Billing entities send and read einvoicing, and the organization sends and reads slug and exposes lago_id.
  • Plan responses expose pending_deletion, entitlements (privilege values keep their JSON type) and applicable_usage_thresholds.
  • New TriggeredAlert and ResolvedAlert models parse the alert.triggered and alert.resolved webhook payloads.

wallet_transactions.create now leaves out priority and voided_transaction_id when they are not set. Sending them as null would make the API reject a void request that has no priority.

Breaking change: SubscriptionResponse.subscription_date is removed, because the API no longer returns it. Code that reads it should use subscription_at.

Each change is covered by unit tests, and the create, update and read calls were also checked through the client against a local Lago API on main.

Fixes ING-771

groyoh added 13 commits October 28, 2026 18:56
## Context

The `alert.triggered` and `alert.resolved` webhooks have no typed model in the client, so consumers parse the payloads by hand.

## Description

`TriggeredAlert` and `ResolvedAlert` were added to `models/alert.py` and exported from `lago_python_client.models`. Crossed and recovered thresholds use a new `AlertCrossedThreshold` model, since `AlertThresholdResponse` also carries `notify_on` and a required `recurring`. Decimal values stay strings. The deprecated `subscription_external_id` and `customer_external_id` fields are kept as optional because the API still sends them.
## Context

The Lago API accepts and returns customer fields that the Python client does not declare yet, so SDK users cannot send or read them.

## Description

`Customer` now accepts `account_type` and `external_salesforce_id`, and `IntegrationCustomer` accepts `code`. Fields left unset are not sent.

`CustomerResponse` exposes `account_type`, `external_salesforce_id`, `sequential_id` and `slug`. `IntegrationCustomerResponse` exposes `code`, `is_default` and `category`.
## Context

The Lago API returns several payment fields that `PaymentResponse` did
not declare, so they were not available on the parsed object.

## Description

`PaymentResponse` now exposes `lago_customer_id`, `external_customer_id`,
`lago_payable_id`, `payable_type`, `status`, `payment_provider_code`,
`payment_provider_type`, `provider_payment_id`, `provider_customer_id`
and `next_action` (a dict). All of them are optional, since payment
receipts embed a payment and their fixtures do not carry these keys.

The fixtures now include a provider payment and a manual payment, whose
provider fields are null and whose `next_action` is empty. A test for
`payments.find` was added.
## Context

The Lago API documents an `einvoicing` flag on billing entities and a `slug` (plus `lago_id`) on the organization. The Python client could neither send nor read these fields.

## Description

`BillingEntity`, `BillingEntityUpdate` and `BillingEntityResponse` now have an optional `einvoicing` boolean. The `Organization` input accepts an optional `slug`, and `OrganizationResponse` exposes `lago_id` and `slug`.

Fields left unset are still dropped from the request body, and an explicit `einvoicing=False` is sent.
## Context

The Lago API accepts and returns `progressive_billing_disabled` on
subscriptions, and accepts `usage_thresholds` on subscription create and
update. The Python client did not declare them, so they were dropped from
requests and responses. `SubscriptionResponse` also declared
`subscription_date`, which the API no longer returns.

## Description

The `Subscription` input now has `progressive_billing_disabled` and
`usage_thresholds` (reusing `UsageThresholdsOverrides`), and
`SubscriptionResponse` exposes `progressive_billing_disabled`.

`SubscriptionResponse.subscription_date` was removed. This is a breaking
change for code that reads it; `subscription_at` holds the same date.
## Context

The plan object returned by the Lago API documents `pending_deletion`, `entitlements` and `applicable_usage_thresholds`, but `PlanResponse` did not declare them, so they were dropped when parsing.

## Description

`PlanResponse` now exposes the three fields. Entitlements are parsed into new models in `models/entitlement.py`. A privilege `value` is typed `Any` so it keeps its JSON type (an integer, a boolean or a string like `"10"`); a `Union` would make pydantic v1 coerce `true` to `1` and `"10"` to `10`.

Applicable usage thresholds use a new `ApplicableUsageThresholdResponse`, since these items have no `lago_id` and `UsageThresholdResponse` requires one.
## Context

The Lago API accepts `transaction_priority` when a wallet is created, and
`priority` and `voided_transaction_id` when a wallet transaction is created.
The input models of the client did not declare these fields, so they could
not be sent.

## Description

`Wallet` now declares `transaction_priority`, and `WalletTransaction`
declares `priority` and `voided_transaction_id`. The response models are
unchanged.
## Context

`client.wallet_transactions.create` serializes the input model without
excluding unset fields, so every unset field is sent as `null`. The Lago API
treats a `null` `priority` or `voided_transaction_id` the same as an absent
one.

## Description

A test now shows that `priority` and `voided_transaction_id` are sent as
`null` when they are not set.
## Context

The wallet transaction specs for priority and voided_transaction_id read
the sent request body and checked a few keys, so other fields of the
body were not checked.

## Description

Both specs now pass the full expected body to match_json, like the
other client specs. Unset fields are expected as null, which is how
WalletTransactionClient serializes them.
## Context

The alert webhook tests only parsed subscription alerts, so the Optional subscription fields and the deprecated keys were never absent or null in a test.

## Description

New tests parse `alert.triggered` and `alert.resolved` payloads for a wallet alert, with the subscription fields set to null and the deprecated keys removed.
## Context

lago-api's void path passes the raw `priority` param to the wallet
transaction create service, which requires a priority. Sending
`priority: null` makes void requests without a priority fail.

## Description

`WalletTransactionClient.create` now leaves out `priority` and
`voided_transaction_id` when they are None. Other fields keep their
current serialization.
The plan entitlement and applicable usage threshold response models are now re-exported from lago_python_client.models, like the other response models.
A priority of 0 is valid and must be sent. This test guards the None check that leaves out an unset priority.
@groyoh
groyoh marked this pull request as ready for review October 29, 2026 08:31
@groyoh
groyoh requested a review from vincent-pochet October 29, 2026 08:31

@vincent-pochet vincent-pochet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. A few non-blocking follow-ups inline, most important first: SubscriptionResponse doesn't parse the applicable_usage_thresholds / entitlements the API returns.

Comment thread lago_python_client/wallets/clients.py Resolved
Comment thread lago_python_client/models/alert.py Resolved
Comment thread lago_python_client/models/alert.py Resolved
TriggeredAlert and ResolvedAlert now inherit their common fields from
AlertWebhookBase. Crossed and recovered thresholds have the same shape as
alert thresholds, so they reuse AlertThresholdResponseList instead of a
separate model.

The resolved alert fixture no longer holds the deprecated
subscription_external_id and customer_external_id keys, which the API only
sends on triggered alerts.
The wallet transaction create now sends input_object.dict(exclude_none=True),
like the other create calls, instead of a hand-written list of keys to drop.
On create, an unset field and a null field have the same effect on the API.
A priority of 0 is still sent.
The API returns applicable_usage_thresholds on subscription create, update
and show, and entitlements on create and show. SubscriptionResponse now
parses both, so callers can read back the thresholds that apply after
sending usage_thresholds.

Subscription entitlements get their own models: each privilege carries the
plan_value and override_value next to the value, and the entitlement holds
the overrides map.
@groyoh
groyoh merged commit 029e991 into main Oct 29, 2026
7 checks passed
@groyoh
groyoh deleted the feat/ing-771-sync-openapi-fields branch October 29, 2026 19:31
@groyoh

groyoh commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @vincent-pochet for the review!

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.

2 participants