From 3f0179ef1563dc8be33bd8735ebc627ed46c44a0 Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Tue, 8 Sep 2026 11:01:51 +0200 Subject: [PATCH 1/2] add deleteSubAccount to the organization sub-accounts API --- src/Api/General/SubAccount.php | 16 +++++++++ tests/Api/General/SubAccountTest.php | 50 +++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/Api/General/SubAccount.php b/src/Api/General/SubAccount.php index 3e12e91..2a54d43 100644 --- a/src/Api/General/SubAccount.php +++ b/src/Api/General/SubAccount.php @@ -46,6 +46,22 @@ public function createSubAccount(string $name): ResponseInterface ); } + /** + * Delete a sub-account by ID. Requires sub-account management permissions for the organization. + * The deletion is permanent and removes all sub-account data; deleting the organization's last + * sub-account also deletes the organization. A repeated call for the same ID returns 404. + * Rate limit: 10 requests per minute per organization. + * + * @param int $subAccountId + * @return ResponseInterface + */ + public function deleteSubAccount(int $subAccountId): ResponseInterface + { + return $this->handleResponse( + $this->httpDelete($this->getBasePath() . '/' . $subAccountId) + ); + } + public function getOrganizationId(): int { return $this->organizationId; diff --git a/tests/Api/General/SubAccountTest.php b/tests/Api/General/SubAccountTest.php index cda49f2..60e5cd6 100644 --- a/tests/Api/General/SubAccountTest.php +++ b/tests/Api/General/SubAccountTest.php @@ -24,7 +24,7 @@ protected function setUp(): void { parent::setUp(); $this->subAccount = $this->getMockBuilder(SubAccount::class) - ->onlyMethods(['httpGet', 'httpPost']) + ->onlyMethods(['httpGet', 'httpPost', 'httpDelete']) ->setConstructorArgs([$this->getConfigMock(), self::FAKE_ORGANIZATION_ID]) ->getMock(); } @@ -110,6 +110,54 @@ public function testCreateSubAccountFailsWithValidationError(): void $this->subAccount->createSubAccount($invalidName); } + public function testDeleteSubAccount(): void + { + $subAccountId = 1; + + $this->subAccount->expects($this->once()) + ->method('httpDelete') + ->with(AbstractApi::DEFAULT_HOST . '/api/organizations/' . self::FAKE_ORGANIZATION_ID . '/sub_accounts/' . $subAccountId) + ->willReturn(new Response(204)); + + $response = $this->subAccount->deleteSubAccount($subAccountId); + + $this->assertEquals(204, $response->getStatusCode()); + } + + public function testDeleteSubAccountFailsWithForbidden(): void + { + $subAccountId = 1; + + $this->subAccount->expects($this->once()) + ->method('httpDelete') + ->with(AbstractApi::DEFAULT_HOST . '/api/organizations/' . self::FAKE_ORGANIZATION_ID . '/sub_accounts/' . $subAccountId) + ->willReturn( + new Response(403, ['Content-Type' => 'application/json'], json_encode(['errors' => 'Access forbidden'])) + ); + + $this->expectException(HttpClientException::class); + $this->expectExceptionMessage('Access forbidden'); + + $this->subAccount->deleteSubAccount($subAccountId); + } + + public function testDeleteSubAccountFailsWithNotFound(): void + { + $subAccountId = 1; + + $this->subAccount->expects($this->once()) + ->method('httpDelete') + ->with(AbstractApi::DEFAULT_HOST . '/api/organizations/' . self::FAKE_ORGANIZATION_ID . '/sub_accounts/' . $subAccountId) + ->willReturn( + new Response(404, ['Content-Type' => 'application/json'], json_encode(['error' => 'Not Found'])) + ); + + $this->expectException(HttpClientException::class); + $this->expectExceptionMessage('Not Found'); + + $this->subAccount->deleteSubAccount($subAccountId); + } + private function getExpectedSubAccountResponse(int $id = 1, string $name = 'My sub-account'): array { return [ From d4a545baceb090df79a60fc1e3eebcd6fd01042a Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Tue, 8 Sep 2026 11:01:51 +0200 Subject: [PATCH 2/2] document sub-account deletion in the example, readmes and changelog --- CHANGELOG.md | 2 +- README.md | 1 + examples/README.md | 1 + examples/sub-accounts/all.php | 17 +++++++++++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7df23b2..d21487e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ ## [Unreleased] - Add Stats API with get, byDomain, byCategory, byEmailServiceProvider, byDate endpoints - Add API Tokens API: list, get, create, delete and reset account API tokens -- Add Sub-Accounts API (organization-scoped): list and create organization sub-accounts via `MailtrapGeneralClient->organization($organizationId)->subAccounts()` +- Add Sub-Accounts API (organization-scoped): list, create and delete organization sub-accounts via `MailtrapGeneralClient->organization($organizationId)->subAccounts()` - Add Sandbox Message endpoints: `forward` and `getMailHeaders` - Add Webhooks API: list, get, create, update and delete account webhooks diff --git a/README.md b/README.md index 3f41bcb..5feba27 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,7 @@ Email marketing: General API: - Templates CRUD – [`templates/all.php`](examples/templates/all.php) - API tokens CRUD – [`api-tokens/all.php`](examples/api-tokens/all.php) +- Sub-accounts (list, create, delete) – [`sub-accounts/all.php`](examples/sub-accounts/all.php) - Billing info – [`general/billing.php`](examples/general/billing.php) - Accounts info – [`general/accounts.php`](examples/general/accounts.php) - Permissions listing – [`general/permissions.php`](examples/general/permissions.php) diff --git a/examples/README.md b/examples/README.md index 9c791c6..ced8e02 100644 --- a/examples/README.md +++ b/examples/README.md @@ -73,6 +73,7 @@ Central index of runnable example scripts demonstrating Mailtrap PHP SDK feature | Purpose | File | |---------|------| | API tokens CRUD | [`api-tokens/all.php`](api-tokens/all.php) | +| Sub-accounts (list, create, delete) | [`sub-accounts/all.php`](sub-accounts/all.php) | | Accounts info | [`general/accounts.php`](general/accounts.php) | | Billing info | [`general/billing.php`](general/billing.php) | | Permissions listing | [`general/permissions.php`](general/permissions.php) | diff --git a/examples/sub-accounts/all.php b/examples/sub-accounts/all.php index 2b1b950..7b12890 100644 --- a/examples/sub-accounts/all.php +++ b/examples/sub-accounts/all.php @@ -35,3 +35,20 @@ } catch (Exception $e) { echo 'Caught exception: ', $e->getMessage(), "\n"; } + +/** + * Delete a sub-account from the organization. Requires sub-account management permissions. + * The deletion is permanent and removes all sub-account data; deleting the organization's last + * sub-account also deletes the organization. A repeated call for the same ID returns 404. + * + * DELETE https://mailtrap.io/api/organizations/{organization_id}/sub_accounts/{sub_account_id} + */ +try { + $subAccountId = 12347; + + $response = $subAccounts->deleteSubAccount($subAccountId); + + echo $response->getStatusCode(); // 204 +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +}