Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.1.0"
".": "0.2.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 12
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/azure/partnermax-f1de4d57d6db51b99f89340dcc6f5e6526e3b6a2fe412ac6284558b7b7519b5d.yml
openapi_spec_hash: 8b7784a9e9cab8307b6ff497331dda11
config_hash: e0912dd3f20745785d7b0adc0bb61b3f
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/azure/partnermax-52b9912a508298f3c622752ebf2b3a23da0c923955e1c5effe76912f7ed00b44.yml
openapi_spec_hash: 549792bed17760a971f104f9268072b2
config_hash: bf921bb2f4db038ffed15141c74cd018
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 0.2.0 (2026-05-17)

Full Changelog: [v0.1.0...v0.2.0](https://github.com/DealerMax-app/partnermax-python/compare/v0.1.0...v0.2.0)

### Features

* **api:** api update ([c6bbbc9](https://github.com/DealerMax-app/partnermax-python/commit/c6bbbc935dac5ffe3836ea5a1e0a05695e257564))


### Chores

* update SDK settings ([330cce0](https://github.com/DealerMax-app/partnermax-python/commit/330cce0d4b5c07c78814f22e0021e25aa9373bf6))
* update SDK settings ([7f94790](https://github.com/DealerMax-app/partnermax-python/commit/7f94790da7d611485d22af9a3917411cdd84c9fb))

## 0.1.0 (2026-05-17)

Full Changelog: [v0.0.2...v0.1.0](https://github.com/DealerMax-app/partnermax-python/compare/v0.0.2...v0.1.0)
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ nlt_settings = client.dealers.nlt_settings.update(
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
)
print(nlt_settings.down_payment_tiers)
```
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "partnermax"
version = "0.1.0"
version = "0.2.0"
description = "The official Python library for the partnermax API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
30 changes: 24 additions & 6 deletions src/partnermax/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ def __init__(

@cached_property
def keys(self) -> KeysResource:
"""Human session login (cookie JWT) and API key lifecycle management."""
"""API key lifecycle management — issue, list, revoke.

The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer <key>`; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner.
"""
from .resources.keys import KeysResource

return KeysResource(self)
Expand Down Expand Up @@ -403,7 +406,10 @@ def __init__(

@cached_property
def keys(self) -> AsyncKeysResource:
"""Human session login (cookie JWT) and API key lifecycle management."""
"""API key lifecycle management — issue, list, revoke.

The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer <key>`; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner.
"""
from .resources.keys import AsyncKeysResource

return AsyncKeysResource(self)
Expand Down Expand Up @@ -567,7 +573,10 @@ def __init__(self, client: Partnermax) -> None:

@cached_property
def keys(self) -> keys.KeysResourceWithRawResponse:
"""Human session login (cookie JWT) and API key lifecycle management."""
"""API key lifecycle management — issue, list, revoke.

The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer <key>`; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner.
"""
from .resources.keys import KeysResourceWithRawResponse

return KeysResourceWithRawResponse(self._client.keys)
Expand All @@ -588,7 +597,10 @@ def __init__(self, client: AsyncPartnermax) -> None:

@cached_property
def keys(self) -> keys.AsyncKeysResourceWithRawResponse:
"""Human session login (cookie JWT) and API key lifecycle management."""
"""API key lifecycle management — issue, list, revoke.

The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer <key>`; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner.
"""
from .resources.keys import AsyncKeysResourceWithRawResponse

return AsyncKeysResourceWithRawResponse(self._client.keys)
Expand All @@ -609,7 +621,10 @@ def __init__(self, client: Partnermax) -> None:

@cached_property
def keys(self) -> keys.KeysResourceWithStreamingResponse:
"""Human session login (cookie JWT) and API key lifecycle management."""
"""API key lifecycle management — issue, list, revoke.

The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer <key>`; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner.
"""
from .resources.keys import KeysResourceWithStreamingResponse

return KeysResourceWithStreamingResponse(self._client.keys)
Expand All @@ -630,7 +645,10 @@ def __init__(self, client: AsyncPartnermax) -> None:

@cached_property
def keys(self) -> keys.AsyncKeysResourceWithStreamingResponse:
"""Human session login (cookie JWT) and API key lifecycle management."""
"""API key lifecycle management — issue, list, revoke.

The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer <key>`; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner.
"""
from .resources.keys import AsyncKeysResourceWithStreamingResponse

return AsyncKeysResourceWithStreamingResponse(self._client.keys)
Expand Down
2 changes: 1 addition & 1 deletion src/partnermax/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "partnermax"
__version__ = "0.1.0" # x-release-please-version
__version__ = "0.2.0" # x-release-please-version
32 changes: 22 additions & 10 deletions src/partnermax/resources/dealers/nlt_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ def update(
*,
agency_markup_percent: float,
down_payment_tiers: DownPaymentTiersParam,
vat_treatment: Literal["private", "business"],
currency: Literal["EUR"] | Omit = omit,
idempotency_key: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
Expand All @@ -100,9 +99,17 @@ def update(
/ medium / high). Down-payment tiers MUST be in strictly ascending order.
Changes propagate to the cross-network AI surfaces within five minutes.

The displayed canon for an offer is computed as:
`displayed_canon = base_canon × (1 + agency_markup_percent / 100) × adjustment_for_down_payment_tier × vat_multiplier`
where `vat_multiplier = 1.22` if `vat_treatment = "private"`, else `1.0`.
The displayed monthly canon for an offer is computed as:

```
listino_imponibile = prezzo_listino / 1.22
provvigione = listino_imponibile × (agency_markup_percent / 100)
canon = base_canon + provvigione / durata - anticipo_eur / durata
if offer.vat_treatment == "private": canon *= 1.22
```

VAT treatment is a property of each offer (`NltOfferSummary.vat_treatment`), not
of the dealer.

Args:
down_payment_tiers: Three down-payment scenarios shown to consumers, in strictly ascending order
Expand All @@ -125,7 +132,6 @@ def update(
{
"agency_markup_percent": agency_markup_percent,
"down_payment_tiers": down_payment_tiers,
"vat_treatment": vat_treatment,
"currency": currency,
},
nlt_setting_update_params.NltSettingUpdateParams,
Expand Down Expand Up @@ -198,7 +204,6 @@ async def update(
*,
agency_markup_percent: float,
down_payment_tiers: DownPaymentTiersParam,
vat_treatment: Literal["private", "business"],
currency: Literal["EUR"] | Omit = omit,
idempotency_key: str | Omit = omit,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
Expand All @@ -213,9 +218,17 @@ async def update(
/ medium / high). Down-payment tiers MUST be in strictly ascending order.
Changes propagate to the cross-network AI surfaces within five minutes.

The displayed canon for an offer is computed as:
`displayed_canon = base_canon × (1 + agency_markup_percent / 100) × adjustment_for_down_payment_tier × vat_multiplier`
where `vat_multiplier = 1.22` if `vat_treatment = "private"`, else `1.0`.
The displayed monthly canon for an offer is computed as:

```
listino_imponibile = prezzo_listino / 1.22
provvigione = listino_imponibile × (agency_markup_percent / 100)
canon = base_canon + provvigione / durata - anticipo_eur / durata
if offer.vat_treatment == "private": canon *= 1.22
```

VAT treatment is a property of each offer (`NltOfferSummary.vat_treatment`), not
of the dealer.

Args:
down_payment_tiers: Three down-payment scenarios shown to consumers, in strictly ascending order
Expand All @@ -238,7 +251,6 @@ async def update(
{
"agency_markup_percent": agency_markup_percent,
"down_payment_tiers": down_payment_tiers,
"vat_treatment": vat_treatment,
"currency": currency,
},
nlt_setting_update_params.NltSettingUpdateParams,
Expand Down
22 changes: 16 additions & 6 deletions src/partnermax/resources/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@


class KeysResource(SyncAPIResource):
"""Human session login (cookie JWT) and API key lifecycle management."""
"""API key lifecycle management — issue, list, revoke.

The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer <key>`; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner.
"""

@cached_property
def with_raw_response(self) -> KeysResourceWithRawResponse:
Expand Down Expand Up @@ -88,8 +91,10 @@ def issue(

The key material is returned in
plaintext in the response and is never retrievable again — store it securely on
first receipt. Can be called with a cookie session (from /v1/auth/login) or with
an existing API key that has `can_issue_keys` capability.
first receipt. Must be called with an existing API key that has the
`can_issue_keys` capability (the initial key issued by DealerMAX support has
this capability by default; rotated keys inherit it unless explicitly scoped
down).

Args:
label: Human-readable identifier for this key, used for safe logging.
Expand Down Expand Up @@ -159,7 +164,10 @@ def revoke(


class AsyncKeysResource(AsyncAPIResource):
"""Human session login (cookie JWT) and API key lifecycle management."""
"""API key lifecycle management — issue, list, revoke.

The partner authenticates every request with `X-Api-Key` (preferred) or `Authorization: Bearer <key>`; the server identifies the partner from the key and scopes all reads/writes to dealers owned by that partner.
"""

@cached_property
def with_raw_response(self) -> AsyncKeysResourceWithRawResponse:
Expand Down Expand Up @@ -221,8 +229,10 @@ async def issue(

The key material is returned in
plaintext in the response and is never retrievable again — store it securely on
first receipt. Can be called with a cookie session (from /v1/auth/login) or with
an existing API key that has `can_issue_keys` capability.
first receipt. Must be called with an existing API key that has the
`can_issue_keys` capability (the initial key issued by DealerMAX support has
this capability by default; rotated keys inherit it unless explicitly scoped
down).

Args:
label: Human-readable identifier for this key, used for safe logging.
Expand Down
5 changes: 5 additions & 0 deletions src/partnermax/types/dealer_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class DealerDetail(DealerSummary):
metadata: Optional[Dict[str, str]] = None

nlt_settings: Optional[NltSettings] = None
"""Dealer-level NLT economics.

VAT treatment is NOT a dealer-level field — it is a property of the offer (see
`NltOfferSummary.vat_treatment`).
"""

partner_id: Optional[str] = None

Expand Down
7 changes: 7 additions & 0 deletions src/partnermax/types/dealers/nlt/nlt_offer_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ class NltOfferSummary(BaseModel):

segment: Literal["A", "B", "C", "D", "E", "SUV", "VAN"]

vat_treatment: Literal["private", "business"]
"""VAT treatment of this offer (not the dealer).

`private` → `monthly_canon_from_eur` is VAT-inclusive (×1.22). `business` →
VAT-exclusive. Sourced from `nlt_offerte.solo_privati`.
"""

canonical_url: Optional[str] = None
"""Consumer-facing URL on the dealer's public site."""

Expand Down
2 changes: 0 additions & 2 deletions src/partnermax/types/dealers/nlt_setting_update_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class NltSettingUpdateParams(TypedDict, total=False):
(low < medium < high).
"""

vat_treatment: Required[Literal["private", "business"]]

currency: Literal["EUR"]

idempotency_key: Annotated[str, PropertyInfo(alias="Idempotency-Key")]
8 changes: 5 additions & 3 deletions src/partnermax/types/dealers/nlt_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@


class NltSettings(BaseModel):
"""Dealer-level NLT economics.

VAT treatment is NOT a dealer-level field — it is a property of the offer (see `NltOfferSummary.vat_treatment`).
"""

agency_markup_percent: float
"""Markup applied on top of the network base canon, in percent. Hard cap at 10%."""

Expand All @@ -25,6 +30,3 @@ class NltSettings(BaseModel):
"""

effective_from: datetime

vat_treatment: Literal["private", "business"]
"""private = display VAT-inclusive (×1.22). business = display VAT-exclusive."""
10 changes: 0 additions & 10 deletions tests/api_resources/dealers/test_nlt_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ def test_method_update(self, client: Partnermax) -> None:
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
)
assert_matches_type(NltSettings, nlt_setting, path=["response"])

Expand All @@ -85,7 +84,6 @@ def test_method_update_with_all_params(self, client: Partnermax) -> None:
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
currency="EUR",
idempotency_key="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
Expand All @@ -102,7 +100,6 @@ def test_raw_response_update(self, client: Partnermax) -> None:
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
)

assert response.is_closed is True
Expand All @@ -121,7 +118,6 @@ def test_streaming_response_update(self, client: Partnermax) -> None:
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
Expand All @@ -143,7 +139,6 @@ def test_path_params_update(self, client: Partnermax) -> None:
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
)


Expand Down Expand Up @@ -205,7 +200,6 @@ async def test_method_update(self, async_client: AsyncPartnermax) -> None:
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
)
assert_matches_type(NltSettings, nlt_setting, path=["response"])

Expand All @@ -220,7 +214,6 @@ async def test_method_update_with_all_params(self, async_client: AsyncPartnermax
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
currency="EUR",
idempotency_key="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
Expand All @@ -237,7 +230,6 @@ async def test_raw_response_update(self, async_client: AsyncPartnermax) -> None:
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
)

assert response.is_closed is True
Expand All @@ -256,7 +248,6 @@ async def test_streaming_response_update(self, async_client: AsyncPartnermax) ->
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
Expand All @@ -278,5 +269,4 @@ async def test_path_params_update(self, async_client: AsyncPartnermax) -> None:
"low_eur": 0,
"medium_eur": 3000,
},
vat_treatment="private",
)
Loading