dub 0.26.12__py3-none-any.whl → 0.27.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. dub/_version.py +3 -3
  2. dub/analytics.py +53 -78
  3. dub/basesdk.py +4 -4
  4. dub/commissions.py +105 -156
  5. dub/customers.py +261 -390
  6. dub/domains.py +309 -472
  7. dub/embed_tokens.py +53 -80
  8. dub/events.py +53 -78
  9. dub/folders.py +205 -316
  10. dub/links.py +511 -770
  11. dub/models/errors/__init__.py +9 -0
  12. dub/models/errors/badrequest.py +12 -6
  13. dub/models/errors/conflict.py +12 -6
  14. dub/models/errors/duberror.py +26 -0
  15. dub/models/errors/forbidden.py +12 -6
  16. dub/models/errors/internalservererror.py +12 -6
  17. dub/models/errors/inviteexpired.py +12 -6
  18. dub/models/errors/no_response_error.py +13 -0
  19. dub/models/errors/notfound.py +12 -6
  20. dub/models/errors/ratelimitexceeded.py +12 -6
  21. dub/models/errors/responsevalidationerror.py +25 -0
  22. dub/models/errors/sdkerror.py +30 -14
  23. dub/models/errors/unauthorized.py +12 -6
  24. dub/models/errors/unprocessableentity.py +12 -6
  25. dub/models/operations/createcustomer.py +3 -0
  26. dub/models/operations/getcustomer.py +3 -0
  27. dub/models/operations/getcustomers.py +3 -0
  28. dub/models/operations/tracklead.py +2 -2
  29. dub/models/operations/tracksale.py +2 -2
  30. dub/models/operations/updatecustomer.py +3 -0
  31. dub/partners.py +255 -384
  32. dub/qr_codes.py +49 -74
  33. dub/tags.py +205 -308
  34. dub/track.py +105 -156
  35. dub/utils/serializers.py +3 -2
  36. dub/utils/unmarshal_json_response.py +24 -0
  37. dub/workspaces.py +105 -156
  38. {dub-0.26.12.dist-info → dub-0.27.1.dist-info}/METADATA +50 -56
  39. {dub-0.26.12.dist-info → dub-0.27.1.dist-info}/RECORD +41 -37
  40. {dub-0.26.12.dist-info → dub-0.27.1.dist-info}/LICENSE +0 -0
  41. {dub-0.26.12.dist-info → dub-0.27.1.dist-info}/WHEEL +0 -0
@@ -12,6 +12,7 @@ if TYPE_CHECKING:
12
12
  ConflictError,
13
13
  ConflictErrorTypedDict,
14
14
  )
15
+ from .duberror import DubError
15
16
  from .forbidden import (
16
17
  Forbidden,
17
18
  ForbiddenCode,
@@ -33,6 +34,7 @@ if TYPE_CHECKING:
33
34
  InviteExpiredError,
34
35
  InviteExpiredErrorTypedDict,
35
36
  )
37
+ from .no_response_error import NoResponseError
36
38
  from .notfound import (
37
39
  NotFound,
38
40
  NotFoundCode,
@@ -47,6 +49,7 @@ if TYPE_CHECKING:
47
49
  RateLimitExceededError,
48
50
  RateLimitExceededErrorTypedDict,
49
51
  )
52
+ from .responsevalidationerror import ResponseValidationError
50
53
  from .sdkerror import SDKError
