Repository: nylas/nylas-python

Issue:
Python SDK (Nylas API v3) Error Messages
**Describe the bug**
Testing the Python SDK and receiving error messages for various calls, will include source code and errors below.

#### POST /v3/grants/{grant_id}/calendars/free-busy
_code sample:_
```
# SDK doesn't accept identifer
# https://nylas-python-sdk-reference.pages.dev/reference/resources/calendars/#resources.calendars.Calendars.get_availability

from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
    os.environ.get('NYLAS_API_KEY'),
    os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("GRANT_ID")
email = os.environ.get("EMAIL")

free_busy = nylas.calendars.get_free_busy(
  grant_id,
  request_body={
    "start_time": 1630435200,
    "end_time": 1630521600,
    "emails": [email]
  }
)

print(free_busy)
```

_error message_
```

# Traceback (most recent call last):
#   File "/Users/ram/Documents/v3-code-samples-node/src/get-free-busy.py", line 19, in <module>
#     free_busy = nylas.calendars.get_free_busy(
#                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/resources/calendars.py", line 155, in get_free_busy
#     return Response.from_dict(json_response, GetFreeBusyResponse)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/models/response.py", line 46, in from_dict
#     data=generic_type.from_dict(resp["data"]),
#          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/api.py", line 70, in from_dict
#     return _decode_dataclass(cls, kvs, infer_missing)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 150, in _decode_dataclass
#     kvs = {decode_names.get(k, k): v for k, v in kvs.items()}
#                                                  ^^^^^^^^^
# AttributeError: 'list' object has no attribute 'items'
```

#### GET /v3/grants/{grant_id}/messages/schedules
_code_
```
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
    os.environ.get('NYLAS_API_KEY'),
    os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("GRANT_ID")

messages = nylas.messages.list_scheduled_messages(
  grant_id
)

print(messages)
```
_error message_
```
# Traceback (most recent call last):
#   File "/Users/ram/Documents/v3-code-samples-node/src/get-message-schedules.py", line 15, in <module>
#     messages = nylas.messages.list_scheduled_messages(
#                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/resources/messages.py", line 159, in list_scheduled_messages
#     return Response.from_dict(json_response, ScheduledMessagesList)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/models/response.py", line 46, in from_dict
#     data=generic_type.from_dict(resp["data"]),
#          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/api.py", line 70, in from_dict
#     return _decode_dataclass(cls, kvs, infer_missing)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 150, in _decode_dataclass
#     kvs = {decode_names.get(k, k): v for k, v in kvs.items()}
#                                                  ^^^^^^^^^
# AttributeError: 'list' object has no attribute 'items'
```

#### GET /v3/grants/{grant_id}/messages/schedules/
_code_
```
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
    os.environ.get('NYLAS_API_KEY'),
    os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("GRANT_ID")
schedule_id = os.environ.get("SCHEDULE_ID")

event = nylas.messages.find_scheduled_message(
  grant_id,
  schedule_id,
)

print(event)
```
_error message_
```
# Traceback (most recent call last):
#   File "/Users/ram/Documents/v3-code-samples-node/src/get-message-scheduled-by-id.py", line 16, in <module>
#     event = nylas.messages.find_scheduled_message(
#             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/resources/messages.py", line 179, in find_scheduled_message
#     return Response.from_dict(json_response, ScheduledMessage)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/models/response.py", line 46, in from_dict
#     data=generic_type.from_dict(resp["data"]),
#          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/api.py", line 70, in from_dict
#     return _decode_dataclass(cls, kvs, infer_missing)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 224, in _decode_dataclass
#     init_kwargs[field.name] = _support_extended_types(field_type,
#                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 251, in _support_extended_types
#     else field_type(field_value))
#          ^^^^^^^^^^^^^^^^^^^^^^^
# ValueError: invalid literal for int() with base 10: '846cab57-cfa6-493e-9fb3-a448b12b9406'
```

#### POST /v3/grants/{grant_id}/messages/smart-compose
_code_
```
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
    os.environ.get('NYLAS_API_KEY'),
    os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("GRANT_ID")
email = os.environ.get("EMAIL")

message = nylas.messages.smart_compose.compose_message(
  grant_id,
  request_body={
    "prompt": "Tell my colleague how we can use Nylas APIs",
  }
)

print(message)
```
_error message_
```
# Traceback (most recent call last):
#   File "/Users/ram/Documents/v3-code-samples-node/src/smart-compose.py", line 16, in <module>
#     message = nylas.messages.smart_compose.compose_message(
#               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/resources/smart_compose.py", line 25, in compose_message
#     return self._http_client.execute(
#            ^^^^^^^^^^^^^^^^^^^^^^^^^
# AttributeError: 'HttpClient' object has no attribute 'execute'. Did you mean: '_execute'?
```

