Repository: nylas/nylas-python

Issue:
Add metadata field to SendMessageRequest model
**Is your feature request related to a problem? Please describe.**
Nylas API allows for setting a `metadata` field on Calendar, Event, Message, and Draft objects, but this repo doesn't currently account for this ([documentation](https://developer.nylas.com/docs/api/v3/ecc/#overview--metadata)). It would be nice to be able to add metadata when sending Messages specifically (can see the [API allows it on requests here](https://developer.nylas.com/docs/api/v3/ecc/#post-/v3/grants/-grant_id-/messages/send)).

**Describe the solution you'd like**
It seems reasonable to add a metadata field onto the [SendMessageRequest model](https://github.com/nylas/nylas-python/blob/4e094c3dc1390178948573cee0f3d6838b288ea4/nylas/models/drafts.py#L150-L172) (and potentially the other "Send" models that metadata applies to).

**Describe alternatives you've considered**
Don't think there are any workarounds here.

**Additional context**
Would also be nice if Nylas API could include the metadata field on `message.created` webhooks -- it looks like they currently include all other Message fields _except_ the metadata which is pretty inconvenient since it forces consumers to have to make an additional http call to the GET message endpoint to see the metadata.


Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f96548..0b2bbd5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,9 +2,10 @@ nylas-python Changelog
 ======================
 
 Unreleased
---------------
+----------------
 * Add support for Scheduler APIs
 * Fixed attachment download response handling
+* Add metadata field support for drafts and messages through CreateDraftRequest model
 
 v6.4.0
 ----------------
diff --git a/examples/metadata_field_demo/README.md b/examples/metadata_field_demo/README.md
new file mode 100644
index 0000000..f5310f3
--- /dev/null
+++ b/examples/metadata_field_demo/README.md
@@ -0,0 +1,67 @@
+# Metadata Field Example
+
+This example demonstrates how to use metadata fields when creating drafts and sending messages using the Nylas Python SDK.
+
+## Features
+
+- Create drafts with custom metadata fields
+- Send messages with custom metadata fields
+- Error handling and environment variable configuration
+- Clear output and status messages
+
+## Prerequisites
+
+1. A Nylas account with API access
+2. Python 3.x installed
+3. Local installation of the Nylas Python SDK (this repository)
+
+## Setup
+
+1. Install the SDK in development mode from the repository root:
+```bash
+cd /path/to/nylas-python
+pip install -e .
+```
+
+2. Set your environment variables:
+```bash
+export NYLAS_API_KEY="your_api_key"
+export NYLAS_GRANT_ID="your_grant_id"
+export TEST_EMAIL="recipient@example.com"  # Optional
+```
+
+3. Run the example from the repository root:
+```bash
+python examples/metadata_field_demo/metadata_example.py
+```
+
+## Example Output
+
+```
+Demonstrating Metadata Field Usage
+=================================
+
+1. Creating draft with metadata...
+✓ Created draft with ID: draft-abc123
+  Request ID: req-xyz789
+
+2. Sending message with metadata...
+✓ Sent message with ID: msg-def456
+  Request ID: req-uvw321
+
+Example completed successfully!
+```
+
+## Error Handling
+
+The example includes proper error handling for:
+- Missing environment variables
+- API authentication errors
+- Draft creation failures
+- Message sending failures
+
+## Documentation
+
+For more information about the Nylas Python SDK and its features, visit:
+- [Nylas Python SDK Documentation](https://developer.nylas.com/docs/sdks/python/)
+- [Nylas API Reference](https://developer.nylas.com/docs/api/)
diff --git a/examples/metadata_field_demo/metadata_example.py b/examples/metadata_field_demo/metadata_example.py
new file mode 100644
index 0000000..737dbaf
--- /dev/null
+++ b/examples/metadata_field_demo/metadata_example.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+"""
+Nylas SDK Example: Using Metadata Fields with Drafts and Messages
+
+This example demonstrates how to use metadata fields when creating drafts
+and sending messages using the Nylas Python SDK.
+
+Required Environment Variables:
+    NYLAS_API_KEY: Your Nylas API key
+    NYLAS_GRANT_ID: Your Nylas grant ID
+    TEST_EMAIL: Email address for sending test messages (optional)
+
+Usage:
+    First, install the SDK in development mode:
+    cd /path/to/nylas-python
+    pip install -e .
+
+    Then set environment variables and run:
+    export NYLAS_API_KEY="your_api_key"
+    export NYLAS_GRANT_ID="your_grant_id"
+    export TEST_EMAIL="recipient@example.com"
+    python examples/metadata_field_demo/metadata_example.py
+"""
+
+import os
+import sys
+from typing import Dict, Any, Optional
+
+# Import from local nylas package
+from nylas import Client
+from nylas.models.errors import NylasApiError
+
+
+def get_env_or_exit(var_name: str, required: bool = True) -> Optional[str]:
+    """Get an environment variable or exit if required and not found."""
+    value = os.getenv(var_name)
+    if required and not value:
+        print(f"Error: {var_name} environment variable is required")
+        sys.exit(1)
+    return value
+
+
+def create_draft_with_metadata(
+    client: Client, grant_id: str, metadata: Dict[str, Any], recipient: str
+) -> str:
+    """Create a draft message with metadata fields."""
+    try:
+        draft_request = {
+            "subject": "Test Draft with Metadata",
+            "to": [{"email": recipient}],
+            "body": "This is a test draft with metadata fields.",
+            "metadata": metadata
+        }
+
+        draft, request_id = client.drafts.create(
+            identifier=grant_id,
+            request_body=draft_request
+        )
+        print(f"✓ Created draft with ID: {draft.id}")
+        print(f"  Request ID: {request_id}")
+        return draft.id
+    except NylasApiError as e:
+        print(f"✗ Failed to create draft: {e}")
+        sys.exit(1)
+
+
+def send_message_with_metadata(
+    client: Client, grant_id: str, metadata: Dict[str, Any], recipient: str
+) -> str:
+    """Send a message directly with metadata fields."""
+    try:
+        message_request = {
+            "subject": "Test Message with Metadata",
+            "to": [{"email": recipient}],
+            "body": "This is a test message with metadata fields.",
+            "metadata": metadata
+        }
+
+        message, request_id = client.messages.send(
+            identifier=grant_id,
+            request_body=message_request
+        )
+        print(f"✓ Sent message with ID: {message.id}")
+        print(f"  Request ID: {request_id}")
+
+        return message.id
+    except NylasApiError as e:
+        print(f"✗ Failed to send message: {e}")
+        sys.exit(1)
+
+
+def main():
+    """Main function demonstrating metadata field usage."""
+    # Get required environment variables
+    api_key = get_env_or_exit("NYLAS_API_KEY")
+    grant_id = get_env_or_exit("NYLAS_GRANT_ID")
+    recipient = get_env_or_exit("TEST_EMAIL", required=False) or "recipient@example.com"
+
+    # Initialize Nylas client
+    client = Client(
+        api_key=api_key,
+    )
+
+    # Example metadata
+    metadata = {
+        "campaign_id": "example-123",
+        "user_id": "user-456",
+        "custom_field": "test-value"
+    }
+
+    print("\nDemonstrating Metadata Field Usage")
+    print("=================================")
+
+    # Create a draft with metadata
+    print("\n1. Creating draft with metadata...")
+    draft_id = create_draft_with_metadata(client, grant_id, metadata, recipient)
+
+    # Send a message with metadata
+    print("\n2. Sending message with metadata...")
+    message_id = send_message_with_metadata(client, grant_id, metadata, recipient)
+
+    print("\nExample completed successfully!")
+
+    # Get the draft and message to demonstrate metadata retrieval
+    draft = client.drafts.find(identifier=grant_id, draft_id=draft_id)
+    message = client.messages.find(identifier=grant_id, message_id=message_id)
+
+    print("\nRetrieved Draft Metadata:")
+    print("-------------------------")
+    print(draft.data)
+
+    print("\nRetrieved Message Metadata:")
+    print("---------------------------")
+    print(message.data)
+
+if __name__ == "__main__":
+    main()
diff --git a/nylas/models/drafts.py b/nylas/models/drafts.py
index f77a125..f6e3803 100644
--- a/nylas/models/drafts.py
+++ b/nylas/models/drafts.py
@@ -1,5 +1,5 @@
 from dataclasses import dataclass
-from typing import List, get_type_hints
+from typing import List, Dict, Any, get_type_hints
 
 from dataclasses_json import dataclass_json
 from typing_extensions import TypedDict, NotRequired
@@ -87,6 +87,7 @@ class CreateDraftRequest(TypedDict):
         reply_to_message_id: The ID of the message that you are replying to.
         tracking_options: Options for tracking opens, links, and thread replies.
         custom_headers: Custom headers to add to the message.
+        metadata: A dictionary of key-value pairs storing additional data.
     """
 
     body: NotRequired[str]
@@ -101,6 +102,7 @@ class CreateDraftRequest(TypedDict):
     reply_to_message_id: NotRequired[str]
     tracking_options: NotRequired[TrackingOptions]
     custom_headers: NotRequired[List[CustomHeader]]
+    metadata: NotRequired[Dict[str, Any]]
 
 
 UpdateDraftRequest = CreateDraftRequest
diff --git a/nylas/models/messages.py b/nylas/models/messages.py
index 7d5d405..b6f0bbd 100644
--- a/nylas/models/messages.py
+++ b/nylas/models/messages.py
@@ -80,6 +80,7 @@ class Message:
     date: Optional[int] = None
     schedule_id: Optional[str] = None
     send_at: Optional[int] = None
+    metadata: Optional[Dict[str, Any]] = None
 
 
 # Need to use Functional typed dicts because "from" and "in" are Python


Test patch:
diff --git a/tests/resources/test_drafts.py b/tests/resources/test_drafts.py
index ecd51eb..8613a2f 100644
--- a/tests/resources/test_drafts.py
+++ b/tests/resources/test_drafts.py
@@ -2,6 +2,7 @@ from unittest.mock import patch, Mock
 
 from nylas.models.drafts import Draft
 from nylas.resources.drafts import Drafts
+from nylas.resources.messages import Messages
 
 
 class TestDraft:
@@ -143,6 +144,27 @@ class TestDraft:
             overrides=None,
         )
 
+    def test_create_draft_with_metadata(self, http_client_response):
+        drafts = Drafts(http_client_response)
+        request_body = {
+            "subject": "Hello from Nylas!",
+            "to": [{"name": "Jon Snow", "email": "jsnow@gmail.com"}],
+            "cc": [{"name": "Arya Stark", "email": "astark@gmail.com"}],
+            "body": "This is the body of my draft message.",
+            "metadata": {"custom_field": "value", "another_field": 123}
+        }
+
+        drafts.create(identifier="abc-123", request_body=request_body)
+
+        http_client_response._execute.assert_called_once_with(
+            "POST",
+            "/v3/grants/abc-123/drafts",
+            None,
+            None,
+            request_body,
+            overrides=None,
+        )
+
     def test_create_draft_small_attachment(self, http_client_response):
         drafts = Drafts(http_client_response)
         request_body = {
@@ -349,6 +371,25 @@ class TestDraft:
             method="POST", path="/v3/grants/abc-123/drafts/draft-123", overrides=None
         )
 
+    def test_send_message_with_metadata(self, http_client_response):
+        messages = Messages(http_client_response)
+        request_body = {
+            "subject": "Hello from Nylas!",
+            "to": [{"name": "Jon Snow", "email": "jsnow@gmail.com"}],
+            "body": "This is the body of my message.",
+            "metadata": {"custom_field": "value", "another_field": 123}
+        }
+
+        messages.send(identifier="abc-123", request_body=request_body)
+
+        http_client_response._execute.assert_called_once_with(
+            method="POST",
+            path="/v3/grants/abc-123/messages/send",
+            request_body=request_body,
+            data=None,
+            overrides=None,
+        )
+
     def test_send_draft_encoded_id(self, http_client_response):
         drafts = Drafts(http_client_response)
 
diff --git a/tests/resources/test_messages.py b/tests/resources/test_messages.py
index 1af9a95..5e9b372 100644
--- a/tests/resources/test_messages.py
+++ b/tests/resources/test_messages.py
@@ -37,6 +37,7 @@ class TestMessage:
             "thread_id": "1t8tv3890q4vgmwq6pmdwm8qgsaer",
             "to": [{"name": "Jon Snow", "email": "j.snow@example.com"}],
             "unread": True,
+            "metadata": {"custom_field": "value", "another_field": 123},
         }
 
         message = Message.from_dict(message_json)
@@ -64,6 +65,341 @@ class TestMessage:
         assert message.thread_id == "1t8tv3890q4vgmwq6pmdwm8qgsaer"
         assert message.to == [{"name": "Jon Snow", "email": "j.snow@example.com"}]
         assert message.unread is True
+        assert message.metadata == {"custom_field": "value", "another_field": 123}
+
+    def test_list_messages(self, http_client_list_response):
+        messages = Messages(http_client_list_response)
+
+        messages.list(identifier="abc-123")
+
+        http_client_list_response._execute.assert_called_once_with(
+            "GET", "/v3/grants/abc-123/messages", None, None, None, overrides=None
+        )
+
+    def test_list_messages_with_query_params(self, http_client_list_response):
+        messages = Messages(http_client_list_response)
+
+        messages.list(
+            identifier="abc-123",
+            query_params={
+                "subject": "Hello from Nylas!",
+            },
+        )
+
+        http_client_list_response._execute.assert_called_once_with(
+            "GET",
+            "/v3/grants/abc-123/messages",
+            None,
+            {
+                "subject": "Hello from Nylas!",
+            },
+            None,
+            overrides=None,
+        )
+
+    def test_find_message(self, http_client_response):
+        messages = Messages(http_client_response)
+
+        messages.find(identifier="abc-123", message_id="message-123")
+
+        http_client_response._execute.assert_called_once_with(
+            "GET",
+            "/v3/grants/abc-123/messages/message-123",
+            None,
+            None,
+            None,
+            overrides=None,
+        )
+
+    def test_find_message_encoded_id(self, http_client_response):
+        messages = Messages(http_client_response)
+
+        messages.find(
+            identifier="abc-123",
+            message_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/0BgqPmf+AQAAAAA=@example.com>",
+        )
+
+        http_client_response._execute.assert_called_once_with(
+            "GET",
+            "/v3/grants/abc-123/messages/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
+            None,
+            None,
+            None,
+            overrides=None,
+        )
+
+    def test_find_message_with_query_params(self, http_client_response):
+        messages = Messages(http_client_response)
+
+        messages.find(
+            identifier="abc-123",
+            message_id="message-123",
+            query_params={"fields": "standard"},
+        )
+
+        http_client_response._execute.assert_called_once_with(
+            "GET",
+            "/v3/grants/abc-123/messages/message-123",
+            None,
+            {"fields": "standard"},
+            None,
+            overrides=None,
+        )
+
+    def test_update_message(self, http_client_response):
+        messages = Messages(http_client_response)
+        request_body = {
+            "starred": True,
+            "unread": False,
+            "folders": ["folder-123"],
+            "metadata": {"foo": "bar"},
+        }
+
+        messages.update(
+            identifier="abc-123",
+            message_id="message-123",
+            request_body=request_body,
+        )
+
+        http_client_response._execute.assert_called_once_with(
+            "PUT",
+            "/v3/grants/abc-123/messages/message-123",
+            None,
+            None,
+            request_body,
+            overrides=None,
+        )
+
+    def test_update_message_encoded_id(self, http_client_response):
+        messages = Messages(http_client_response)
+        request_body = {
+            "starred": True,
+            "unread": False,
+            "folders": ["folder-123"],
+            "metadata": {"foo": "bar"},
+        }
+
+        messages.update(
+            identifier="abc-123",
+            message_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/0BgqPmf+AQAAAAA=@example.com>",
+            request_body=request_body,
+        )
+
+        http_client_response._execute.assert_called_once_with(
+            "PUT",
+            "/v3/grants/abc-123/messages/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
+            None,
+            None,
+            request_body,
+            overrides=None,
+        )
+
+    def test_destroy_message(self, http_client_delete_response):
+        messages = Messages(http_client_delete_response)
+
+        messages.destroy(identifier="abc-123", message_id="message-123")
+
+        http_client_delete_response._execute.assert_called_once_with(
+            "DELETE",
+            "/v3/grants/abc-123/messages/message-123",
+            None,
+            None,
+            None,
+            overrides=None,
+        )
+
+    def test_destroy_message_encoded_id(self, http_client_delete_response):
+        messages = Messages(http_client_delete_response)
+
+        messages.destroy(
+            identifier="abc-123",
+            message_id="<!&!AAAAAAAAAAAuAAAAAAAAABQ/wHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ/T4N/0BgqPmf+AQAAAAA=@example.com>",
+        )
+
+        http_client_delete_response._execute.assert_called_once_with(
+            "DELETE",
+            "/v3/grants/abc-123/messages/%3C%21%26%21AAAAAAAAAAAuAAAAAAAAABQ%2FwHZyqaNCptfKg5rnNAoBAMO2jhD3dRHOtM0AqgC7tuYAAAAAAA4AABAAAACTn3BxdTQ%2FT4N%2F0BgqPmf%2BAQAAAAA%3D%40example.com%3E",
+            None,
+            None,
+            None,
+            overrides=None,
+        )
+
+    def test_send_message(self, http_client_response):
+        messages = Messages(http_client_response)
+        request_body = {
+            "subject": "Hello from Nylas!",
+            "to": [{"name": "Jon Snow", "email": "jsnow@gmail.com"}],
+            "cc": [{"name": "Arya Stark", "email": "astark@gmail.com"}],
+            "body": "This is the body of my draft message.",
+            "metadata": {"custom_field": "value", "another_field": 123},
+        }
+
+        messages.send(identifier="abc-123", request_body=request_body)
+
+        http_client_response._execute.assert_called_once_with(
+            method="POST",
+            path="/v3/grants/abc-123/messages/send",
+            request_body=request_body,
+            data=None,
+            overrides=None,
+        )
+
+    def test_send_message_small_attachment(self, http_client_response):
+        messages = Messages(http_client_response)
+        request_body = {
+            "subject": "Hello from Nylas!",
+            "to": [{"name": "Jon Snow", "email": "jsnow@gmail.com"}],
+            "cc": [{"name": "Arya Stark", "email": "astark@gmail.com"}],
+            "body": "This is the body of my draft message.",
+            "attachments": [
+                {
+                    "filename": "file1.txt",
+                    "content_type": "text/plain",
+                    "content": "this is a file",
+                    "size": 3,
+                },
+            ],
+            "metadata": {"custom_field": "value", "another_field": 123},
+        }
+
+        messages.send(identifier="abc-123", request_body=request_body)
+
+        http_client_response._execute.assert_called_once_with(
+            method="POST",
+            path="/v3/grants/abc-123/messages/send",
+            request_body=request_body,
+            data=None,
+            overrides=None,
+        )
+
+    def test_send_message_large_attachment(self, http_client_response):
+        messages = Messages(http_client_response)
+        mock_encoder = Mock()
+        request_body = {
+            "subject": "Hello from Nylas!",
+            "to": [{"name": "Jon Snow", "email": "jsnow@gmail.com"}],
+            "cc": [{"name": "Arya Stark", "email": "astark@gmail.com"}],
+            "body": "This is the body of my draft message.",
+            "attachments": [
+                {
+                    "filename": "file1.txt",
+                    "content_type": "text/plain",
+                    "content": "this is a file",
+                    "size": 3 * 1024 * 1024,
+                },
+            ],
+            "metadata": {"custom_field": "value", "another_field": 123},
+        }
+
+        with patch(
+            "nylas.resources.messages._build_form_request", return_value=mock_encoder
+        ):
+            messages.send(identifier="abc-123", request_body=request_body)
+
+            http_client_response._execute.assert_called_once_with(
+                method="POST",
+                path="/v3/grants/abc-123/messages/send",
+                request_body=None,
+                data=mock_encoder,
+                overrides=None,
+            )
+
+    def test_list_scheduled_messages(self, http_client_list_scheduled_messages):
+        messages = Messages(http_client_list_scheduled_messages)
+
+        res = messages.list_scheduled_messages(identifier="abc-123")
+
+        http_client_list_scheduled_messages._execute.assert_called_once_with(
+            method="GET", path="/v3/grants/abc-123/messages/schedules", overrides=None
+        )
+        assert res.request_id == "dd3ec9a2-8f15-403d-b269-32b1f1beb9f5"
+        assert len(res.data) == 2
+        assert res.data[0].schedule_id == "8cd56334-6d95-432c-86d1-c5dab0ce98be"
+        assert res.data[0].status.code == "pending"
+        assert res.data[0].status.description == "schedule send awaiting send at time"
+        assert res.data[1].schedule_id == "rb856334-6d95-432c-86d1-c5dab0ce98be"
+        assert res.data[1].status.code == "success"
+        assert res.data[1].status.description == "schedule send succeeded"
+        assert res.data[1].close_time == 1690579819
+
+    def test_find_scheduled_message(self, http_client_response):
+        messages = Messages(http_client_response)
+
+        messages.find_scheduled_message(
+            identifier="abc-123", schedule_id="schedule-123"
+        )
+
+        http_client_response._execute.assert_called_once_with(
+            method="GET",
+            path="/v3/grants/abc-123/messages/schedules/schedule-123",
+            overrides=None,
+        )
+
+    def test_stop_scheduled_message(self, http_client_response):
+        messages = Messages(http_client_response)
+
+        messages.stop_scheduled_message(
+            identifier="abc-123", schedule_id="schedule-123"
+        )
+
+        http_client_response._execute.assert_called_once_with(
+            method="DELETE",
+            path="/v3/grants/abc-123/messages/schedules/schedule-123",
+            overrides=None,
+        )
+
+    def test_clean_messages(self, http_client_clean_messages):
+        messages = Messages(http_client_clean_messages)
+        request_body = {
+            "message_id": ["message-1", "message-2"],
+            "ignore_images": True,
+            "ignore_links": True,
+            "ignore_tables": True,
+            "images_as_markdown": True,
+            "remove_conclusion_phrases": True,
+        }
+
+        response = messages.clean_messages(
+            identifier="abc-123",
+            request_body=request_body,
+        )
+
+        http_client_clean_messages._execute.assert_called_once_with(
+            method="PUT",
+            path="/v3/grants/abc-123/messages/clean",
+            request_body=request_body,
+            overrides=None,
+        )
+
+        # Assert the conversation field, and the typical message fields serialize properly
+        assert len(response.data) == 2
+        assert response.data[0].body == "Hello, I just sent a message using Nylas!"
+        assert response.data[0].from_ == [
+            {"name": "Daenerys Targaryen", "email": "daenerys.t@example.com"}
+        ]
+        assert response.data[0].object == "message"
+        assert response.data[0].id == "message-1"
+        assert response.data[0].grant_id == "41009df5-bf11-4c97-aa18-b285b5f2e386"
+        assert response.data[0].conversation == "cleaned example"
+        assert response.data[1].conversation == "another example"
+        assert message.folders[0] == "8l6c4d11y1p4dm4fxj52whyr9"
+        assert message.folders[1] == "d9zkcr2tljpu3m4qpj7l2hbr0"
+        assert message.from_ == [
+            {"name": "Daenerys Targaryen", "email": "daenerys.t@example.com"}
+        ]
+        assert message.grant_id == "41009df5-bf11-4c97-aa18-b285b5f2e386"
+        assert message.id == "5d3qmne77v32r8l4phyuksl2x"
+        assert message.object == "message"
+        assert message.reply_to == [
+            {"name": "Daenerys Targaryen", "email": "daenerys.t@example.com"}
+        ]
+        assert message.snippet == "Hello, I just sent a message using Nylas!"
+        assert message.starred is True
+        assert message.subject == "Hello from Nylas!"
+        assert message.thread_id == "1t8tv3890q4vgmwq6pmdwm8qgsaer"
+        assert message.to == [{"name": "Jon Snow", "email": "j.snow@example.com"}]
+        assert message.unread is True
 
     def test_list_messages(self, http_client_list_response):
         messages = Messages(http_client_list_response)