From 316c429442e9af83870bb06c0e1a4f1bb02e06f3 Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Tue, 8 Sep 2026 11:01:02 +0200 Subject: [PATCH 1/2] add sub account deletion to SubAccountsApi --- mailtrap/api/resources/sub_accounts.py | 25 +++++++- .../api/organizations/test_sub_accounts.py | 57 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/mailtrap/api/resources/sub_accounts.py b/mailtrap/api/resources/sub_accounts.py index 2de47f9..cbe7a15 100644 --- a/mailtrap/api/resources/sub_accounts.py +++ b/mailtrap/api/resources/sub_accounts.py @@ -1,4 +1,7 @@ +from typing import Optional + from mailtrap.http import HttpClient +from mailtrap.models.common import DeletedObject from mailtrap.models.organizations import CreateSubAccountParams from mailtrap.models.organizations import SubAccount @@ -27,5 +30,23 @@ def create(self, sub_account_params: CreateSubAccountParams) -> SubAccount: ) return SubAccount(**response) - def _api_path(self) -> str: - return f"/api/organizations/{self._organization_id}/sub_accounts" + def delete(self, sub_account_id: int) -> DeletedObject: + """ + Delete a sub account of the organization. Requires sub account + management permissions for this organization. + + The sub account and all of its data are removed permanently and cannot + be restored. Deleting the organization's last sub account also deletes + the organization. A repeated call for the same sub account returns + a 404 error. + + Rate limit: 10 requests per minute per organization. + """ + self._client.delete(self._api_path(sub_account_id)) + return DeletedObject(id=sub_account_id) + + def _api_path(self, sub_account_id: Optional[int] = None) -> str: + path = f"/api/organizations/{self._organization_id}/sub_accounts" + if sub_account_id is not None: + return f"{path}/{sub_account_id}" + return path diff --git a/tests/unit/api/organizations/test_sub_accounts.py b/tests/unit/api/organizations/test_sub_accounts.py index bf90926..b617b8e 100644 --- a/tests/unit/api/organizations/test_sub_accounts.py +++ b/tests/unit/api/organizations/test_sub_accounts.py @@ -7,6 +7,7 @@ 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.organizations import CreateSubAccountParams from mailtrap.models.organizations import SubAccount from tests import conftest @@ -152,3 +153,59 @@ def test_create_should_return_sub_account_and_wrap_body_under_account_key( responses.calls[0].request.body == b'{"account": {"name": "New Team Account"}}' ) + + @pytest.mark.parametrize( + "status_code,response_json,expected_error_message", + [ + ( + conftest.UNAUTHORIZED_STATUS_CODE, + conftest.UNAUTHORIZED_RESPONSE, + conftest.UNAUTHORIZED_ERROR_MESSAGE, + ), + ( + conftest.FORBIDDEN_STATUS_CODE, + conftest.FORBIDDEN_RESPONSE, + conftest.FORBIDDEN_ERROR_MESSAGE, + ), + ( + conftest.NOT_FOUND_STATUS_CODE, + conftest.NOT_FOUND_RESPONSE, + conftest.NOT_FOUND_ERROR_MESSAGE, + ), + ( + conftest.RATE_LIMIT_ERROR_STATUS_CODE, + conftest.RATE_LIMIT_ERROR_RESPONSE, + conftest.RATE_LIMIT_ERROR_MESSAGE, + ), + ], + ) + @responses.activate + def test_delete_should_raise_api_errors( + self, + client: SubAccountsApi, + status_code: int, + response_json: dict, + expected_error_message: str, + ) -> None: + responses.delete( + f"{BASE_SUB_ACCOUNTS_URL}/{SUB_ACCOUNT_ID}", + status=status_code, + json=response_json, + ) + + with pytest.raises(APIError) as exc_info: + client.delete(SUB_ACCOUNT_ID) + + assert expected_error_message in str(exc_info.value) + + @responses.activate + def test_delete_should_return_deleted_object(self, client: SubAccountsApi) -> None: + responses.delete( + f"{BASE_SUB_ACCOUNTS_URL}/{SUB_ACCOUNT_ID}", + status=204, + ) + + result = client.delete(SUB_ACCOUNT_ID) + + assert isinstance(result, DeletedObject) + assert result.id == SUB_ACCOUNT_ID From 6d24cc99af7b3ddf519b83b517f2ccac4ed0b641 Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Tue, 8 Sep 2026 11:01:59 +0200 Subject: [PATCH 2/2] add sub account deletion to the example and README --- README.md | 2 +- examples/organizations/sub_accounts.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0cd5f38..d2c976c 100644 --- a/README.md +++ b/README.md @@ -276,7 +276,7 @@ The same situation applies to both `client.batch_send()` and `client.sending_api - Permissions listing – [`general/permissions.py`](examples/general/permissions.py) ### Organizations API: -- Sub-Accounts management – [`organizations/sub_accounts.py`](examples/organizations/sub_accounts.py) +- Sub-Accounts management (list, create, delete) – [`organizations/sub_accounts.py`](examples/organizations/sub_accounts.py) ## Contributing diff --git a/examples/organizations/sub_accounts.py b/examples/organizations/sub_accounts.py index 2379b3c..238a247 100644 --- a/examples/organizations/sub_accounts.py +++ b/examples/organizations/sub_accounts.py @@ -1,6 +1,7 @@ import os import mailtrap as mt +from mailtrap.models.common import DeletedObject from mailtrap.models.organizations import SubAccount API_KEY = os.environ["MAILTRAP_API_KEY"] @@ -18,9 +19,16 @@ def create_sub_account(name: str) -> SubAccount: return sub_accounts_api.create(mt.CreateSubAccountParams(name=name)) +def delete_sub_account(sub_account_id: int) -> DeletedObject: + return sub_accounts_api.delete(sub_account_id=sub_account_id) + + if __name__ == "__main__": sub_accounts = list_sub_accounts() print(sub_accounts) created = create_sub_account("New Team Account") print(created) + + deleted = delete_sub_account(created.id) + print(deleted)