#### POST /v3/grants/{grant_id}/messages/{message_id}/smart-compose
_code_
```
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
    os.environ.get('NYLAS_API_KEY'),
    os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("GRANT_ID")
message_id = os.environ.get("MESSAGE_ID")

message = nylas.messages.smart_compose.compose_message_reply(
  grant_id,
  message_id,
  request_body={
    "prompt": "Respond to the email",
  }
)

print(message)
```
_error message_
```
# Traceback (most recent call last):
#   File "/Users/ram/Documents/v3-code-samples-node/src/smart-compose-response.py", line 16, in <module>
#     message = nylas.messages.smart_compose.compose_message_reply(
#               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/resources/smart_compose.py", line 46, in compose_message_reply
#     return self._http_client.execute(
#            ^^^^^^^^^^^^^^^^^^^^^^^^^
# AttributeError: 'HttpClient' object has no attribute 'execute'. Did you mean: '_execute'?
```

#### GET /v3/grants/{grant_id}/drafts
#### GET /v3/grants/{grant_id}/drafts/
#### PUT /v3/grants/{grant_id}/drafts/
#### DELETE /v3/grants/{grant_id}/drafts/
_code_
```
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
    os.environ.get('NYLAS_API_KEY'),
    os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("GRANT_ID")

drafts = nylas.drafts.list(
  grant_id,
)

print(drafts)
```
_error message_
```
# python3 retrieve-all-drafts.py
# Traceback (most recent call last):
#   File "/Users/ram/Documents/v3-code-samples-node/src/retrieve-all-drafts.py", line 15, in <module>
#     drafts = nylas.drafts.list(
#              ^^^^^^^^^^^^
# AttributeError: 'Client' object has no attribute 'drafts'. Did you mean: 'grants'?
```

#### GET /v3/grants/{grant_id}/contacts
_code_
```
from dotenv import load_dotenv
load_dotenv()

import os
import sys
from nylas import Client

nylas = Client(
    os.environ.get('NYLAS_API_KEY'),
    os.environ.get('NYLAS_API_URI')
)

grant_id = os.environ.get("GRANT_ID")

contacts = nylas.contacts.list(
  grant_id,
)

print(contacts)
```
_error message_
```

# Traceback (most recent call last):
#   File "/Users/ram/Documents/v3-code-samples-node/src/retrieve-all-contacts.py", line 15, in <module>
#     contacts = nylas.contacts.list(
#                ^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/resources/contacts.py", line 41, in list
#     return super(Contacts, self).list(
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/handler/api_resources.py", line 15, in list
#     return ListResponse.from_dict(response_json, response_type)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/nylas/models/response.py", line 93, in from_dict
#     converted_data.append(generic_type.from_dict(item))
#                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/api.py", line 70, in from_dict
#     return _decode_dataclass(cls, kvs, infer_missing)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 220, in _decode_dataclass
#     init_kwargs[field.name] = _decode_generic(field_type,
#                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 311, in _decode_generic
#     res = _decode_generic(type_arg, value, infer_missing)
#           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 291, in _decode_generic
#     xs = _decode_items(_get_type_arg_param(type_, 0), value, infer_missing)
#          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 399, in _decode_items
#     return list(_decode_item(type_args, x) for x in xs)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 399, in <genexpr>
#     return list(_decode_item(type_args, x) for x in xs)
#                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 372, in _decode_item
#     return _decode_dataclass(type_arg, x, infer_missing)
#            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 220, in _decode_dataclass
#     init_kwargs[field.name] = _decode_generic(field_type,
#                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 311, in _decode_generic
#     res = _decode_generic(type_arg, value, infer_missing)
#           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Users/ram/Documents/v3-code-samples-node/src/env/lib/python3.12/site-packages/dataclasses_json/core.py", line 272, in _decode_generic
#     res = type_(value)
#           ^^^^^^^^^^^^
#   File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/enum.py", line 744, in __call__
#     return cls.__new__(cls, value)
#            ^^^^^^^^^^^^^^^^^^^^^^^
#   File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/enum.py", line 1158, in __new__
#     raise ve_exc
# ValueError: 'personal' is not a valid ContactType
```

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/nylas/nylas-python/pull/350?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nylas) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 98.87%. Comparing base [(`5564fdb`)](https://app.codecov.io/gh/nylas/nylas-python/commit/5564fdbbc57db90f9d76f7b81607765f9a9f6cdc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nylas) to head [(`4c66263`)](https://app.codecov.io/gh/nylas/nylas-python/pull/350?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nylas).


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #350      +/-   ##
==========================================
- Coverage   98.89%   98.87%   -0.02%     
==========================================
  Files          45       45              
  Lines        1630     1606      -24     
==========================================
- Hits         1612     1588      -24     
  Misses         18       18              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/nylas/nylas-python/pull/350?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nylas).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=nylas).


