diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index 430f6a8..b825958 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -93,6 +93,39 @@ sinch_client = SinchClient(
sinch_client.configuration.conversation_region = "eu"
```
+### [`Conversation`](https://github.com/sinch/sinch-sdk-python/tree/main/sinch/domains/conversation)
+
+#### Replacement models
+
+##### Messages (send, get, delete, list)
+
+| Old class | New class |
+|-----------|-----------|
+| `sinch.domains.conversation.models.message.requests.SendConversationMessageRequest` | `send()`: pass `app_id`, `message` (dict or [`SendMessageRequestBodyDict`](sinch/domains/conversation/models/v1/messages/types/send_message_request_body_dict.py)), and either `contact_id` or `recipient_identities`. Internally uses [`SendMessageRequest`](sinch/domains/conversation/models/v1/messages/internal/request/send_message_request.py), [`SendMessageRequestBody`](sinch/domains/conversation/models/v1/messages/internal/request/send_message_request_body.py). For typed payloads use `send_text_message()`, `send_card_message()`, etc.
+| `sinch.domains.conversation.models.message.responses.SendConversationMessageResponse` | [`SendMessageResponse`](sinch/domains/conversation/models/v1/messages/response/types/send_message_response.py) (`message_id`, optional `accepted_time` as `datetime`) |
+| `sinch.domains.conversation.models.message.requests.GetConversationMessageRequest` | `get(message_id, messages_source=None, **kwargs)`. Internally uses [`MessageIdRequest`](sinch/domains/conversation/models/v1/messages/internal/request/message_id_request.py). |
+| `sinch.domains.conversation.models.message.responses.GetConversationMessageResponse` | [`ConversationMessageResponse`](sinch/domains/conversation/models/v1/messages/response/types/__init__.py) (Union of app/contact message response types) |
+| `sinch.domains.conversation.models.message.requests.DeleteConversationMessageRequest` | `delete(message_id, messages_source=None, **kwargs)`. Internally uses [`MessageIdRequest`](sinch/domains/conversation/models/v1/messages/internal/request/message_id_request.py). |
+| `sinch.domains.conversation.models.message.responses.DeleteConversationMessageResponse` | `None` (method returns `None`) |
+| `sinch.domains.conversation.models.message.requests.ListConversationMessagesRequest` | `list()` with individual parameters: `conversation_id`, `contact_id`, `app_id`, `page_size`, `page_token`, `view`, `messages_source`, `only_recipient_originated` (signature aligned with V1 where available) |
+| `sinch.domains.conversation.models.message.responses.ListConversationMessagesResponse` | Response type for `list()` (messages list, next_page_token) |
+
+#### Replacement APIs
+
+The Conversation domain API access remains `sinch_client.conversation`; message operations are under `sinch_client.conversation.messages`. Recipient is specified with exactly one of `contact_id` or `recipient_identities` (list of `{channel, identity}`).
+
+##### Messages API
+
+| Old method | New method in `conversation.messages` |
+|------------|----------------------------------------|
+| `send()` with `SendConversationMessageRequest` | Use convenience methods: `send_text_message()`, `send_card_message()`, `send_carousel_message()`, `send_choice_message()`, `send_contact_info_message()`, `send_list_message()`, `send_location_message()`, `send_media_message()`, `send_template_message()`
Or `send()` with `app_id`, `message` (dict or `SendMessageRequestBodyDict`), and either `contact_id` or `recipient_identities` |
+| `get()` with `GetConversationMessageRequest` | `get()` with `message_id: str` parameter |
+| `delete()` with `DeleteConversationMessageRequest` | `delete()` with `message_id: str` parameter |
+| `list()` with `ListConversationMessagesRequest` | In Progress |
+| — | **New in V2:** `update()` with `message_id`, `metadata`, and optional `messages_source`|
+
+
+
### [`SMS`](https://github.com/sinch/sinch-sdk-python/tree/main/sinch/domains/sms)
#### Replacement models
diff --git a/examples/snippets/conversation/messages/send/snippet.py b/examples/snippets/conversation/messages/send/snippet.py
new file mode 100644
index 0000000..970c9c9
--- /dev/null
+++ b/examples/snippets/conversation/messages/send/snippet.py
@@ -0,0 +1,41 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+response = sinch_client.conversation.messages.send(
+ app_id=app_id,
+ message={
+ "text_message": {
+ "text": "[Python SDK: Conversation Message] Sample text message"
+ }
+ },
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_card_message/snippet.py b/examples/snippets/conversation/messages/send_card_message/snippet.py
new file mode 100644
index 0000000..5300eaf
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_card_message/snippet.py
@@ -0,0 +1,46 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+card_message = {
+ "title": "Card title",
+ "description": "Optional card description",
+ "choices": [
+ {"text_message": {"text": "Yes"}, "postback_data": "yes"},
+ {"text_message": {"text": "No"}, "postback_data": "no"},
+ ]
+}
+
+response = sinch_client.conversation.messages.send_card_message(
+ app_id=app_id,
+ card_message=card_message,
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent card message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_carousel_message/snippet.py b/examples/snippets/conversation/messages/send_carousel_message/snippet.py
new file mode 100644
index 0000000..bb8ecb4
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_carousel_message/snippet.py
@@ -0,0 +1,52 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+carousel_message = {
+ "cards": [
+ {
+ "title": "Card 1",
+ "description": "First card description",
+ "choices": [{"text_message": {"text": "Option 1"}}],
+ },
+ {
+ "title": "Card 2",
+ "description": "Second card description",
+ "choices": [{"url_message": {"title": "Link", "url": "https://example.com"}}],
+ },
+ ],
+}
+
+response = sinch_client.conversation.messages.send_carousel_message(
+ app_id=app_id,
+ carousel_message=carousel_message,
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent carousel message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_choice_message/snippet.py b/examples/snippets/conversation/messages/send_choice_message/snippet.py
new file mode 100644
index 0000000..315e13d
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_choice_message/snippet.py
@@ -0,0 +1,45 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+choice_message = {
+ "text_message": {"text": "Choose an option:"},
+ "choices": [
+ {"text_message": {"text": "Option A"}, "postback_data": "option_a"},
+ {"text_message": {"text": "Option B"}, "postback_data": "option_b"},
+ ],
+}
+
+response = sinch_client.conversation.messages.send_choice_message(
+ app_id=app_id,
+ choice_message=choice_message,
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent choice message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_contact_info_message/snippet.py b/examples/snippets/conversation/messages/send_contact_info_message/snippet.py
new file mode 100644
index 0000000..70f84c5
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_contact_info_message/snippet.py
@@ -0,0 +1,42 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+contact_info_message = {
+ "name": {"full_name": "John Doe"},
+ "phone_numbers": [{"phone_number": "+1234567890"}],
+}
+
+response = sinch_client.conversation.messages.send_contact_info_message(
+ app_id=app_id,
+ contact_info_message=contact_info_message,
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent contact info message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_list_message/snippet.py b/examples/snippets/conversation/messages/send_list_message/snippet.py
new file mode 100644
index 0000000..3b7010a
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_list_message/snippet.py
@@ -0,0 +1,51 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+list_message = {
+ "title": "Choose an option",
+ "description": "Select from the list below",
+ "sections": [
+ {
+ "title": "Section 1",
+ "items": [
+ {"choice": {"title": "Option A", "postback_data": "option_a"}},
+ {"choice": {"title": "Option B", "postback_data": "option_b"}},
+ ],
+ },
+ ],
+}
+
+response = sinch_client.conversation.messages.send_list_message(
+ app_id=app_id,
+ list_message=list_message,
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent list message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_location_message/snippet.py b/examples/snippets/conversation/messages/send_location_message/snippet.py
new file mode 100644
index 0000000..451d0d8
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_location_message/snippet.py
@@ -0,0 +1,42 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+location_message = {
+ "title": "Our office",
+ "coordinates": {"latitude": 59.3293, "longitude": 18.0686},
+}
+
+response = sinch_client.conversation.messages.send_location_message(
+ app_id=app_id,
+ location_message=location_message,
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent location message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_media_message/snippet.py b/examples/snippets/conversation/messages/send_media_message/snippet.py
new file mode 100644
index 0000000..df7aa97
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_media_message/snippet.py
@@ -0,0 +1,41 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+media_message = {
+ "url": "https://example.com/image.jpg",
+}
+
+response = sinch_client.conversation.messages.send_media_message(
+ app_id=app_id,
+ media_message=media_message,
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent media message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_template_message/snippet.py b/examples/snippets/conversation/messages/send_template_message/snippet.py
new file mode 100644
index 0000000..cb604a7
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_template_message/snippet.py
@@ -0,0 +1,44 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "RCS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+template_message = {
+ "omni_template": {
+ "template_id": "TEMPLATE_ID",
+ "version": "1",
+ },
+}
+
+response = sinch_client.conversation.messages.send_template_message(
+ app_id=app_id,
+ template_message=template_message,
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent template message.\n{response}")
diff --git a/examples/snippets/conversation/messages/send_text_message/snippet.py b/examples/snippets/conversation/messages/send_text_message/snippet.py
new file mode 100644
index 0000000..6a5bebb
--- /dev/null
+++ b/examples/snippets/conversation/messages/send_text_message/snippet.py
@@ -0,0 +1,37 @@
+"""
+Sinch Python Snippet
+
+TODO: Update links when v2 is released.
+This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
+"""
+
+import os
+from dotenv import load_dotenv
+from sinch import SinchClient
+
+load_dotenv()
+
+sinch_client = SinchClient(
+ project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
+ key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
+ key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
+ conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
+)
+
+# The ID of the Conversation App to send the message from
+app_id = "CONVERSATION_APP_ID"
+# The phone number of the recipient in E.164 format (e.g. +46701234567)
+recipient_identities = [
+ {
+ "channel": "SMS",
+ "identity": "RECIPIENT_PHONE_NUMBER"
+ }
+]
+
+response = sinch_client.conversation.messages.send_text_message(
+ app_id=app_id,
+ text="[Python SDK: Conversation] Sample text message",
+ recipient_identities=recipient_identities
+)
+
+print(f"Successfully sent text message.\n{response}")
diff --git a/tests/unit/domains/conversation/v1/test_conversation_messages.py b/tests/unit/domains/conversation/v1/test_conversation_messages.py
new file mode 100644
index 0000000..144d30d
--- /dev/null
+++ b/tests/unit/domains/conversation/v1/test_conversation_messages.py
@@ -0,0 +1,306 @@
+"""
+Unit tests for Conversation Messages API
+"""
+from unittest.mock import MagicMock
+import pytest
+from sinch.domains.conversation.conversation import Conversation
+from sinch.domains.conversation.api.v1 import Messages
+from sinch.domains.conversation.api.v1.internal import (
+ DeleteMessageEndpoint,
+ GetMessageEndpoint,
+ SendMessageEndpoint,
+ UpdateMessageMetadataEndpoint,
+)
+from sinch.domains.conversation.models.v1.messages.internal.request import (
+ MessageIdRequest,
+ UpdateMessageMetadataRequest,
+ SendMessageRequest,
+)
+from sinch.domains.conversation.models.v1.messages.response.types import (
+ SendMessageResponse,
+)
+
+
+@pytest.fixture
+def mock_send_message_response():
+ return SendMessageResponse(
+ message_id="01FC66621SND04119Z8PMV1QPQ",
+ )
+
+
+@pytest.fixture
+def mock_conversation_message_response():
+ response = MagicMock()
+ response.id = "01FC66621GET02119Z8PMV1QPQ"
+ return response
+
+
+def test_conversation_expects_messages_attribute(mock_sinch_client_conversation):
+ """Test that Conversation exposes .messages as Messages instance."""
+ conversation = Conversation(mock_sinch_client_conversation)
+ assert isinstance(conversation.messages, Messages)
+
+
+def test_messages_delete_expects_correct_request(
+ mock_sinch_client_conversation, mocker
+):
+ """Test that delete sends the correct request."""
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ None
+ )
+ spy_endpoint = mocker.spy(DeleteMessageEndpoint, "__init__")
+
+ message_id = "01FC66621DEL01119Z8PMV1QPQ"
+ conversation = Conversation(mock_sinch_client_conversation)
+ conversation.messages.delete(message_id=message_id)
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert kwargs["project_id"] == "test_project_id"
+ assert isinstance(kwargs["request_data"], MessageIdRequest)
+ assert kwargs["request_data"].message_id == message_id
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()
+
+
+def test_messages_delete_with_messages_source_expects_correct_request(
+ mock_sinch_client_conversation, mocker
+):
+ """Test that delete with messages_source sends the correct request."""
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ None
+ )
+ spy_endpoint = mocker.spy(DeleteMessageEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ message_id = "01FC66621DL205119Z8PMV1QPQ"
+ conversation.messages.delete(
+ message_id=message_id,
+ messages_source="DISPATCH_SOURCE",
+ )
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert kwargs["request_data"].message_id == message_id
+ assert kwargs["request_data"].messages_source == "DISPATCH_SOURCE"
+
+
+def test_messages_get_expects_correct_request(
+ mock_sinch_client_conversation, mock_conversation_message_response, mocker
+):
+ """Test that get sends the correct request and returns the response."""
+ message_id = "01FC66621GET02119Z8PMV1QPQ"
+ mock_conversation_message_response.id = message_id
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ mock_conversation_message_response
+ )
+ spy_endpoint = mocker.spy(GetMessageEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ response = conversation.messages.get(message_id=message_id)
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert kwargs["project_id"] == "test_project_id"
+ assert isinstance(kwargs["request_data"], MessageIdRequest)
+ assert kwargs["request_data"].message_id == message_id
+ assert response.id == message_id
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()
+
+
+def test_messages_update_expects_correct_request(
+ mock_sinch_client_conversation, mock_conversation_message_response, mocker
+):
+ """Test that update sends the correct request and returns the response."""
+ message_id = "01FC66621UPD03119Z8PMV1QPQ"
+ mock_conversation_message_response.id = message_id
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ mock_conversation_message_response
+ )
+ spy_endpoint = mocker.spy(UpdateMessageMetadataEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ response = conversation.messages.update(
+ message_id=message_id,
+ metadata="updated-metadata",
+ )
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert kwargs["project_id"] == "test_project_id"
+ assert isinstance(kwargs["request_data"], UpdateMessageMetadataRequest)
+ assert kwargs["request_data"].message_id == message_id
+ assert kwargs["request_data"].metadata == "updated-metadata"
+ assert response.id == message_id
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()
+
+
+def test_messages_send_expects_correct_request(
+ mock_sinch_client_conversation, mock_send_message_response, mocker
+):
+ """Test that send sends the correct request and returns SendMessageResponse."""
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ mock_send_message_response
+ )
+ spy_endpoint = mocker.spy(SendMessageEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ response = conversation.messages.send(
+ app_id="APP_ID",
+ message={"text_message": {"text": "Hello"}},
+ recipient_identities=[
+ {"channel": "RCS", "identity": "+46701234567"},
+ ],
+ )
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert kwargs["project_id"] == "test_project_id"
+ assert isinstance(kwargs["request_data"], SendMessageRequest)
+ assert kwargs["request_data"].app_id == "APP_ID"
+ assert kwargs["request_data"].message.text_message is not None
+ assert kwargs["request_data"].message.text_message.text == "Hello"
+ assert isinstance(response, SendMessageResponse)
+ assert response.message_id == "01FC66621SND04119Z8PMV1QPQ"
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()
+
+
+def test_messages_send_with_contact_id_expects_correct_request(
+ mock_sinch_client_conversation, mock_send_message_response, mocker
+):
+ """Test that send with contact_id builds recipient correctly."""
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ mock_send_message_response
+ )
+ spy_endpoint = mocker.spy(SendMessageEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ response = conversation.messages.send(
+ app_id="APP_ID",
+ message={"text_message": {"text": "Hi"}},
+ contact_id="CONTACT_123",
+ )
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert isinstance(kwargs["request_data"], SendMessageRequest)
+ assert kwargs["request_data"].app_id == "APP_ID"
+ assert kwargs["request_data"].recipient.contact_id == "CONTACT_123"
+ assert isinstance(response, SendMessageResponse)
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()
+
+
+def test_messages_send_text_message_expects_correct_request(
+ mock_sinch_client_conversation, mock_send_message_response, mocker
+):
+ """Test that send_text_message sends the correct request."""
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ mock_send_message_response
+ )
+ spy_endpoint = mocker.spy(SendMessageEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ response = conversation.messages.send_text_message(
+ app_id="APP_ID",
+ text="Hello",
+ recipient_identities=[
+ {"channel": "RCS", "identity": "+46701234567"},
+ ],
+ )
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert isinstance(kwargs["request_data"], SendMessageRequest)
+ assert kwargs["request_data"].app_id == "APP_ID"
+ assert kwargs["request_data"].message.text_message is not None
+ assert kwargs["request_data"].message.text_message.text == "Hello"
+ assert isinstance(response, SendMessageResponse)
+ assert response.message_id == "01FC66621SND04119Z8PMV1QPQ"
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()
+
+
+def test_messages_send_card_message_expects_correct_request(
+ mock_sinch_client_conversation, mock_send_message_response, mocker
+):
+ """Test that send_card_message sends the correct request."""
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ mock_send_message_response
+ )
+ spy_endpoint = mocker.spy(SendMessageEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ response = conversation.messages.send_card_message(
+ app_id="APP_ID",
+ card_message={"title": "Card title", "description": "Description"},
+ recipient_identities=[
+ {"channel": "RCS", "identity": "+46701234567"},
+ ],
+ )
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert isinstance(kwargs["request_data"], SendMessageRequest)
+ assert kwargs["request_data"].app_id == "APP_ID"
+ assert kwargs["request_data"].message.card_message is not None
+ assert kwargs["request_data"].message.card_message.title == "Card title"
+ assert isinstance(response, SendMessageResponse)
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()
+
+
+def test_messages_send_choice_message_expects_correct_request(
+ mock_sinch_client_conversation, mock_send_message_response, mocker
+):
+ """Test that send_choice_message sends the correct request."""
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ mock_send_message_response
+ )
+ spy_endpoint = mocker.spy(SendMessageEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ response = conversation.messages.send_choice_message(
+ app_id="APP_ID",
+ choice_message={
+ "text_message": {"text": "Choose:"},
+ "choices": [
+ {"text_message": {"text": "Option A"}, "postback_data": "a"},
+ ],
+ },
+ recipient_identities=[
+ {"channel": "RCS", "identity": "+46701234567"},
+ ],
+ )
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert isinstance(kwargs["request_data"], SendMessageRequest)
+ assert kwargs["request_data"].app_id == "APP_ID"
+ assert kwargs["request_data"].message.choice_message is not None
+ assert kwargs["request_data"].message.choice_message.text_message.text == "Choose:"
+ assert len(kwargs["request_data"].message.choice_message.choices) == 1
+ assert isinstance(response, SendMessageResponse)
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()
+
+
+def test_messages_send_media_message_expects_correct_request(
+ mock_sinch_client_conversation, mock_send_message_response, mocker
+):
+ """Test that send_media_message sends the correct request."""
+ mock_sinch_client_conversation.configuration.transport.request.return_value = (
+ mock_send_message_response
+ )
+ spy_endpoint = mocker.spy(SendMessageEndpoint, "__init__")
+
+ conversation = Conversation(mock_sinch_client_conversation)
+ response = conversation.messages.send_media_message(
+ app_id="APP_ID",
+ media_message={"url": "https://example.com/image.jpg"},
+ recipient_identities=[
+ {"channel": "RCS", "identity": "+46701234567"},
+ ],
+ )
+
+ spy_endpoint.assert_called_once()
+ _, kwargs = spy_endpoint.call_args
+ assert kwargs["request_data"].message.media_message is not None
+ assert kwargs["request_data"].message.media_message.url == "https://example.com/image.jpg"
+ assert isinstance(response, SendMessageResponse)
+ mock_sinch_client_conversation.configuration.transport.request.assert_called_once()