51
54
  from .unauthorized import (
52
55
  Unauthorized,
@@ -72,6 +75,7 @@ __all__ = [
72
75
  "ConflictData",
73
76
  "ConflictError",
74
77
  "ConflictErrorTypedDict",
78
+ "DubError",
75
79
  "Error",
76
80
  "ErrorTypedDict",
77
81
  "Forbidden",
@@ -89,6 +93,7 @@ __all__ = [
89
93
  "InviteExpiredData",
90
94
  "InviteExpiredError",
91
95
  "InviteExpiredErrorTypedDict",
96
+ "NoResponseError",
92
97
  "NotFound",
93
98
  "NotFoundCode",
94
99
  "NotFoundData",
@@ -99,6 +104,7 @@ __all__ = [
99
104
  "RateLimitExceededData",
100
105
  "RateLimitExceededError",
101
106
  "RateLimitExceededErrorTypedDict",
107
+ "ResponseValidationError",
102
108
  "SDKError",
103
109
  "Unauthorized",
104
110
  "UnauthorizedCode",
@@ -123,6 +129,7 @@ _dynamic_imports: dict[str, str] = {
123
129
  "ConflictData": ".conflict",
124
130
  "ConflictError": ".conflict",
125
131
  "ConflictErrorTypedDict": ".conflict",
132
+ "DubError": ".duberror",
126
133
  "Forbidden": ".forbidden",
127
134
  "ForbiddenCode": ".forbidden",
128
135
  "ForbiddenData": ".forbidden",
@@ -138,6 +145,7 @@ _dynamic_imports: dict[str, str] = {
138
145
  "InviteExpiredData": ".inviteexpired",
139
146
  "InviteExpiredError": ".inviteexpired",
140
147
  "InviteExpiredErrorTypedDict": ".inviteexpired",
148
+ "NoResponseError": ".no_response_error",
141
149
  "NotFound": ".notfound",
142
150
  "NotFoundCode": ".notfound",
143
151
  "NotFoundData": ".notfound",
@@ -148,6 +156,7 @@ _dynamic_imports: dict[str, str] = {
148
156
  "RateLimitExceededData": ".ratelimitexceeded",
149
157
  "RateLimitExceededError": ".ratelimitexceeded",
150
158
  "RateLimitExceededErrorTypedDict": ".ratelimitexceeded",
159
+ "ResponseValidationError": ".responsevalidationerror",
151
160
  "SDKError": ".sdkerror",
152
161
  "Unauthorized": ".unauthorized",
153
162
  "UnauthorizedCode": ".unauthorized",
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class BadRequestData(BaseModel):
38
39
  error: Error
39
40
 
40
41
 
41
- class BadRequest(Exception):
42
+ class BadRequest(DubError):
42
43
  r"""The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing)."""
43
44
 
44
45
  data: BadRequestData
45
46
 
46
- def __init__(self, data: BadRequestData):
47
+ def __init__(
48
+ self,
49
+ data: BadRequestData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, BadRequestData)
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class ConflictData(BaseModel):
38
39
  error: ConflictError
39
40
 
40
41
 
41
- class Conflict(Exception):
42
+ class Conflict(DubError):
42
43
  r"""This response is sent when a request conflicts with the current state of the server."""
43
44
 
44
45
  data: ConflictData
45
46
 
46
- def __init__(self, data: ConflictData):
47
+ def __init__(
48
+ self,
49
+ data: ConflictData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, ConflictData)
@@ -0,0 +1,26 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ import httpx
4
+ from typing import Optional
5
+
6
+
7
+ class DubError(Exception):
8
+ """The base class for all HTTP error responses."""
9
+
10
+ message: str
11
+ status_code: int
12
+ body: str
13
+ headers: httpx.Headers
14
+ raw_response: httpx.Response
15
+
16
+ def __init__(
17
+ self, message: str, raw_response: httpx.Response, body: Optional[str] = None
18
+ ):
19
+ self.message = message
20
+ self.status_code = raw_response.status_code
21
+ self.body = body if body is not None else raw_response.text
22
+ self.headers = raw_response.headers
23
+ self.raw_response = raw_response
24
+
25
+ def __str__(self):
26
+ return self.message
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class ForbiddenData(BaseModel):
38
39
  error: ForbiddenError
39
40
 
40
41
 
41
- class Forbidden(Exception):
42
+ class Forbidden(DubError):
42
43
  r"""The client does not have access rights to the content; that is, it is unauthorized, so the server is refusing to give the requested resource. Unlike 401 Unauthorized, the client's identity is known to the server."""
43
44
 
44
45
  data: ForbiddenData
45
46
 
46
- def __init__(self, data: ForbiddenData):
47
+ def __init__(
48
+ self,
49
+ data: ForbiddenData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, ForbiddenData)
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class InternalServerErrorData(BaseModel):
38
39
  error: InternalServerErrorError
39
40
 
40
41
 
41
- class InternalServerError(Exception):
42
+ class InternalServerError(DubError):
42
43
  r"""The server has encountered a situation it does not know how to handle."""
43
44
 
44
45
  data: InternalServerErrorData
45
46
 
46
- def __init__(self, data: InternalServerErrorData):
47
+ def __init__(
48
+ self,
49
+ data: InternalServerErrorData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, InternalServerErrorData)
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class InviteExpiredData(BaseModel):
38
39
  error: InviteExpiredError
39
40
 
40
41
 
41
- class InviteExpired(Exception):
42
+ class InviteExpired(DubError):
42
43
  r"""This response is sent when the requested content has been permanently deleted from server, with no forwarding address."""
43
44
 
44
45
  data: InviteExpiredData
45
46
 
46
- def __init__(self, data: InviteExpiredData):
47
+ def __init__(
48
+ self,
49
+ data: InviteExpiredData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, InviteExpiredData)
@@ -0,0 +1,13 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ class NoResponseError(Exception):
4
+ """Error raised when no HTTP response is received from the server."""
5
+
6
+ message: str
7
+
8
+ def __init__(self, message: str = "No response received"):
9
+ self.message = message
10
+ super().__init__(message)
11
+
12
+ def __str__(self):
13
+ return self.message
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class NotFoundData(BaseModel):
38
39
  error: NotFoundError
39
40
 
40
41
 
41
- class NotFound(Exception):
42
+ class NotFound(DubError):
42
43
  r"""The server cannot find the requested resource."""
43
44
 
44
45
  data: NotFoundData
45
46
 
46
- def __init__(self, data: NotFoundData):
47
+ def __init__(
48
+ self,
49
+ data: NotFoundData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, NotFoundData)
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class RateLimitExceededData(BaseModel):
38
39
  error: RateLimitExceededError
39
40
 
40
41
 
41
- class RateLimitExceeded(Exception):
42
+ class RateLimitExceeded(DubError):
42
43
  r"""The user has sent too many requests in a given amount of time (\"rate limiting\")"""
43
44
 
44
45
  data: RateLimitExceededData
45
46
 
46
- def __init__(self, data: RateLimitExceededData):
47
+ def __init__(
48
+ self,
49
+ data: RateLimitExceededData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, RateLimitExceededData)
@@ -0,0 +1,25 @@
1
+ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
+
3
+ import httpx
4
+ from typing import Optional
5
+
6
+ from dub.models.errors import DubError
7
+
8
+
9
+ class ResponseValidationError(DubError):
10
+ """Error raised when there is a type mismatch between the response data and the expected Pydantic model."""
11
+
12
+ def __init__(
13
+ self,
14
+ message: str,
15
+ raw_response: httpx.Response,
16
+ cause: Exception,
17
+ body: Optional[str] = None,
18
+ ):
19
+ message = f"{message}: {cause}"
20
+ super().__init__(message, raw_response, body)
21
+
22
+ @property
23
+ def cause(self):
24
+ """Normally the Pydantic ValidationError"""
25
+ return self.__cause__
@@ -1,22 +1,38 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
- from dataclasses import dataclass
4
- from typing import Optional
5
3
  import httpx
4
+ from typing import Optional
5
+
6
+ from dub.models.errors import DubError
7
+
8
+ MAX_MESSAGE_LEN = 10_000
9
+
10
+
11
+ class SDKError(DubError):
12
+ """The fallback error class if no more specific error class is matched."""
13
+
14
+ def __init__(
15
+ self, message: str, raw_response: httpx.Response, body: Optional[str] = None
16
+ ):
17
+ body_display = body or raw_response.text or '""'
6
18
 
19
+ if message:
20
+ message += ": "
21
+ message += f"Status {raw_response.status_code}"
7
22
 
8
- @dataclass
9
- class SDKError(Exception):
10
- """Represents an error returned by the API."""
23
+ headers = raw_response.headers
24
+ content_type = headers.get("content-type", '""')
25
+ if content_type != "application/json":
26
+ if " " in content_type:
27
+ content_type = f'"{content_type}"'
28
+ message += f" Content-Type {content_type}"
11
29
 
12
- message: str
13
- status_code: int = -1
14
- body: str = ""
15
- raw_response: Optional[httpx.Response] = None
30
+ if len(body_display) > MAX_MESSAGE_LEN:
31
+ truncated = body_display[:MAX_MESSAGE_LEN]
32
+ remaining = len(body_display) - MAX_MESSAGE_LEN
33
+ body_display = f"{truncated}...and {remaining} more chars"
16
34
 
17
- def __str__(self):
18
- body = ""
19
- if len(self.body) > 0:
20
- body = f"\n{self.body}"
35
+ message += f". Body: {body_display}"
36
+ message = message.strip()
21
37
 
22
- return f"{self.message}: Status {self.status_code}{body}"
38
+ super().__init__(message, raw_response, body)
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class UnauthorizedData(BaseModel):
38
39
  error: UnauthorizedError
39
40
 
40
41
 
41
- class Unauthorized(Exception):
42
+ class Unauthorized(DubError):
42
43
  r"""Although the HTTP standard specifies \"unauthorized\", semantically this response means \"unauthenticated\". That is, the client must authenticate itself to get the requested response."""
43
44
 
44
45
  data: UnauthorizedData
45
46
 
46
- def __init__(self, data: UnauthorizedData):
47
+ def __init__(
48
+ self,
49
+ data: UnauthorizedData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, UnauthorizedData)
@@ -1,9 +1,10 @@
1
1
  """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
2
2
 
3
3
  from __future__ import annotations
4
- from dub import utils
4
+ from dub.models.errors import DubError
5
5
  from dub.types import BaseModel
6
6
  from enum import Enum
7
+ import httpx
7
8
  from typing import Optional
8
9
  from typing_extensions import NotRequired, TypedDict
9
10
 
@@ -38,13 +39,18 @@ class UnprocessableEntityData(BaseModel):
38
39
  error: UnprocessableEntityError
39
40
 
40
41
 
41
- class UnprocessableEntity(Exception):
42
+ class UnprocessableEntity(DubError):
42
43
  r"""The request was well-formed but was unable to be followed due to semantic errors."""
43
44
 
44
45
  data: UnprocessableEntityData
45
46
 
46
- def __init__(self, data: UnprocessableEntityData):
47
+ def __init__(
48
+ self,
49
+ data: UnprocessableEntityData,
50
+ raw_response: httpx.Response,
51
+ body: Optional[str] = None,
52
+ ):
53
+ fallback = body or raw_response.text
54
+ message = str(data.error.message) or fallback
55
+ super().__init__(message, raw_response, body)
47
56
  self.data = data
48
-
49
- def __str__(self) -> str:
50
- return utils.marshal_json(self.data, UnprocessableEntityData)
@@ -195,6 +195,7 @@ class CreateCustomerDiscountTypedDict(TypedDict):
195
195
  max_duration: Nullable[float]
196
196
  coupon_id: Nullable[str]
197
197
  coupon_test_id: Nullable[str]
198
+ default: bool
198
199
  description: NotRequired[Nullable[str]]
199
200
  partners_count: NotRequired[Nullable[float]]
200
201
 
@@ -212,6 +213,8 @@ class CreateCustomerDiscount(BaseModel):
212
213
 
213
214
  coupon_test_id: Annotated[Nullable[str], pydantic.Field(alias="couponTestId")]
214
215
 
216
+ default: bool
217
+
215
218
  description: OptionalNullable[str] = UNSET
216
219
 
217
220
  partners_count: Annotated[
@@ -163,6 +163,7 @@ class GetCustomerDiscountTypedDict(TypedDict):
163
163
  max_duration: Nullable[float]
164
164
  coupon_id: Nullable[str]
165
165
  coupon_test_id: Nullable[str]
166
+ default: bool
166
167
  description: NotRequired[Nullable[str]]
167
168
  partners_count: NotRequired[Nullable[float]]
168
169
 
@@ -180,6 +181,8 @@ class GetCustomerDiscount(BaseModel):
180
181
 
181
182
  coupon_test_id: Annotated[Nullable[str], pydantic.Field(alias="couponTestId")]
182
183
 
184
+ default: bool
185
+
183
186
  description: OptionalNullable[str] = UNSET
184
187
 
185
188
  partners_count: Annotated[
@@ -247,6 +247,7 @@ class DiscountTypedDict(TypedDict):
247
247
  max_duration: Nullable[float]
248
248
  coupon_id: Nullable[str]
249
249
  coupon_test_id: Nullable[str]
250
+ default: bool
250
251
  description: NotRequired[Nullable[str]]
251
252
  partners_count: NotRequired[Nullable[float]]
252
253
 
@@ -264,6 +265,8 @@ class Discount(BaseModel):
264
265
 
265
266
  coupon_test_id: Annotated[Nullable[str], pydantic.Field(alias="couponTestId")]
266
267
 
268
+ default: bool
269
+
267
270
  description: OptionalNullable[str] = UNSET
268
271
 
269
272
  partners_count: Annotated[
@@ -21,7 +21,7 @@ class TrackLeadRequestBodyTypedDict(TypedDict):
21
21
  r"""The unique ID of the click that the lead conversion event is attributed to. You can read this value from `dub_id` cookie."""
22
22
  event_name: str
23
23
  r"""The name of the lead event to track. Can also be used as a unique identifier to associate a given lead event for a customer for a subsequent sale event (via the `leadEventName` prop in `/track/sale`)."""
24
- external_id: str
24
+ customer_external_id: str
25
25
  r"""The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer."""
26
26
  customer_name: NotRequired[Nullable[str]]
27
27
  r"""The name of the customer. If not passed, a random name will be generated (e.g. “Big Red Caribou”)."""
@@ -44,7 +44,7 @@ class TrackLeadRequestBody(BaseModel):
44
44
  event_name: Annotated[str, pydantic.Field(alias="eventName")]
45
45
  r"""The name of the lead event to track. Can also be used as a unique identifier to associate a given lead event for a customer for a subsequent sale event (via the `leadEventName` prop in `/track/sale`)."""
46
46
 
47
- external_id: Annotated[str, pydantic.Field(alias="externalId")]
47
+ customer_external_id: Annotated[str, pydantic.Field(alias="customerExternalId")]
48
48
  r"""The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer."""
49
49
 
50
50
  customer_name: Annotated[
@@ -20,7 +20,7 @@ class PaymentProcessor(str, Enum):
20
20
 
21
21
 
22
22
  class TrackSaleRequestBodyTypedDict(TypedDict):
23
- external_id: str
23
+ customer_external_id: str
24
24
  r"""The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer."""
25
25
  amount: int
26
26
  r"""The amount of the sale in cents (for all two-decimal currencies). If the sale is in a zero-decimal currency, pass the full integer value (e.g. `1437` JPY). Learn more: https://d.to/currency"""
@@ -39,7 +39,7 @@ class TrackSaleRequestBodyTypedDict(TypedDict):
39
39
 
40
40
 
41
41
  class TrackSaleRequestBody(BaseModel):
42
- external_id: Annotated[str, pydantic.Field(alias="externalId")]
42
+ customer_external_id: Annotated[str, pydantic.Field(alias="customerExternalId")]
43
43
  r"""The unique ID of the customer in your system. Will be used to identify and attribute all future events to this customer."""
44
44
 
45
45
  amount: int
@@ -229,6 +229,7 @@ class UpdateCustomerDiscountTypedDict(TypedDict):
229
229
  max_duration: Nullable[float]
230
230
  coupon_id: Nullable[str]
231
231
  coupon_test_id: Nullable[str]
232
+ default: bool
232
233
  description: NotRequired[Nullable[str]]
233
234
  partners_count: NotRequired[Nullable[float]]
234
235
 
@@ -246,6 +247,8 @@ class UpdateCustomerDiscount(BaseModel):
246
247
 
247
248
  coupon_test_id: Annotated[Nullable[str], pydantic.Field(alias="couponTestId")]
248
249
 
250
+ default: bool
251
+
249
252
  description: OptionalNullable[str] = UNSET
250
253
 
251
254
  partners_count: Annotated[