From 51ca221d09e863a830a91842a20ef9607c4c9fe8 Mon Sep 17 00:00:00 2001 From: mayankbohradev Date: Sat, 17 Oct 2026 22:02:04 +0530 Subject: [PATCH 1/2] fix: allow null optional inbox params --- resend/inboxes/drafts/_inbox_drafts.py | 8 ++--- .../threads/emails/_inbox_thread_emails.py | 32 ++++++++--------- tests/test_nullable_inbox_params.py | 35 +++++++++++++++++++ 3 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 tests/test_nullable_inbox_params.py diff --git a/resend/inboxes/drafts/_inbox_drafts.py b/resend/inboxes/drafts/_inbox_drafts.py index 701601e..0ecb346 100644 --- a/resend/inboxes/drafts/_inbox_drafts.py +++ b/resend/inboxes/drafts/_inbox_drafts.py @@ -111,7 +111,7 @@ class CreateParams(TypedDict): subject (NotRequired[Optional[str]]): The subject. html (NotRequired[Optional[str]]): The HTML body. text (NotRequired[Optional[str]]): The plain text body. - attachments (NotRequired[List[AttachmentParams]]): Files to store on the draft. + attachments (NotRequired[Optional[List[AttachmentParams]]]): Files to store on the draft. thread_id (NotRequired[str]): The Thread ID to reply in. reply_to_email_id (NotRequired[str]): The Email ID to reply to. """ @@ -146,7 +146,7 @@ class CreateParams(TypedDict): """ The plain text body. """ - attachments: NotRequired[List["InboxDrafts.AttachmentParams"]] + attachments: NotRequired[Optional[List["InboxDrafts.AttachmentParams"]]] """ Files to store on the draft. Each one needs ``content``, ``filename`` and ``content_type``. A ``path`` or ``content_id`` returns a 422. The files on @@ -313,10 +313,10 @@ class SendParams(TypedDict): SendParams are the optional body parameters for sending a draft. Attributes: - headers (NotRequired[Dict[str, str]]): Custom headers to be added to the email. + headers (NotRequired[Optional[Dict[str, str]]]): Custom headers to be added to the email. """ - headers: NotRequired[Dict[str, str]] + headers: NotRequired[Optional[Dict[str, str]]] """ Custom headers to be added to the email. The ``In-Reply-To``, ``References`` and ``Reply-To`` headers are set by Resend and cannot be diff --git a/resend/inboxes/threads/emails/_inbox_thread_emails.py b/resend/inboxes/threads/emails/_inbox_thread_emails.py index 8c2354c..866e73a 100644 --- a/resend/inboxes/threads/emails/_inbox_thread_emails.py +++ b/resend/inboxes/threads/emails/_inbox_thread_emails.py @@ -83,12 +83,12 @@ class ReplyParams(TypedDict): email_id (str): The Email ID of the message to reply to. html (NotRequired[str]): The HTML body of the reply. text (NotRequired[str]): The plain-text body of the reply. - cc (NotRequired[Union[str, List[str]]]): CC recipients. - bcc (NotRequired[Union[str, List[str]]]): BCC recipients. + cc (NotRequired[Optional[Union[str, List[str]]]]): CC recipients. + bcc (NotRequired[Optional[Union[str, List[str]]]]): BCC recipients. subject (NotRequired[str]): The subject of the reply. reply_all (NotRequired[bool]): Reply to everyone on the original message. - headers (NotRequired[Dict[str, str]]): Custom headers to be added to the email. - attachments (NotRequired[List[Union[Attachment, RemoteAttachment]]]): Files to attach. + headers (NotRequired[Optional[Dict[str, str]]]): Custom headers to be added to the email. + attachments (NotRequired[Optional[List[Union[Attachment, RemoteAttachment]]]]): Files to attach. """ inbox_id: str @@ -112,7 +112,7 @@ class ReplyParams(TypedDict): """ The plain-text body of the reply. """ - cc: NotRequired[Union[str, List[str]]] + cc: NotRequired[Optional[Union[str, List[str]]]] """ CC recipient email address. For multiple addresses, send a list of strings. Without ``reply_all``, the reply copies only these addresses. With @@ -121,7 +121,7 @@ class ReplyParams(TypedDict): addresses. After the ``to`` recipients are added, the reply cannot exceed 50 recipients. """ - bcc: NotRequired[Union[str, List[str]]] + bcc: NotRequired[Optional[Union[str, List[str]]]] """ BCC recipient email address. For multiple addresses, send a list of strings. BCC recipients of the original message are never copied. @@ -142,7 +142,7 @@ class ReplyParams(TypedDict): recipients. The inbox address is always excluded, and duplicates are removed. Defaults to False. """ - headers: NotRequired[Dict[str, str]] + headers: NotRequired[Optional[Dict[str, str]]] """ Custom headers to be added to the email. The ``In-Reply-To``, ``References`` and ``Reply-To`` headers are set by Resend and cannot be @@ -150,7 +150,7 @@ class ReplyParams(TypedDict): names cannot be empty, and names and values cannot contain line breaks or null characters, or the request returns a 422. """ - attachments: NotRequired[List[Union[Attachment, RemoteAttachment]]] + attachments: NotRequired[Optional[List[Union[Attachment, RemoteAttachment]]]] """ Files to attach, as on ``Emails.send``. Each one needs ``content`` or ``path``. The ``content`` of all files can total at most 25 MB. A @@ -204,13 +204,13 @@ class ForwardParams(TypedDict): thread_id (str): The Thread ID. email_id (str): The Email ID of the message to forward. to (Union[str, List[str]]): The recipients. - cc (NotRequired[Union[str, List[str]]]): CC recipients. - bcc (NotRequired[Union[str, List[str]]]): BCC recipients. + cc (NotRequired[Optional[Union[str, List[str]]]]): CC recipients. + bcc (NotRequired[Optional[Union[str, List[str]]]]): BCC recipients. html (NotRequired[str]): An HTML note sent above the quoted original. text (NotRequired[str]): A plain-text note sent above the quoted original. subject (NotRequired[str]): The subject of the forward. - headers (NotRequired[Dict[str, str]]): Custom headers to be added to the email. - attachments (NotRequired[List[Union[Attachment, RemoteAttachment]]]): Files to attach. + headers (NotRequired[Optional[Dict[str, str]]]): Custom headers to be added to the email. + attachments (NotRequired[Optional[List[Union[Attachment, RemoteAttachment]]]]): Files to attach. """ inbox_id: str @@ -232,12 +232,12 @@ class ForwardParams(TypedDict): Must name at least one address. ``to``, ``cc`` and ``bcc`` combined cannot exceed 50 recipients. """ - cc: NotRequired[Union[str, List[str]]] + cc: NotRequired[Optional[Union[str, List[str]]]] """ CC recipient email address. For multiple addresses, send a list of strings. Counts toward the 50-recipient limit. """ - bcc: NotRequired[Union[str, List[str]]] + bcc: NotRequired[Optional[Union[str, List[str]]]] """ BCC recipient email address. For multiple addresses, send a list of strings. Counts toward the 50-recipient limit. @@ -255,7 +255,7 @@ class ForwardParams(TypedDict): The subject of the forward. Cannot contain a line break. When omitted, the thread subject is used, prefixed with ``Fwd:`` if it does not already start with it. """ - headers: NotRequired[Dict[str, str]] + headers: NotRequired[Optional[Dict[str, str]]] """ Custom headers to be added to the email. The ``In-Reply-To``, ``References`` and ``Reply-To`` headers are set by Resend and cannot be @@ -263,7 +263,7 @@ class ForwardParams(TypedDict): names cannot be empty, and names and values cannot contain line breaks or null characters, or the request returns a 422. """ - attachments: NotRequired[List[Union[Attachment, RemoteAttachment]]] + attachments: NotRequired[Optional[List[Union[Attachment, RemoteAttachment]]]] """ Files to attach, as on ``Emails.send``. Each one needs ``content`` or ``path``. They share one limit with the attachments of the original diff --git a/tests/test_nullable_inbox_params.py b/tests/test_nullable_inbox_params.py new file mode 100644 index 0000000..c9cc29f --- /dev/null +++ b/tests/test_nullable_inbox_params.py @@ -0,0 +1,35 @@ +from resend.inboxes.drafts._inbox_drafts import InboxDrafts +from resend.inboxes.threads.emails._inbox_thread_emails import \ + InboxThreadEmails + + +def test_optional_inbox_params_accept_none() -> None: + reply: InboxThreadEmails.ReplyParams = { + "inbox_id": "inbox_123", + "thread_id": "thread_123", + "email_id": "email_123", + "cc": None, + "bcc": None, + "headers": None, + "attachments": None, + } + forward: InboxThreadEmails.ForwardParams = { + "inbox_id": "inbox_123", + "thread_id": "thread_123", + "email_id": "email_123", + "to": "recipient@example.com", + "cc": None, + "bcc": None, + "headers": None, + "attachments": None, + } + create_draft: InboxDrafts.CreateParams = { + "inbox_id": "inbox_123", + "attachments": None, + } + send_draft: InboxDrafts.SendParams = {"headers": None} + + assert reply["cc"] is None + assert forward["bcc"] is None + assert create_draft["attachments"] is None + assert send_draft["headers"] is None From a0a76c8a038f0dc60347a9ab8748686a9f95c180 Mon Sep 17 00:00:00 2001 From: dielduarte Date: Thu, 29 Oct 2026 11:38:00 -0400 Subject: [PATCH 2/2] test(inboxes): assert nullable inbox params are sent as null Move the regression test into inboxes_test.py following the TestInboxesRequest pattern, and bump version to 2.50.1. --- resend/version.py | 2 +- tests/inboxes_test.py | 36 +++++++++++++++++++++++++++++ tests/test_nullable_inbox_params.py | 35 ---------------------------- 3 files changed, 37 insertions(+), 36 deletions(-) delete mode 100644 tests/test_nullable_inbox_params.py diff --git a/resend/version.py b/resend/version.py index 79982a3..b77813e 100644 --- a/resend/version.py +++ b/resend/version.py @@ -1,4 +1,4 @@ -__version__ = "2.50.0" +__version__ = "2.50.1" def get_version() -> str: diff --git a/tests/inboxes_test.py b/tests/inboxes_test.py index f045e81..93cc449 100644 --- a/tests/inboxes_test.py +++ b/tests/inboxes_test.py @@ -1075,6 +1075,42 @@ def test_drafts_paths_and_bodies(self) -> None: req = self.last_request() assert (req["method"], req["url"]) == ("post", f"{base}/{DRAFT_ID}/send") + def test_nullable_params_are_sent_as_null(self) -> None: + nulls = {"cc": None, "bcc": None, "headers": None, "attachments": None} + resend.Inboxes.Threads.Emails.reply( + { + "inbox_id": INBOX_ID, + "thread_id": THREAD_ID, + "email_id": EMAIL_ID, + "text": "Hi", + "cc": None, + "bcc": None, + "headers": None, + "attachments": None, + } + ) + assert self.last_request()["json"] == {"text": "Hi", **nulls} + + resend.Inboxes.Threads.Emails.forward( + { + "inbox_id": INBOX_ID, + "thread_id": THREAD_ID, + "email_id": EMAIL_ID, + "to": "colleague@example.org", + "cc": None, + "bcc": None, + "headers": None, + "attachments": None, + } + ) + assert self.last_request()["json"] == {"to": "colleague@example.org", **nulls} + + resend.Inboxes.Drafts.create({"inbox_id": INBOX_ID, "attachments": None}) + assert self.last_request()["json"] == {"attachments": None} + + resend.Inboxes.Drafts.send(INBOX_ID, DRAFT_ID, {"headers": None}) + assert self.last_request()["json"] == {"headers": None} + def test_sending_methods_send_the_idempotency_key(self) -> None: resend.Inboxes.Threads.Emails.reply( {"inbox_id": INBOX_ID, "thread_id": THREAD_ID, "email_id": EMAIL_ID, "text": "Hi"}, diff --git a/tests/test_nullable_inbox_params.py b/tests/test_nullable_inbox_params.py deleted file mode 100644 index c9cc29f..0000000 --- a/tests/test_nullable_inbox_params.py +++ /dev/null @@ -1,35 +0,0 @@ -from resend.inboxes.drafts._inbox_drafts import InboxDrafts -from resend.inboxes.threads.emails._inbox_thread_emails import \ - InboxThreadEmails - - -def test_optional_inbox_params_accept_none() -> None: - reply: InboxThreadEmails.ReplyParams = { - "inbox_id": "inbox_123", - "thread_id": "thread_123", - "email_id": "email_123", - "cc": None, - "bcc": None, - "headers": None, - "attachments": None, - } - forward: InboxThreadEmails.ForwardParams = { - "inbox_id": "inbox_123", - "thread_id": "thread_123", - "email_id": "email_123", - "to": "recipient@example.com", - "cc": None, - "bcc": None, - "headers": None, - "attachments": None, - } - create_draft: InboxDrafts.CreateParams = { - "inbox_id": "inbox_123", - "attachments": None, - } - send_draft: InboxDrafts.SendParams = {"headers": None} - - assert reply["cc"] is None - assert forward["bcc"] is None - assert create_draft["attachments"] is None - assert send_draft["headers"] is None