Skip to content

feat(account-tree): add usage attribution types and attributed usage - #427

Open
lovrocolic wants to merge 3 commits into
mainfrom
usage_attribution
Open

lovrocolic wants to merge 3 commits into
mainfrom
usage_attribution

Conversation

@lovrocolic

@lovrocolic lovrocolic commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Context

The account tree feature lets usage be tracked under custom types, such as teams or users. The API exposes CRUD endpoints for usage attribution types (getlago/lago-api#6415) and an endpoint that reads attributed usage (getlago/lago-api#6566).

Description

Usage attribution types

  • New client.usage_attribution_types client with create, update, find, destroy and find_all. Each one is keyed by code.
  • New UsageAttributionType input model and UsageAttributionTypeResponse response model.
  • find_all accepts page, per_page, role and search_term options.

Attributed usage

  • New client.customers.attributed_usage(external_customer_id, external_subscription_id, group_by, filters=None, charge_codes=None, options=None). group_by is required, as it is in the API. It calls GET /customers/:external_customer_id/attributed_usage.
  • In filters, a string value is sent as filters[key]=value and a list value as repeated filters[key][]=value. charge_codes is sent as charge_codes[]=value. An empty list in either one raises ValueError, because it would send no param and the API would drop the filter or read it as all charges. The other query params (from_datetime, to_datetime, split_charge_code, search_term, basis, page, per_page) go through options.
  • It returns {"attributed_usage": AttributedUsageResponse, "meta": {...}}. The response includes the rows, the unattributed usage and the totals, and each one is split into charge usage.

Fixtures and tests cover each endpoint.

🤖 Generated with Claude Code

@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.

Checked against the API (main) and the spec in getlago/lago-openapi#665. The usage attribution type models and payload match the API. Only minor issues in attributed_usage.

Comment thread lago_python_client/customers/clients.py Resolved
Comment thread lago_python_client/customers/clients.py Resolved
Comment thread lago_python_client/customers/clients.py Resolved
lovrocolic and others added 2 commits October 23, 2026 12:19
if not charge_codes:
# An empty list would send no param, which the API reads as all charges
raise ValueError("charge_codes must not be empty")
query_params["charge_codes[]"] = list(charge_codes)

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.

A single string passed here, charge_codes="tokens", gets split into characters by list(charge_codes) and sends charge_codes[]=t&charge_codes[]=o&.... The API then returns a 404 "charge not found", which looks like a missing charge rather than a misuse of the client. Could we wrap a string in a list ([charge_codes] if isinstance(charge_codes, str) else list(charge_codes)) or raise a clear error?

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