diff --git a/README.md b/README.md
index 0cd5f38..2d196ca 100644
--- a/README.md
+++ b/README.md
@@ -239,6 +239,7 @@ The same situation applies to both `client.batch_send()` and `client.sending_api
- Contact Imports – [`contacts/contact_imports.py`](examples/contacts/contact_imports.py)
### Email Templates API:
+- Templates (experimental, `/api/templates`) – [`paginated_templates/templates.py`](examples/paginated_templates/templates.py)
- Templates management – [`email_templates/templates.py`](examples/email_templates/templates.py)
### Sending Domains API:
diff --git a/examples/paginated_templates/templates.py b/examples/paginated_templates/templates.py
new file mode 100644
index 0000000..bd99886
--- /dev/null
+++ b/examples/paginated_templates/templates.py
@@ -0,0 +1,80 @@
+import os
+from typing import Optional
+
+import mailtrap as mt
+from mailtrap.models.common import DeletedObject
+from mailtrap.models.paginated_templates import Template
+from mailtrap.models.paginated_templates import TemplateListResponse
+
+API_KEY = os.environ["MAILTRAP_API_KEY"]
+ACCOUNT_ID = os.environ["MAILTRAP_ACCOUNT_ID"]
+
+client = mt.MailtrapClient(token=API_KEY, account_id=ACCOUNT_ID)
+templates_api = client.templates_api.templates
+
+
+def list_templates() -> TemplateListResponse:
+ # `token` is the page number (page-token pagination); `per_page` caps at 100.
+ response = templates_api.get_list(mt.TemplateListParams(per_page=50, token=1))
+ print(response.data)
+ print(response.pagination)
+ return response
+
+
+def create_template(
+ name: str,
+ subject: str,
+ category: str,
+ body_html: Optional[str] = None,
+ body_text: Optional[str] = None,
+) -> Template:
+ params = mt.CreateTemplateParams(
+ name=name,
+ subject=subject,
+ category=category,
+ body_html=body_html,
+ body_text=body_text,
+ )
+ return templates_api.create(params)
+
+
+def get_template(template_id: int) -> Template:
+ return templates_api.get_by_id(template_id)
+
+
+def update_template(
+ template_id: int,
+ name: Optional[str] = None,
+ subject: Optional[str] = None,
+ category: Optional[str] = None,
+ body_html: Optional[str] = None,
+ body_text: Optional[str] = None,
+) -> Template:
+ params = mt.UpdateTemplateParams(
+ name=name,
+ subject=subject,
+ category=category,
+ body_html=body_html,
+ body_text=body_text,
+ )
+ return templates_api.update(template_id, params)
+
+
+def delete_template(template_id: int) -> DeletedObject:
+ return templates_api.delete(template_id)
+
+
+if __name__ == "__main__":
+ list_templates()
+
+ created = create_template(
+ name="Welcome",
+ subject="Welcome aboard",
+ category="Onboarding",
+ body_html="
Hello!
",
+ )
+ print(created)
+
+ print(get_template(created.id))
+ print(update_template(created.id, subject="Welcome to Mailtrap"))
+ print(delete_template(created.id))
diff --git a/mailtrap/__init__.py b/mailtrap/__init__.py
index eb34f7c..96015ce 100644
--- a/mailtrap/__init__.py
+++ b/mailtrap/__init__.py
@@ -58,6 +58,11 @@
from .models.mail import MailFromTemplate
from .models.messages import UpdateEmailMessageParams
from .models.organizations import CreateSubAccountParams
+from .models.paginated_templates import CreateTemplateParams
+from .models.paginated_templates import Template
+from .models.paginated_templates import TemplateListParams
+from .models.paginated_templates import TemplateListResponse
+from .models.paginated_templates import UpdateTemplateParams
from .models.permissions import PermissionResourceParams
from .models.projects import ProjectParams
from .models.sending_domains import CreateSendingDomainParams
diff --git a/mailtrap/api/paginated_templates.py b/mailtrap/api/paginated_templates.py
new file mode 100644
index 0000000..4ad88a3
--- /dev/null
+++ b/mailtrap/api/paginated_templates.py
@@ -0,0 +1,12 @@
+from mailtrap.api.resources.paginated_templates import PaginatedTemplatesApi
+from mailtrap.http import HttpClient
+
+
+class TemplatesBaseApi:
+ def __init__(self, client: HttpClient, account_id: str) -> None:
+ self._account_id = account_id
+ self._client = client
+
+ @property
+ def templates(self) -> PaginatedTemplatesApi:
+ return PaginatedTemplatesApi(account_id=self._account_id, client=self._client)
diff --git a/mailtrap/api/resources/paginated_templates.py b/mailtrap/api/resources/paginated_templates.py
new file mode 100644
index 0000000..8d2ce75
--- /dev/null
+++ b/mailtrap/api/resources/paginated_templates.py
@@ -0,0 +1,62 @@
+from typing import Optional
+
+from mailtrap.http import HttpClient
+from mailtrap.models.common import DeletedObject
+from mailtrap.models.paginated_templates import CreateTemplateParams
+from mailtrap.models.paginated_templates import Template
+from mailtrap.models.paginated_templates import TemplateListParams
+from mailtrap.models.paginated_templates import TemplateListResponse
+from mailtrap.models.paginated_templates import TemplateResponse
+from mailtrap.models.paginated_templates import UpdateTemplateParams
+
+
+class PaginatedTemplatesApi:
+ """
+ Templates API. The ``/api/templates`` endpoints are experimental: their
+ request and response shapes may change before general availability.
+ """
+
+ def __init__(self, client: HttpClient, account_id: str) -> None:
+ self._account_id = account_id
+ self._client = client
+
+ def get_list(
+ self, params: Optional[TemplateListParams] = None
+ ) -> TemplateListResponse:
+ """
+ List email templates in the account, one page at a time. Unlike the
+ ``email_templates_api`` list, it does not return every template: pass
+ ``pagination.next_token`` with the same ``per_page`` to get the next
+ page. Omit ``params`` for the first page with API defaults.
+ """
+ query_params = params.api_query_params if params else None
+ response = self._client.get(self._api_path(), params=query_params or None)
+ return TemplateListResponse(**response)
+
+ def get_by_id(self, template_id: int) -> Template:
+ """Get an email template by ID."""
+ response = self._client.get(self._api_path(template_id))
+ return TemplateResponse(**response).data
+
+ def create(self, template_params: CreateTemplateParams) -> Template:
+ """Create a new email template."""
+ response = self._client.post(self._api_path(), json=template_params.api_data)
+ return TemplateResponse(**response).data
+
+ def update(self, template_id: int, template_params: UpdateTemplateParams) -> Template:
+ """Update an email template. Only the supplied fields are changed."""
+ response = self._client.patch(
+ self._api_path(template_id), json=template_params.api_data
+ )
+ return TemplateResponse(**response).data
+
+ def delete(self, template_id: int) -> DeletedObject:
+ """Delete an email template."""
+ self._client.delete(self._api_path(template_id))
+ return DeletedObject(template_id)
+
+ def _api_path(self, template_id: Optional[int] = None) -> str:
+ path = f"/api/accounts/{self._account_id}/templates"
+ if template_id is not None:
+ return f"{path}/{template_id}"
+ return path
diff --git a/mailtrap/client.py b/mailtrap/client.py
index 520e7b1..85e6236 100644
--- a/mailtrap/client.py
+++ b/mailtrap/client.py
@@ -13,6 +13,7 @@
from mailtrap.api.general import GeneralApi
from mailtrap.api.inbound import InboundBaseApi
from mailtrap.api.organizations import OrganizationsBaseApi
+from mailtrap.api.paginated_templates import TemplatesBaseApi
from mailtrap.api.resources.stats import StatsApi
from mailtrap.api.sending import SendingApi
from mailtrap.api.sending_domains import SendingDomainsBaseApi
@@ -103,6 +104,14 @@ def email_templates_api(self) -> EmailTemplatesApi:
client=HttpClient(host=GENERAL_HOST, headers=self.headers),
)
+ @property
+ def templates_api(self) -> TemplatesBaseApi:
+ self._validate_account_id("Templates API")
+ return TemplatesBaseApi(
+ account_id=cast(str, self.account_id),
+ client=HttpClient(host=GENERAL_HOST, headers=self.headers),
+ )
+
@property
def contacts_api(self) -> ContactsBaseApi:
self._validate_account_id("Contacts API")
diff --git a/mailtrap/models/paginated_templates.py b/mailtrap/models/paginated_templates.py
new file mode 100644
index 0000000..3f339f4
--- /dev/null
+++ b/mailtrap/models/paginated_templates.py
@@ -0,0 +1,88 @@
+"""Models for the account-scoped, paginated Templates API (``/api/templates``)."""
+
+from typing import Optional
+
+from pydantic import Field
+from pydantic.dataclasses import dataclass
+
+from mailtrap.models.common import Pagination
+from mailtrap.models.common import RequestParams
+
+
+@dataclass
+class Template:
+ """A single email template."""
+
+ id: int
+ uuid: Optional[str] = None
+ name: Optional[str] = None
+ category: Optional[str] = None
+ subject: Optional[str] = None
+ body_html: Optional[str] = None
+ body_text: Optional[str] = None
+ created_at: Optional[str] = None
+ updated_at: Optional[str] = None
+
+
+@dataclass
+class TemplateResponse:
+ """Envelope of a single-template response."""
+
+ data: Template
+
+
+@dataclass
+class TemplateListResponse:
+ """Paginated response from listing templates."""
+
+ data: list[Template] = Field(default_factory=list)
+ pagination: Optional[Pagination] = None
+
+
+@dataclass
+class TemplateListParams(RequestParams):
+ """
+ Query params for listing templates. ``token`` is the page number and
+ ``per_page`` is capped at 100.
+ """
+
+ per_page: Optional[int] = None
+ token: Optional[int] = None
+
+
+@dataclass
+class CreateTemplateParams(RequestParams):
+ """Attributes for creating a template (sent as a flat JSON body)."""
+
+ name: str
+ subject: str
+ category: str
+ body_html: Optional[str] = None
+ body_text: Optional[str] = None
+
+
+@dataclass
+class UpdateTemplateParams(RequestParams):
+ """
+ Attributes for updating a template (sent as a flat JSON body). All fields
+ are optional, but at least one must be provided.
+ """
+
+ name: Optional[str] = None
+ subject: Optional[str] = None
+ category: Optional[str] = None
+ body_html: Optional[str] = None
+ body_text: Optional[str] = None
+
+ def __post_init__(self) -> None:
+ if all(
+ value is None
+ for value in [
+ self.name,
+ self.subject,
+ self.category,
+ self.body_html,
+ self.body_text,
+ ]
+ ):
+ raise ValueError("At least one field must be provided for update action")
diff --git a/tests/unit/api/paginated_templates/__init__.py b/tests/unit/api/paginated_templates/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/unit/api/paginated_templates/test_paginated_templates.py b/tests/unit/api/paginated_templates/test_paginated_templates.py
new file mode 100644
index 0000000..409360a
--- /dev/null
+++ b/tests/unit/api/paginated_templates/test_paginated_templates.py
@@ -0,0 +1,363 @@
+import json
+from typing import Any
+from urllib.parse import parse_qs
+from urllib.parse import urlparse
+
+import pytest
+import responses
+
+from mailtrap.api.resources.paginated_templates import PaginatedTemplatesApi
+from mailtrap.config import GENERAL_HOST
+from mailtrap.exceptions import APIError
+from mailtrap.http import HttpClient
+from mailtrap.models.common import DeletedObject
+from mailtrap.models.paginated_templates import CreateTemplateParams
+from mailtrap.models.paginated_templates import Template
+from mailtrap.models.paginated_templates import TemplateListParams
+from mailtrap.models.paginated_templates import TemplateListResponse
+from mailtrap.models.paginated_templates import UpdateTemplateParams
+from tests import conftest
+
+ACCOUNT_ID = "321"
+TEMPLATE_ID = 26730
+BASE_TEMPLATES_URL = f"https://{GENERAL_HOST}/api/accounts/{ACCOUNT_ID}/templates"
+
+
+@pytest.fixture
+def client() -> PaginatedTemplatesApi:
+ return PaginatedTemplatesApi(account_id=ACCOUNT_ID, client=HttpClient(GENERAL_HOST))
+
+
+@pytest.fixture
+def sample_template_dict() -> dict[str, Any]:
+ return {
+ "id": TEMPLATE_ID,
+ "uuid": "b81aabcd-1a1e-41cf-91b6-eca0254b3d96",
+ "name": "Promotion Template",
+ "category": "Promotion",
+ "subject": "Promotion Template subject",
+ "body_html": "body
",
+ "body_text": "Text body",
+ "created_at": "2026-05-01T10:15:00.000Z",
+ "updated_at": "2026-05-02T09:00:00.000Z",
+ }
+
+
+class TestPaginatedTemplatesApi:
+
+ @pytest.mark.parametrize(
+ "status_code,response_json,expected_error_message",
+ [
+ (
+ conftest.UNAUTHORIZED_STATUS_CODE,
+ conftest.UNAUTHORIZED_RESPONSE,
+ conftest.UNAUTHORIZED_ERROR_MESSAGE,
+ ),
+ (
+ conftest.RATE_LIMIT_ERROR_STATUS_CODE,
+ conftest.RATE_LIMIT_ERROR_RESPONSE,
+ conftest.RATE_LIMIT_ERROR_MESSAGE,
+ ),
+ (
+ conftest.VALIDATION_ERRORS_STATUS_CODE,
+ {"errors": "token is out of range"},
+ "token is out of range",
+ ),
+ ],
+ )
+ @responses.activate
+ def test_get_list_should_raise_api_errors(
+ self,
+ client: PaginatedTemplatesApi,
+ status_code: int,
+ response_json: dict,
+ expected_error_message: str,
+ ) -> None:
+ responses.get(BASE_TEMPLATES_URL, status=status_code, json=response_json)
+
+ with pytest.raises(APIError) as exc_info:
+ client.get_list()
+
+ assert expected_error_message in str(exc_info.value)
+
+ @responses.activate
+ def test_get_list_should_return_templates_and_pagination(
+ self, client: PaginatedTemplatesApi, sample_template_dict: dict
+ ) -> None:
+ responses.get(
+ BASE_TEMPLATES_URL,
+ json={
+ "data": [sample_template_dict, {"id": 26731, "name": "Second"}],
+ "pagination": {
+ "token": 1,
+ "prev_token": None,
+ "next_token": 2,
+ "first_url": f"{BASE_TEMPLATES_URL}?per_page=50&token=1",
+ "prev_url": None,
+ "current_url": f"{BASE_TEMPLATES_URL}?per_page=50&token=1",
+ "next_url": f"{BASE_TEMPLATES_URL}?per_page=50&token=2",
+ },
+ },
+ status=200,
+ )
+
+ result = client.get_list()
+
+ assert isinstance(result, TemplateListResponse)
+ assert all(isinstance(t, Template) for t in result.data)
+ assert len(result.data) == 2
+ assert result.data[0].id == TEMPLATE_ID
+ assert result.data[0].uuid == "b81aabcd-1a1e-41cf-91b6-eca0254b3d96"
+ assert result.data[0].body_html == "body
"
+ assert result.data[1].name == "Second"
+ assert result.data[1].body_html is None
+ assert result.pagination is not None
+ assert result.pagination.token == 1
+ assert result.pagination.prev_token is None
+ assert result.pagination.next_token == 2
+ assert result.pagination.next_url == f"{BASE_TEMPLATES_URL}?per_page=50&token=2"
+
+ @responses.activate
+ def test_get_list_should_return_empty_list(
+ self, client: PaginatedTemplatesApi
+ ) -> None:
+ responses.get(
+ BASE_TEMPLATES_URL, json={"data": [], "pagination": {"token": 1}}, status=200
+ )
+
+ result = client.get_list()
+
+ assert isinstance(result, TemplateListResponse)
+ assert result.data == []
+
+ @responses.activate
+ def test_get_list_should_send_per_page_and_token_query_params(
+ self, client: PaginatedTemplatesApi
+ ) -> None:
+ responses.get(BASE_TEMPLATES_URL, json={"data": [], "pagination": {}}, status=200)
+
+ client.get_list(TemplateListParams(per_page=25, token=2))
+
+ query = parse_qs(urlparse(responses.calls[0].request.url).query)
+ assert query["per_page"] == ["25"]
+ assert query["token"] == ["2"]
+
+ @responses.activate
+ def test_get_list_should_send_no_query_params_by_default(
+ self, client: PaginatedTemplatesApi
+ ) -> None:
+ responses.get(BASE_TEMPLATES_URL, json={"data": [], "pagination": {}}, status=200)
+
+ client.get_list()
+
+ assert urlparse(responses.calls[0].request.url).query == ""
+
+ @pytest.mark.parametrize(
+ "status_code,response_json,expected_error_message",
+ [
+ (
+ conftest.UNAUTHORIZED_STATUS_CODE,
+ conftest.UNAUTHORIZED_RESPONSE,
+ conftest.UNAUTHORIZED_ERROR_MESSAGE,
+ ),
+ (
+ conftest.NOT_FOUND_STATUS_CODE,
+ conftest.NOT_FOUND_RESPONSE,
+ conftest.NOT_FOUND_ERROR_MESSAGE,
+ ),
+ ],
+ )
+ @responses.activate
+ def test_get_by_id_should_raise_api_errors(
+ self,
+ client: PaginatedTemplatesApi,
+ status_code: int,
+ response_json: dict,
+ expected_error_message: str,
+ ) -> None:
+ responses.get(
+ f"{BASE_TEMPLATES_URL}/{TEMPLATE_ID}",
+ status=status_code,
+ json=response_json,
+ )
+
+ with pytest.raises(APIError) as exc_info:
+ client.get_by_id(TEMPLATE_ID)
+
+ assert expected_error_message in str(exc_info.value)
+
+ @responses.activate
+ def test_get_by_id_should_unwrap_data_envelope(
+ self, client: PaginatedTemplatesApi, sample_template_dict: dict
+ ) -> None:
+ responses.get(
+ f"{BASE_TEMPLATES_URL}/{TEMPLATE_ID}",
+ json={"data": sample_template_dict},
+ status=200,
+ )
+
+ template = client.get_by_id(TEMPLATE_ID)
+
+ assert isinstance(template, Template)
+ assert template.id == TEMPLATE_ID
+ assert template.name == "Promotion Template"
+ assert template.category == "Promotion"
+ assert template.subject == "Promotion Template subject"
+ assert template.body_text == "Text body"
+ assert template.created_at == "2026-05-01T10:15:00.000Z"
+
+ @pytest.mark.parametrize(
+ "status_code,response_json,expected_error_message",
+ [
+ (
+ conftest.UNAUTHORIZED_STATUS_CODE,
+ conftest.UNAUTHORIZED_RESPONSE,
+ conftest.UNAUTHORIZED_ERROR_MESSAGE,
+ ),
+ (
+ conftest.VALIDATION_ERRORS_STATUS_CODE,
+ {"errors": {"name": ["can't be blank"]}},
+ "name: can't be blank",
+ ),
+ ],
+ )
+ @responses.activate
+ def test_create_should_raise_api_errors(
+ self,
+ client: PaginatedTemplatesApi,
+ status_code: int,
+ response_json: dict,
+ expected_error_message: str,
+ ) -> None:
+ responses.post(BASE_TEMPLATES_URL, status=status_code, json=response_json)
+
+ with pytest.raises(APIError) as exc_info:
+ client.create(CreateTemplateParams(name="", subject="s", category="c"))
+
+ assert expected_error_message in str(exc_info.value)
+
+ @responses.activate
+ def test_create_should_send_flat_body_and_unwrap_response(
+ self, client: PaginatedTemplatesApi, sample_template_dict: dict
+ ) -> None:
+ responses.post(
+ BASE_TEMPLATES_URL, json={"data": sample_template_dict}, status=201
+ )
+
+ template = client.create(
+ CreateTemplateParams(
+ name="Promotion Template",
+ subject="Promotion Template subject",
+ category="Promotion",
+ body_html="body
",
+ )
+ )
+
+ assert isinstance(template, Template)
+ assert template.id == TEMPLATE_ID
+ assert json.loads(responses.calls[0].request.body) == {
+ "name": "Promotion Template",
+ "subject": "Promotion Template subject",
+ "category": "Promotion",
+ "body_html": "body
",
+ }
+
+ @pytest.mark.parametrize(
+ "status_code,response_json,expected_error_message",
+ [
+ (
+ conftest.UNAUTHORIZED_STATUS_CODE,
+ conftest.UNAUTHORIZED_RESPONSE,
+ conftest.UNAUTHORIZED_ERROR_MESSAGE,
+ ),
+ (
+ conftest.NOT_FOUND_STATUS_CODE,
+ conftest.NOT_FOUND_RESPONSE,
+ conftest.NOT_FOUND_ERROR_MESSAGE,
+ ),
+ (
+ conftest.VALIDATION_ERRORS_STATUS_CODE,
+ {"errors": {"subject": ["can't be blank"]}},
+ "subject: can't be blank",
+ ),
+ ],
+ )
+ @responses.activate
+ def test_update_should_raise_api_errors(
+ self,
+ client: PaginatedTemplatesApi,
+ status_code: int,
+ response_json: dict,
+ expected_error_message: str,
+ ) -> None:
+ responses.patch(
+ f"{BASE_TEMPLATES_URL}/{TEMPLATE_ID}",
+ status=status_code,
+ json=response_json,
+ )
+
+ with pytest.raises(APIError) as exc_info:
+ client.update(TEMPLATE_ID, UpdateTemplateParams(subject=""))
+
+ assert expected_error_message in str(exc_info.value)
+
+ @responses.activate
+ def test_update_should_patch_flat_body_and_unwrap_response(
+ self, client: PaginatedTemplatesApi, sample_template_dict: dict
+ ) -> None:
+ responses.patch(
+ f"{BASE_TEMPLATES_URL}/{TEMPLATE_ID}",
+ json={"data": {**sample_template_dict, "name": "Renamed"}},
+ status=200,
+ )
+
+ template = client.update(TEMPLATE_ID, UpdateTemplateParams(name="Renamed"))
+
+ assert isinstance(template, Template)
+ assert template.name == "Renamed"
+ assert json.loads(responses.calls[0].request.body) == {"name": "Renamed"}
+
+ @pytest.mark.parametrize(
+ "status_code,response_json,expected_error_message",
+ [
+ (
+ conftest.UNAUTHORIZED_STATUS_CODE,
+ conftest.UNAUTHORIZED_RESPONSE,
+ conftest.UNAUTHORIZED_ERROR_MESSAGE,
+ ),
+ (
+ conftest.NOT_FOUND_STATUS_CODE,
+ conftest.NOT_FOUND_RESPONSE,
+ conftest.NOT_FOUND_ERROR_MESSAGE,
+ ),
+ ],
+ )
+ @responses.activate
+ def test_delete_should_raise_api_errors(
+ self,
+ client: PaginatedTemplatesApi,
+ status_code: int,
+ response_json: dict,
+ expected_error_message: str,
+ ) -> None:
+ responses.delete(
+ f"{BASE_TEMPLATES_URL}/{TEMPLATE_ID}",
+ status=status_code,
+ json=response_json,
+ )
+
+ with pytest.raises(APIError) as exc_info:
+ client.delete(TEMPLATE_ID)
+
+ assert expected_error_message in str(exc_info.value)
+
+ @responses.activate
+ def test_delete_should_return_deleted_object(
+ self, client: PaginatedTemplatesApi
+ ) -> None:
+ responses.delete(f"{BASE_TEMPLATES_URL}/{TEMPLATE_ID}", status=204)
+
+ result = client.delete(TEMPLATE_ID)
+
+ assert isinstance(result, DeletedObject)
+ assert result.id == TEMPLATE_ID
diff --git a/tests/unit/models/test_paginated_templates.py b/tests/unit/models/test_paginated_templates.py
new file mode 100644
index 0000000..866d197
--- /dev/null
+++ b/tests/unit/models/test_paginated_templates.py
@@ -0,0 +1,52 @@
+import pytest
+
+from mailtrap.models.paginated_templates import CreateTemplateParams
+from mailtrap.models.paginated_templates import TemplateListParams
+from mailtrap.models.paginated_templates import UpdateTemplateParams
+
+
+class TestTemplateListParams:
+ def test_api_query_params_should_drop_unset_fields(self) -> None:
+ assert TemplateListParams(per_page=10).api_query_params == {"per_page": 10}
+
+ def test_api_query_params_should_include_all_fields(self) -> None:
+ params = TemplateListParams(per_page=10, token=2)
+ assert params.api_query_params == {"per_page": 10, "token": 2}
+
+
+class TestCreateTemplateParams:
+ def test_api_data_should_return_dict_with_required_props_only(self) -> None:
+ entity = CreateTemplateParams(name="test", subject="test", category="test")
+ assert entity.api_data == {
+ "name": "test",
+ "subject": "test",
+ "category": "test",
+ }
+
+ def test_api_data_should_return_dict_with_all_props(self) -> None:
+ entity = CreateTemplateParams(
+ name="test",
+ subject="test",
+ category="test",
+ body_html="test
",
+ body_text="test",
+ )
+ assert entity.api_data == {
+ "name": "test",
+ "subject": "test",
+ "category": "test",
+ "body_html": "test
",
+ "body_text": "test",
+ }
+
+
+class TestUpdateTemplateParams:
+ def test_raise_error_when_all_fields_are_missing(self) -> None:
+ with pytest.raises(ValueError) as exc:
+ _ = UpdateTemplateParams()
+
+ assert "At least one field must be provided for update action" in str(exc)
+
+ def test_api_data_should_return_only_provided_props(self) -> None:
+ entity = UpdateTemplateParams(name="test", body_text="text")
+ assert entity.api_data == {"name": "test", "body_text": "text"}
diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py
index 02fb138..9178089 100644
--- a/tests/unit/test_client.py
+++ b/tests/unit/test_client.py
@@ -63,6 +63,13 @@ def test_webhooks_api_requires_account_id(self) -> None:
assert "`account_id` is required for Webhooks API" in str(exc_info.value)
+ def test_templates_api_requires_account_id(self) -> None:
+ client = self.get_client()
+ with pytest.raises(mt.ClientConfigurationError) as exc_info:
+ _ = client.templates_api
+
+ assert "`account_id` is required for Templates API" in str(exc_info.value)
+
def test_email_campaigns_api_does_not_require_account_id(self) -> None:
client = self.get_client()