From 8d681a47d1ac1f66444ab4cc92dea4b2e223a038 Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Tue, 8 Sep 2026 11:00:11 +0200 Subject: [PATCH 1/5] add sub account deletion --- src/__tests__/lib/api/Organizations.test.ts | 1 + .../lib/api/resources/SubAccounts.test.ts | 32 +++++++++++++++++++ src/lib/api/resources/SubAccounts.ts | 17 ++++++++++ src/types/api/sub-accounts.ts | 3 ++ 4 files changed, 53 insertions(+) diff --git a/src/__tests__/lib/api/Organizations.test.ts b/src/__tests__/lib/api/Organizations.test.ts index c32a4450..8282bf74 100644 --- a/src/__tests__/lib/api/Organizations.test.ts +++ b/src/__tests__/lib/api/Organizations.test.ts @@ -12,6 +12,7 @@ describe("lib/api/Organizations: ", () => { expect(organizationsAPI).toHaveProperty("subAccounts"); expect(typeof organizationsAPI.subAccounts.getList).toBe("function"); expect(typeof organizationsAPI.subAccounts.create).toBe("function"); + expect(typeof organizationsAPI.subAccounts.delete).toBe("function"); }); }); }); diff --git a/src/__tests__/lib/api/resources/SubAccounts.test.ts b/src/__tests__/lib/api/resources/SubAccounts.test.ts index 00bdddbb..fa144fb4 100644 --- a/src/__tests__/lib/api/resources/SubAccounts.test.ts +++ b/src/__tests__/lib/api/resources/SubAccounts.test.ts @@ -20,6 +20,7 @@ describe("lib/api/resources/SubAccounts: ", () => { it("initializes with all necessary params.", () => { expect(subAccountsAPI).toHaveProperty("getList"); expect(subAccountsAPI).toHaveProperty("create"); + expect(subAccountsAPI).toHaveProperty("delete"); }); }); }); @@ -108,4 +109,35 @@ describe("lib/api/resources/SubAccounts: ", () => { } }); }); + + describe("delete(): ", () => { + const subAccountId = 12347; + const endpoint = `${GENERAL_ENDPOINT}/api/organizations/${organizationId}/sub_accounts/${subAccountId}`; + + it("deletes a sub account, returning nothing (204 No Content).", async () => { + expect.assertions(2); + + mock.onDelete(endpoint).reply(204); + const result = await subAccountsAPI.delete(subAccountId); + + expect(mock.history.delete[0].url).toEqual(endpoint); + expect(result).toBeUndefined(); + }); + + it("fails with error.", async () => { + const expectedErrorMessage = "Request failed with status code 404"; + + expect.assertions(2); + + try { + await subAccountsAPI.delete(subAccountId); + } catch (error) { + expect(error).toBeInstanceOf(MailtrapError); + + if (error instanceof MailtrapError) { + expect(error.message).toEqual(expectedErrorMessage); + } + } + }); + }); }); diff --git a/src/lib/api/resources/SubAccounts.ts b/src/lib/api/resources/SubAccounts.ts index da5bb570..1cea0cf7 100644 --- a/src/lib/api/resources/SubAccounts.ts +++ b/src/lib/api/resources/SubAccounts.ts @@ -3,6 +3,7 @@ import { AxiosInstance } from "axios"; import CONFIG from "../../../config"; import { CreateSubAccountParams, + DeleteSubAccountResponse, SubAccount, } from "../../../types/api/sub-accounts"; @@ -39,4 +40,20 @@ export default class SubAccountsApi { return this.client.post(url, data); } + + /** + * 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 deletes the organization + * as well. A repeated call for the same ID fails with `404`. Rate limited to + * 10 requests per minute per organization. Returns nothing (204 No Content). + */ + public async delete(subAccountId: number) { + const url = `${this.subAccountsURL}/${subAccountId}`; + + return this.client.delete< + DeleteSubAccountResponse, + DeleteSubAccountResponse + >(url); + } } diff --git a/src/types/api/sub-accounts.ts b/src/types/api/sub-accounts.ts index 83e7a7d0..a28b73d0 100644 --- a/src/types/api/sub-accounts.ts +++ b/src/types/api/sub-accounts.ts @@ -6,3 +6,6 @@ export type SubAccount = { export type CreateSubAccountParams = { name: string; }; + +/** Delete returns `204 No Content` – there is no response body. */ +export type DeleteSubAccountResponse = void; From 338e28da73f37475d26ee81d6c3522d25181d2a5 Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Tue, 8 Sep 2026 11:00:48 +0200 Subject: [PATCH 2/5] document sub account deletion in the example and README --- README.md | 2 +- examples/sub-accounts/everything.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 39750c1f..7196b661 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ General API: - Permissions listing – [`general/permissions.ts`](examples/general/permissions.ts) - Users listing – [`general/account-accesses.ts`](examples/general/account-accesses.ts) - API tokens CRUD & reset – [`general/api-tokens.ts`](examples/general/api-tokens.ts) -- Sub-accounts (list & create) – [`sub-accounts/everything.ts`](examples/sub-accounts/everything.ts) +- Sub-accounts (list, create & delete) – [`sub-accounts/everything.ts`](examples/sub-accounts/everything.ts) ## Contributing diff --git a/examples/sub-accounts/everything.ts b/examples/sub-accounts/everything.ts index a29992c2..0170390f 100644 --- a/examples/sub-accounts/everything.ts +++ b/examples/sub-accounts/everything.ts @@ -22,6 +22,12 @@ async function subAccountsFlow() { name: "Acme Marketing", }); console.log("Created sub account:", JSON.stringify(created, null, 2)); + + // Delete the sub account created above. Permanent – removes all of its + // data; deleting the organization's last sub account deletes the + // organization as well. + await subAccountsClient.delete(created.id); + console.log("Deleted sub account:", created.id); } catch (error) { console.error("Error in subAccountsFlow:", error instanceof Error ? error.message : String(error)); } From b3f8ba2d4e7a8b30db1b5e1aedd3886e12bf649f Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Wed, 4 Nov 2026 11:46:16 +0100 Subject: [PATCH 3/5] update the permission note for deleting a sub-account --- src/lib/api/resources/SubAccounts.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/api/resources/SubAccounts.ts b/src/lib/api/resources/SubAccounts.ts index 1cea0cf7..ba9ac21f 100644 --- a/src/lib/api/resources/SubAccounts.ts +++ b/src/lib/api/resources/SubAccounts.ts @@ -42,11 +42,11 @@ export default class SubAccountsApi { } /** - * 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 deletes the organization - * as well. A repeated call for the same ID fails with `404`. Rate limited to - * 10 requests per minute per organization. Returns nothing (204 No Content). + * Delete a sub account by ID. Requires organization admin permissions. The + * deletion is permanent and removes all sub-account data; deleting the + * organization's last sub account deletes the organization as well. A + * repeated call for the same ID fails with `404`. Rate limited to 10 + * requests per minute per organization. Returns nothing (204 No Content). */ public async delete(subAccountId: number) { const url = `${this.subAccountsURL}/${subAccountId}`; From a514768e9f47e2a57f36b24fc161fa30a1ab247a Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Wed, 4 Nov 2026 12:38:17 +0100 Subject: [PATCH 4/5] note the delete permission in the sub-account example --- examples/sub-accounts/everything.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/sub-accounts/everything.ts b/examples/sub-accounts/everything.ts index 0170390f..abb48451 100644 --- a/examples/sub-accounts/everything.ts +++ b/examples/sub-accounts/everything.ts @@ -26,6 +26,7 @@ async function subAccountsFlow() { // Delete the sub account created above. Permanent – removes all of its // data; deleting the organization's last sub account deletes the // organization as well. + // Requires organization admin permissions on the token. await subAccountsClient.delete(created.id); console.log("Deleted sub account:", created.id); } catch (error) { From 5657636da41ddd5d41915ac9f0ab520e7c4f6ca1 Mon Sep 17 00:00:00 2001 From: Alex Shchyhol Date: Thu, 5 Nov 2026 17:42:43 +0100 Subject: [PATCH 5/5] resolve sub account delete with undefined, not an empty string --- src/__tests__/lib/api/resources/SubAccounts.test.ts | 2 +- src/lib/api/resources/SubAccounts.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/lib/api/resources/SubAccounts.test.ts b/src/__tests__/lib/api/resources/SubAccounts.test.ts index fa144fb4..a61fe7f7 100644 --- a/src/__tests__/lib/api/resources/SubAccounts.test.ts +++ b/src/__tests__/lib/api/resources/SubAccounts.test.ts @@ -117,7 +117,7 @@ describe("lib/api/resources/SubAccounts: ", () => { it("deletes a sub account, returning nothing (204 No Content).", async () => { expect.assertions(2); - mock.onDelete(endpoint).reply(204); + mock.onDelete(endpoint).reply(204, ""); const result = await subAccountsAPI.delete(subAccountId); expect(mock.history.delete[0].url).toEqual(endpoint); diff --git a/src/lib/api/resources/SubAccounts.ts b/src/lib/api/resources/SubAccounts.ts index ba9ac21f..1bf248b2 100644 --- a/src/lib/api/resources/SubAccounts.ts +++ b/src/lib/api/resources/SubAccounts.ts @@ -51,7 +51,7 @@ export default class SubAccountsApi { public async delete(subAccountId: number) { const url = `${this.subAccountsURL}/${subAccountId}`; - return this.client.delete< + await this.client.delete< DeleteSubAccountResponse, DeleteSubAccountResponse >(url);