Patch:
diff --git a/.pylintrc b/.pylintrc
index 2db5495..f789fc6 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -29,3 +29,6 @@ generated-members=
     CodeExchangeResponse.from_dict,
     NylasApiErrorResponse.from_dict,
     NylasOAuthErrorResponse.from_dict,
+    FreeBusyError.from_dict,
+    FreeBusy.from_dict,
+    ScheduledMessage.from_dict,
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 790777b..8d55e9e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ Unreleased
 * Added support for `round_to` field in availability response
 * Added support for `attributes` field in folder model
 * Added support for icloud as an auth provider
+* Fixed issue with free busy and scheduled message responses not being deserialized correctly
 * Removed `client_id` from `detect_provider()`
 
 v6.0.1
diff --git a/nylas/models/messages.py b/nylas/models/messages.py
index bc7f531..3e1c82a 100644
--- a/nylas/models/messages.py
+++ b/nylas/models/messages.py
@@ -182,24 +182,11 @@ class ScheduledMessage:
         close_time: The time the message was sent or failed to send, in epoch time.
     """
 
-    schedule_id: int
+    schedule_id: str
     status: ScheduledMessageStatus
     close_time: Optional[int] = None
 
 
-@dataclass_json
-@dataclass
-class ScheduledMessagesList:
-    """
-    A list of scheduled messages.
-
-    Attributes:
-        schedules: The list of scheduled messages.
-    """
-
-    schedules: List[ScheduledMessage]
-
-
 @dataclass_json
 @dataclass
 class StopScheduledMessageResponse:
diff --git a/nylas/resources/calendars.py b/nylas/resources/calendars.py
index af27661..f74a3c4 100644
--- a/nylas/resources/calendars.py
+++ b/nylas/resources/calendars.py
@@ -1,3 +1,5 @@
+from typing import List
+
 from nylas.handler.api_resources import (
     ListableApiResource,
     FindableApiResource,
@@ -6,7 +8,12 @@ from nylas.handler.api_resources import (
     DestroyableApiResource,
 )
 from nylas.models.availability import GetAvailabilityResponse, GetAvailabilityRequest
-from nylas.models.free_busy import GetFreeBusyResponse, GetFreeBusyRequest
+from nylas.models.free_busy import (
+    GetFreeBusyResponse,
+    GetFreeBusyRequest,
+    FreeBusyError,
+    FreeBusy,
+)
 from nylas.models.calendars import (
     Calendar,
     CreateCalendarRequest,
@@ -145,7 +152,7 @@ class Calendars(
 
     def get_free_busy(
         self, identifier: str, request_body: GetFreeBusyRequest
-    ) -> Response[GetFreeBusyResponse]:
+    ) -> Response[List[GetFreeBusyResponse]]:
         """
         Get free/busy info for a Calendar.
 
@@ -162,4 +169,12 @@ class Calendars(
             request_body=request_body,
         )
 
-        return Response.from_dict(json_response, GetFreeBusyResponse)
+        data = []
+        request_id = json_response["request_id"]
+        for item in json_response["data"]:
+            if item.get("object") == "error":
+                data.append(FreeBusyError.from_dict(item))
+            else:
+                data.append(FreeBusy.from_dict(item))
+
+        return Response(data, request_id)
diff --git a/nylas/resources/messages.py b/nylas/resources/messages.py
index a0fc0c8..e627957 100644
--- a/nylas/resources/messages.py
+++ b/nylas/resources/messages.py
@@ -1,4 +1,4 @@
-from typing import Optional
+from typing import Optional, List
 
 from nylas.handler.api_resources import (
     ListableApiResource,
@@ -12,7 +12,6 @@ from nylas.models.messages import (
     ListMessagesQueryParams,
     FindMessageQueryParams,
     UpdateMessageRequest,
-    ScheduledMessagesList,
     ScheduledMessage,
     StopScheduledMessageResponse,
 )
@@ -148,7 +147,7 @@ class Messages(
 
     def list_scheduled_messages(
         self, identifier: str
-    ) -> Response[ScheduledMessagesList]:
+    ) -> Response[List[ScheduledMessage]]:
         """
         Retrieve your scheduled messages.
 
@@ -163,7 +162,12 @@ class Messages(
             path=f"/v3/grants/{identifier}/messages/schedules",
         )
 
-        return Response.from_dict(json_response, ScheduledMessagesList)
+        data = []
+        request_id = json_response["request_id"]
+        for item in json_response["data"]:
+            data.append(ScheduledMessage.from_dict(item))
+
+        return Response(data, request_id)
 
     def find_scheduled_message(
         self, identifier: str, schedule_id: str


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index b89203d..41542ea 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -132,3 +132,60 @@ def http_client_token_info():
         },
     }
     return mock_http_client
+
+
+@pytest.fixture
+def http_client_free_busy():
+    mock_http_client = Mock()
+    mock_http_client._execute.return_value = {
+        "request_id": "dd3ec9a2-8f15-403d-b269-32b1f1beb9f5",
+        "data": [
+            {
+                "email": "user1@example.com",
+                "time_slots": [
+                    {
+                        "start_time": 1690898400,
+                        "end_time": 1690902000,
+                        "status": "busy",
+                        "object": "time_slot",
+                    },
+                    {
+                        "start_time": 1691064000,
+                        "end_time": 1691067600,
+                        "status": "busy",
+                        "object": "time_slot",
+                    },
+                ],
+                "object": "free_busy",
+            },
+            {
+                "email": "user2@example.com",
+                "error": "Unable to resolve e-mail address user2@example.com to an Active Directory object.",
+                "object": "error",
+            },
+        ],
+    }
+    return mock_http_client
+
+
+@pytest.fixture
+def http_client_list_scheduled_messages():
+    mock_http_client = Mock()
+    mock_http_client._execute.return_value = {
+        "request_id": "dd3ec9a2-8f15-403d-b269-32b1f1beb9f5",
+        "data": [
+            {
+                "schedule_id": "8cd56334-6d95-432c-86d1-c5dab0ce98be",
+                "status": {
+                    "code": "pending",
+                    "description": "schedule send awaiting send at time",
+                },
+            },
+            {
+                "schedule_id": "rb856334-6d95-432c-86d1-c5dab0ce98be",
+                "status": {"code": "sucess", "description": "schedule send succeeded"},
+                "close_time": 1690579819,
+            },
+        ],
+    }
+    return mock_http_client
diff --git a/tests/resources/test_calendars.py b/tests/resources/test_calendars.py
index 65b4022..c757185 100644
--- a/tests/resources/test_calendars.py
+++ b/tests/resources/test_calendars.py
@@ -166,18 +166,35 @@ class TestCalendar:
             request_body=request_body,
         )
 
-    def test_get_free_busy(self, http_client_response):
-        calendars = Calendars(http_client_response)
+    def test_get_free_busy(self, http_client_free_busy):
+        calendars = Calendars(http_client_free_busy)
         request_body = {
             "start_time": 1614556800,
             "end_time": 1614643200,
             "emails": ["test@gmail.com"],
         }
 
-        calendars.get_free_busy(identifier="abc-123", request_body=request_body)
+        response = calendars.get_free_busy(
+            identifier="abc-123", request_body=request_body
+        )
 
-        http_client_response._execute.assert_called_once_with(
+        http_client_free_busy._execute.assert_called_once_with(
             method="POST",
             path="/v3/grants/abc-123/calendars/free-busy",
             request_body=request_body,
         )
+        assert len(response.data) == 2
+        assert response.request_id == "dd3ec9a2-8f15-403d-b269-32b1f1beb9f5"
+        assert response.data[0].email == "user1@example.com"
+        assert len(response.data[0].time_slots) == 2
+        assert response.data[0].time_slots[0].start_time == 1690898400
+        assert response.data[0].time_slots[0].end_time == 1690902000
+        assert response.data[0].time_slots[0].status == "busy"
+        assert response.data[0].time_slots[1].start_time == 1691064000
+        assert response.data[0].time_slots[1].end_time == 1691067600
+        assert response.data[0].time_slots[1].status == "busy"
+        assert response.data[1].email == "user2@example.com"
+        assert (
+            response.data[1].error
+            == "Unable to resolve e-mail address user2@example.com to an Active Directory object."
+        )
diff --git a/tests/resources/test_messages.py b/tests/resources/test_messages.py
index deb7fee..1047731 100644
--- a/tests/resources/test_messages.py
+++ b/tests/resources/test_messages.py
@@ -177,15 +177,24 @@ class TestMessage:
                 data=mock_encoder,
             )
 
-    def test_list_scheduled_messages(self, http_client_response):
-        messages = Messages(http_client_response)
+    def test_list_scheduled_messages(self, http_client_list_scheduled_messages):
+        messages = Messages(http_client_list_scheduled_messages)
 
-        messages.list_scheduled_messages(identifier="abc-123")
+        res = messages.list_scheduled_messages(identifier="abc-123")
 
-        http_client_response._execute.assert_called_once_with(
+        http_client_list_scheduled_messages._execute.assert_called_once_with(
             method="GET",
             path="/v3/grants/abc-123/messages/schedules",
         )
+        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 == "sucess"
+        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)