Skip to content

Commit 84cb631

Browse files
release: 7.4.0 (#117)
* fix(types): allow pyright to infer TypedDict types within SequenceNotStr * chore: add missing docstrings * feat: Add timezone field to Delay schema * chore(internal): add missing files argument to base client * feat: Update bulk API spec: make event required, document profile.email req… * release: 7.4.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
1 parent 05a8826 commit 84cb631

28 files changed

Lines changed: 260 additions & 75 deletions

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "7.3.0"
2+
".": "7.4.0"
33
}

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 77
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-59c9efb45644c91ba21c8efaa0af53637071483ed661d5f99f27a15996624ac2.yml
3-
openapi_spec_hash: 0e1d703ca5e68a4d1f1489c04eb43765
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-9859cb664d1a9de0323c857e96cf41d0a5ac5c5903c501059f36bf62553b1583.yml
3+
openapi_spec_hash: d29149d60504eba35c63f583ce4bf0bc
44
config_hash: 3ec521d062b05b81c22bc1a25bfe3d02

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
# Changelog
22

3+
## 7.4.0 (2025-12-16)
4+
5+
Full Changelog: [v7.3.0...v7.4.0](https://github.com/trycourier/courier-python/compare/v7.3.0...v7.4.0)
6+
7+
### Features
8+
9+
* Add timezone field to Delay schema ([85b8c6f](https://github.com/trycourier/courier-python/commit/85b8c6f5fca9a68321d16c2927ea89cf509813d7))
10+
* Update bulk API spec: make event required, document profile.email req… ([1b99e93](https://github.com/trycourier/courier-python/commit/1b99e9336b2f38487b873962a81e9ffd2b82f019))
11+
12+
13+
### Bug Fixes
14+
15+
* **types:** allow pyright to infer TypedDict types within SequenceNotStr ([ab5f2d0](https://github.com/trycourier/courier-python/commit/ab5f2d0b7e4f0534e3fff420f7f1ea6c159693bf))
16+
17+
18+
### Chores
19+
20+
* add missing docstrings ([ea1dec0](https://github.com/trycourier/courier-python/commit/ea1dec0951b56ee5420d3c81d0fcbd5442ae726f))
21+
* **internal:** add missing files argument to base client ([a6fba70](https://github.com/trycourier/courier-python/commit/a6fba70d97a444af4c6c06c9adeb539051ceaee8))
22+
323
## 7.3.0 (2025-12-08)
424

525
Full Changelog: [v7.2.0...v7.3.0](https://github.com/trycourier/courier-python/compare/v7.2.0...v7.3.0)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "trycourier"
3-
version = "7.3.0"
3+
version = "7.4.0"
44
description = "The official Python library for the Courier API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"

src/courier/_base_client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,9 +1247,12 @@ def patch(
12471247
*,
12481248
cast_to: Type[ResponseT],
12491249
body: Body | None = None,
1250+
files: RequestFiles | None = None,
12501251
options: RequestOptions = {},
12511252
) -> ResponseT:
1252-
opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
1253+
opts = FinalRequestOptions.construct(
1254+
method="patch", url=path, json_data=body, files=to_httpx_files(files), **options
1255+
)
12531256
return self.request(cast_to, opts)
12541257

12551258
def put(
@@ -1767,9 +1770,12 @@ async def patch(
17671770
*,
17681771
cast_to: Type[ResponseT],
17691772
body: Body | None = None,
1773+
files: RequestFiles | None = None,
17701774
options: RequestOptions = {},
17711775
) -> ResponseT:
1772-
opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
1776+
opts = FinalRequestOptions.construct(
1777+
method="patch", url=path, json_data=body, files=to_httpx_files(files), **options
1778+
)
17731779
return await self.request(cast_to, opts)
17741780

17751781
async def put(

src/courier/_types.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ class HttpxSendArgs(TypedDict, total=False):
243243
if TYPE_CHECKING:
244244
# This works because str.__contains__ does not accept object (either in typeshed or at runtime)
245245
# https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285
246+
#
247+
# Note: index() and count() methods are intentionally omitted to allow pyright to properly
248+
# infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr.
246249
class SequenceNotStr(Protocol[_T_co]):
247250
@overload
248251
def __getitem__(self, index: SupportsIndex, /) -> _T_co: ...
@@ -251,8 +254,6 @@ def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ...
251254
def __contains__(self, value: object, /) -> bool: ...
252255
def __len__(self) -> int: ...
253256
def __iter__(self) -> Iterator[_T_co]: ...
254-
def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ...
255-
def count(self, value: Any, /) -> int: ...
256257
def __reversed__(self) -> Iterator[_T_co]: ...
257258
else:
258259
# just point this to a normal `Sequence` at runtime to avoid having to special case

src/courier/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
__title__ = "courier"
4-
__version__ = "7.3.0" # x-release-please-version
4+
__version__ = "7.4.0" # x-release-please-version

src/courier/resources/bulk.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ def add_users(
6060
timeout: float | httpx.Timeout | None | NotGiven = not_given,
6161
) -> None:
6262
"""
63-
Ingest user data into a Bulk Job
63+
Ingest user data into a Bulk Job.
64+
65+
**Important**: For email-based bulk jobs, each user must include `profile.email`
66+
for provider routing to work correctly. The `to.email` field is not sufficient
67+
for email provider routing.
6468
6569
Args:
6670
extra_headers: Send extra headers
@@ -95,9 +99,22 @@ def create_job(
9599
timeout: float | httpx.Timeout | None | NotGiven = not_given,
96100
) -> BulkCreateJobResponse:
97101
"""
98-
Create a bulk job
102+
Creates a new bulk job for sending messages to multiple recipients.
103+
104+
**Required**: `message.event` (event ID or notification ID)
105+
106+
**Optional (V2 format)**: `message.template` (notification ID) or
107+
`message.content` (Elemental content) can be provided to override the
108+
notification associated with the event.
99109
100110
Args:
111+
message:
112+
Bulk message definition. Supports two formats:
113+
114+
- V1 format: Requires `event` field (event ID or notification ID)
115+
- V2 format: Optionally use `template` (notification ID) or `content` (Elemental
116+
content) in addition to `event`
117+
101118
extra_headers: Send extra headers
102119
103120
extra_query: Add additional query parameters to the request
@@ -257,7 +274,11 @@ async def add_users(
257274
timeout: float | httpx.Timeout | None | NotGiven = not_given,
258275
) -> None:
259276
"""
260-
Ingest user data into a Bulk Job
277+
Ingest user data into a Bulk Job.
278+
279+
**Important**: For email-based bulk jobs, each user must include `profile.email`
280+
for provider routing to work correctly. The `to.email` field is not sufficient
281+
for email provider routing.
261282
262283
Args:
263284
extra_headers: Send extra headers
@@ -292,9 +313,22 @@ async def create_job(
292313
timeout: float | httpx.Timeout | None | NotGiven = not_given,
293314
) -> BulkCreateJobResponse:
294315
"""
295-
Create a bulk job
316+
Creates a new bulk job for sending messages to multiple recipients.
317+
318+
**Required**: `message.event` (event ID or notification ID)
319+
320+
**Optional (V2 format)**: `message.template` (notification ID) or
321+
`message.content` (Elemental content) can be provided to override the
322+
notification associated with the event.
296323
297324
Args:
325+
message:
326+
Bulk message definition. Supports two formats:
327+
328+
- V1 format: Requires `event` field (event ID or notification ID)
329+
- V2 format: Optionally use `template` (notification ID) or `content` (Elemental
330+
content) in addition to `event`
331+
298332
extra_headers: Send extra headers
299333
300334
extra_query: Add additional query parameters to the request

src/courier/types/bulk_create_job_params.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@
1111

1212
class BulkCreateJobParams(TypedDict, total=False):
1313
message: Required[InboundBulkMessageParam]
14+
"""Bulk message definition. Supports two formats:
15+
16+
- V1 format: Requires `event` field (event ID or notification ID)
17+
- V2 format: Optionally use `template` (notification ID) or `content` (Elemental
18+
content) in addition to `event`
19+
"""

src/courier/types/bulk_retrieve_job_response.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010

1111
class Job(BaseModel):
1212
definition: InboundBulkMessage
13+
"""Bulk message definition. Supports two formats:
14+
15+
- V1 format: Requires `event` field (event ID or notification ID)
16+
- V2 format: Optionally use `template` (notification ID) or `content` (Elemental
17+
content) in addition to `event`
18+
"""
1319

1420
enqueued: int
1521

0 commit comments

Comments
 (0)