Skip to content

Add missing endpoints#65

Merged
IgorDobryn merged 14 commits into
mainfrom
MT-21863-php-sdk-add-webhooks-api-tokens-and-sub-accounts-endpoints
May 18, 2026
Merged

Add missing endpoints#65
IgorDobryn merged 14 commits into
mainfrom
MT-21863-php-sdk-add-webhooks-api-tokens-and-sub-accounts-endpoints

Conversation

@IgorDobryn
Copy link
Copy Markdown
Contributor

@IgorDobryn IgorDobryn commented Apr 30, 2026

Motivation

Changes

Webhooks

  • GET /api/accounts/{account_id}/webhooks
  • POST /api/accounts/{account_id}/webhooks
  • GET /api/accounts/{account_id}/webhooks/{webhook_id}
  • PATCH /api/accounts/{account_id}/webhooks/{webhook_id}
  • DELETE /api/accounts/{account_id}/webhooks/{webhook_id}

API Tokens

  • GET /api/accounts/{account_id}/api_tokens
  • POST /api/accounts/{account_id}/api_tokens
  • GET /api/accounts/{account_id}/api_tokens/{id}
  • DELETE /api/accounts/{account_id}/api_tokens/{id}
  • POST /api/accounts/{account_id}/api_tokens/{id}/reset

Sub-Accounts

  • GET /api/organizations/{organization_id}/sub_accounts
  • POST /api/organizations/{organization_id}/sub_accounts

Sandbox Messages

  • POST /api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/forward
  • GET /api/accounts/{account_id}/inboxes/{inbox_id}/messages/{message_id}/mail_headers

Summary by CodeRabbit

Release Notes

  • New Features

    • API token management: create, list, retrieve, reset, and delete tokens
    • Sub-account management: list and create organization sub-accounts
    • Webhook management: full CRUD operations for sending webhooks with configuration options
    • Enhanced sandbox messaging: forward messages and retrieve mail headers
    • New Stats API endpoints: retrieve statistics by domain, category, email service provider, and date
    • Configuration support: five new Mailtrap environment variables for secure credential management
  • Documentation

    • New example scripts demonstrating API tokens, sub-accounts, webhooks, and message operations

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c767315a-e69d-4013-941b-8381db638c77

📥 Commits

Reviewing files that changed from the base of the PR and between 02a42b6 and e0cd55b.

📒 Files selected for processing (9)
  • src/Api/General/ApiToken.php
  • src/Api/General/Organization.php
  • src/Api/General/SubAccount.php
  • src/Api/Sandbox/Message.php
  • src/Api/Sending/Webhook.php
  • src/DTO/Request/Webhook/UpdateWebhook.php
  • tests/Api/General/SubAccountTest.php
  • tests/Api/Sandbox/MessageTest.php
  • tests/Api/Sending/WebhookTest.php
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/Api/General/Organization.php
  • src/DTO/Request/Webhook/UpdateWebhook.php
  • tests/Api/Sandbox/MessageTest.php
  • tests/Api/Sending/WebhookTest.php
  • src/Api/Sandbox/Message.php

📝 Walkthrough

Walkthrough

This PR extends the Mailtrap PHP SDK with four new feature areas: webhook management (create, read, update, delete with validation), account API token lifecycle operations, organization-scoped sub-account management, and new sandbox message operations (mail headers retrieval and message forwarding). Core changes include a refactored permissions serialization system, comprehensive webhook and request DTOs, updated client entry points with API mappings, and full test coverage with example scripts.

Changes

Mailtrap SDK Feature Expansion

Layer / File(s) Summary
Webhook Data Model & Vocabulary
src/DTO/Request/Webhook/Webhook.php, src/DTO/Request/Webhook/WebhookInterface.php, src/DTO/Request/Webhook/CreateWebhook.php, src/DTO/Request/Webhook/UpdateWebhook.php
Webhook vocabulary constants (types, payload formats, sending streams, event names) and request DTOs with constructor validation enforce allowed webhook types and required fields for email_sending webhooks (eventTypes, sendingStream).
Permissions Serialization Refactoring
src/DTO/Request/Permission/Permissions.php, src/Api/General/Permission.php, tests/Api/General/PermissionTest.php
Permissions DTO now provides toPayload() method that serializes and validates non-empty permissions; Permission::update() calls this directly, removing the private getPayload() helper and centralizing validation.
Webhook API Client Implementation
src/Api/Sending/Webhook.php
New account-scoped webhook service client providing list, fetch by ID, create, update (PATCH), and delete operations with request/response handling and validation that updates contain at least one updatable field.
API Token Client & Account Tokens
src/Api/General/ApiToken.php
New account-scoped API token service client providing list, fetch, create (with Permissions), reset, and delete operations with request/response handling and account-scoped URL routing.
Organization & Sub-Account APIs
src/Api/General/Organization.php, src/Api/General/SubAccount.php
Organization entry point and organization-scoped sub-account service client for listing and creating sub-accounts with request/response handling and organization-scoped URL routing.
Sandbox Message Extensions
src/Api/Sandbox/Message.php
Two new methods: getMailHeaders() retrieves parsed mail headers via GET; forward() sends a message to a specified recipient with email validation and POST request.
Client Wiring & API Mappings
src/MailtrapGeneralClient.php, src/MailtrapSendingClient.php
MailtrapGeneralClient and MailtrapSendingClient updated to register apiTokens and webhooks endpoints via API_MAPPING constants and PHPDoc method annotations.
Webhook API Tests
tests/Api/Sending/WebhookTest.php
Comprehensive PHPUnit coverage for webhook CRUD operations, HTTP method/URL assertions, payload validation, response parsing, and error cases (404, 422, empty update payload).
API Token Tests
tests/Api/General/ApiTokenTest.php
PHPUnit coverage for token lifecycle (list, get, create, delete, reset) with HTTP mocking, response assertions, permission validation, and error handling (401, 404, 422).
Sub-Account Tests
tests/Api/General/SubAccountTest.php
PHPUnit coverage for sub-account operations (list, create) with HTTP mocking, response parsing, and error cases (403 forbidden, 422 validation error).
Sandbox Message Tests
tests/Api/Sandbox/MessageTest.php
Test extensions for new message operations: getMailHeaders with response parsing and forward with success, unverified-recipient error, and email validation error cases.
Client Test Infrastructure Updates
tests/MailtrapGeneralClientTest.php, tests/MailtrapSendingClientTest.php, tests/MailtrapTestCase.php
Test infrastructure updated to treat apiTokens and webhooks as account-scoped mappings in instance provider logic; FAKE_ORGANIZATION_ID constant added to base test case.
Examples & Documentation
examples/api-tokens/all.php, examples/webhooks/all.php, examples/sub-accounts/all.php, examples/testing/messages.php, CHANGELOG.md
Complete example scripts demonstrating all new API operations (token lifecycle, webhook CRUD, sub-account management, message operations); CHANGELOG entries describe new Stats, Tokens, Sub-Accounts, Message, and Webhooks APIs.
Environment Configuration
.envrc
Five Mailtrap environment variables (account ID, organization ID, sandbox ID, API keys) exported from 1Password references for local development.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • mailtrap/mailtrap-php#62: Both PRs update MailtrapSendingClient's API_MAPPING to register new Sending API endpoints—this PR adds webhooks, while the related PR added emailLogs.

Suggested reviewers

  • VladimirTaytor
  • piobeny
  • yanchuk

Poem

🐇 Hops through the SDK with glee,
Four new APIs for all to see—
Webhooks, tokens, messages in flight,
Sub-accounts nested, permission's right. 🚀

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Add missing endpoints' is vague and generic, using non-descriptive phrasing that doesn't convey meaningful information about which endpoints or what specific changes are included. Use a more descriptive title that specifies the main endpoints being added, such as 'Add Webhooks, API Tokens, and Sub-Accounts endpoints' or 'Implement missing Mailtrap API endpoints'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description is partially complete. It includes a detailed 'Changes' section listing all endpoint additions across multiple API categories (Webhooks, API Tokens, Sub-Accounts, Sandbox Messages), but the 'Motivation' section is empty and 'How to test' and 'Images and GIFs' sections are missing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MT-21863-php-sdk-add-webhooks-api-tokens-and-sub-accounts-endpoints

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@IgorDobryn IgorDobryn changed the title Mt 21863 php sdk add webhooks api tokens and sub accounts endpoints Add missing endpoints Apr 30, 2026
@IgorDobryn IgorDobryn force-pushed the MT-21863-php-sdk-add-webhooks-api-tokens-and-sub-accounts-endpoints branch from d240511 to 743ef96 Compare May 1, 2026 06:48
@IgorDobryn IgorDobryn marked this pull request as ready for review May 4, 2026 12:07
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/api-tokens/all.php`:
- Around line 12-14: Add a guard that checks the required environment variables
before using them: validate $_ENV['MAILTRAP_ACCOUNT_ID'] and
$_ENV['MAILTRAP_API_KEY'] and throw/exit with a clear error message if either is
missing so the example fails fast; update the bootstrap around $accountId, the
Config(...) call, and the MailtrapGeneralClient(...)->apiTokens(...) usage to
rely on the validated values (or sanitized variables) rather than accessing
$_ENV directly.

In `@examples/webhooks/all.php`:
- Around line 12-14: The example currently reads $_ENV['MAILTRAP_ACCOUNT_ID']
and $_ENV['MAILTRAP_API_KEY'] directly which can be null and emit notices; add a
small bootstrap guard before creating Config and calling (new
MailtrapSendingClient($config))->webhooks($accountId) that checks required env
vars (MAILTRAP_ACCOUNT_ID and MAILTRAP_API_KEY), optionally MAILTRAP_DOMAIN_ID
if domain-scoped webhooks are intended, and exits with a clear error message
when any are missing so Config is never constructed with null values and the
script fails fast and informatively.

In `@src/DTO/Request/Webhook/CreateWebhook.php`:
- Around line 21-37: CreateWebhook currently allows building invalid payloads
for type-specific webhooks; add validation in the CreateWebhook constructor (or
in toArray) to enforce required fields based on $webhookType (e.g., when
$webhookType === 'email_sending' require non-null $sendingStream and non-empty
$eventTypes), throw a clear exception (InvalidArgumentException) on violation,
or alternatively refactor by splitting CreateWebhook into separate DTOs per
webhook kind (e.g., EmailSendingWebhook with required $sendingStream and
EventTypes) and use the appropriate DTO where needed; update
CreateWebhook::toArray to assume validated fields so it cannot serialize invalid
requests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 60e92d97-9770-4f7f-abc9-d3da6787856a

📥 Commits

Reviewing files that changed from the base of the PR and between 84717fd and 743ef96.

📒 Files selected for processing (24)
  • .envrc
  • CHANGELOG.md
  • examples/api-tokens/all.php
  • examples/sub-accounts/all.php
  • examples/testing/messages.php
  • examples/webhooks/all.php
  • src/Api/General/ApiToken.php
  • src/Api/Organization/OrganizationInterface.php
  • src/Api/Organization/SubAccount.php
  • src/Api/Sandbox/Message.php
  • src/Api/Sending/Webhook.php
  • src/DTO/Request/Webhook/CreateWebhook.php
  • src/DTO/Request/Webhook/UpdateWebhook.php
  • src/DTO/Request/Webhook/WebhookInterface.php
  • src/MailtrapGeneralClient.php
  • src/MailtrapOrganizationClient.php
  • src/MailtrapSendingClient.php
  • tests/Api/General/ApiTokenTest.php
  • tests/Api/Organization/SubAccountTest.php
  • tests/Api/Sandbox/MessageTest.php
  • tests/Api/Sending/WebhookTest.php
  • tests/MailtrapGeneralClientTest.php
  • tests/MailtrapOrganizationClientTest.php
  • tests/MailtrapSendingClientTest.php

Comment on lines +12 to +14
$accountId = $_ENV['MAILTRAP_ACCOUNT_ID'];
$config = new Config($_ENV['MAILTRAP_API_KEY']); #your API token from here https://mailtrap.io/api-tokens
$apiTokens = (new MailtrapGeneralClient($config))->apiTokens($accountId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fail fast when required example env vars are missing.

$_ENV['MAILTRAP_ACCOUNT_ID'] and $_ENV['MAILTRAP_API_KEY'] are accessed directly, so a missing environment setup will surface as notices or a later client error instead of a clear bootstrap failure. A small guard here would make the example much easier to run.

♻️ Suggested bootstrap guard
 require __DIR__ . '/../../vendor/autoload.php';
 
+foreach (['MAILTRAP_ACCOUNT_ID', 'MAILTRAP_API_KEY'] as $envVar) {
+    if (empty($_ENV[$envVar])) {
+        throw new RuntimeException(sprintf('%s must be set for this example.', $envVar));
+    }
+}
+
 $accountId = $_ENV['MAILTRAP_ACCOUNT_ID'];
 $config = new Config($_ENV['MAILTRAP_API_KEY']); `#your` API token from here https://mailtrap.io/api-tokens
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/api-tokens/all.php` around lines 12 - 14, Add a guard that checks
the required environment variables before using them: validate
$_ENV['MAILTRAP_ACCOUNT_ID'] and $_ENV['MAILTRAP_API_KEY'] and throw/exit with a
clear error message if either is missing so the example fails fast; update the
bootstrap around $accountId, the Config(...) call, and the
MailtrapGeneralClient(...)->apiTokens(...) usage to rely on the validated values
(or sanitized variables) rather than accessing $_ENV directly.

Comment thread examples/webhooks/all.php
Comment on lines +12 to +14
$accountId = $_ENV['MAILTRAP_ACCOUNT_ID'];
$config = new Config($_ENV['MAILTRAP_API_KEY']); #your API token from here https://mailtrap.io/api-tokens
$webhooks = (new MailtrapSendingClient($config))->webhooks($accountId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fail fast when required example env vars are missing.

Reading $_ENV['MAILTRAP_ACCOUNT_ID'] / $_ENV['MAILTRAP_API_KEY'] directly will emit notices and hand null to Config when the example is run without setup. A small bootstrap guard would make the script much more robust. If you want this sample to demonstrate domain-scoped webhooks, consider documenting MAILTRAP_DOMAIN_ID alongside the other env vars as well.

♻️ Suggested bootstrap guard
 <?php
 
 use Mailtrap\Config;
@@
 require __DIR__ . '/../../vendor/autoload.php';
 
+foreach (['MAILTRAP_ACCOUNT_ID', 'MAILTRAP_API_KEY'] as $envVar) {
+    if (empty($_ENV[$envVar])) {
+        throw new RuntimeException(sprintf('%s must be set for this example.', $envVar));
+    }
+}
+
 $accountId = $_ENV['MAILTRAP_ACCOUNT_ID'];
 $config = new Config($_ENV['MAILTRAP_API_KEY']); `#your` API token from here https://mailtrap.io/api-tokens
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$accountId = $_ENV['MAILTRAP_ACCOUNT_ID'];
$config = new Config($_ENV['MAILTRAP_API_KEY']); #your API token from here https://mailtrap.io/api-tokens
$webhooks = (new MailtrapSendingClient($config))->webhooks($accountId);
<?php
use Mailtrap\Config;
use Mailtrap\MailtrapSendingClient;
require __DIR__ . '/../../vendor/autoload.php';
foreach (['MAILTRAP_ACCOUNT_ID', 'MAILTRAP_API_KEY'] as $envVar) {
if (empty($_ENV[$envVar])) {
throw new RuntimeException(sprintf('%s must be set for this example.', $envVar));
}
}
$accountId = $_ENV['MAILTRAP_ACCOUNT_ID'];
$config = new Config($_ENV['MAILTRAP_API_KEY']); `#your` API token from here https://mailtrap.io/api-tokens
$webhooks = (new MailtrapSendingClient($config))->webhooks($accountId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/webhooks/all.php` around lines 12 - 14, The example currently reads
$_ENV['MAILTRAP_ACCOUNT_ID'] and $_ENV['MAILTRAP_API_KEY'] directly which can be
null and emit notices; add a small bootstrap guard before creating Config and
calling (new MailtrapSendingClient($config))->webhooks($accountId) that checks
required env vars (MAILTRAP_ACCOUNT_ID and MAILTRAP_API_KEY), optionally
MAILTRAP_DOMAIN_ID if domain-scoped webhooks are intended, and exits with a
clear error message when any are missing so Config is never constructed with
null values and the script fails fast and informatively.

Comment thread src/DTO/Request/Webhook/CreateWebhook.php
Comment thread src/MailtrapOrganizationClient.php Outdated
*
* Class MailtrapOrganizationClient
*/
final class MailtrapOrganizationClient extends AbstractMailtrapClient
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move SubAccount into MailtrapGeneralClient instead of introducing a new client

Looking at Mailtrap's own UI, Organizations lives in the ACCOUNT MANAGEMENT group alongside Accounts, API Tokens, Permissions, and Billing — all of which are exposed through MailtrapGeneralClient. To keep the SDK aligned with that grouping, and to make ownership explicit (sub-accounts belong to an organization, not directly to the general client), I'd suggest introducing an intermediate organizations layer rather than exposing subAccounts at the top level.

DX would read:

  (new MailtrapGeneralClient($config))
      ->organizations($organizationId)
      ->subAccounts()
      ->getSubAccounts();

// src/MailtrapGeneralClient.php
  public const API_MAPPING = [
      'accounts'        => Api\General\Account::class,
      'users'           => Api\General\User::class,
      'permissions'     => Api\General\Permission::class,
      'contacts'        => Api\General\Contact::class,
      'emailTemplates'  => Api\General\EmailTemplate::class,
      'billing'         => Api\General\Billing::class,
      'apiTokens'       => Api\General\ApiToken::class,
      'organizations'   => Api\General\Organization::class, // <-- new entry point
  ];

Comment thread src/Api/General/ApiToken.php Outdated
Comment on lines +98 to +111
private function getPermissionsPayload(Permissions $permissions): array
{
$payload = [];
foreach ($permissions->getAll() as $permission) {
$payload[] = $permission->toArray();
}

if (count($payload) === 0) {
throw new RuntimeException('At least one "permission" object should be added to create an API token');
}

return $payload;
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicates Permission::getPayload()

ApiToken::getPermissionsPayload() is essentially identical to Permission::getPayload() — same loop, same count() guard, same RuntimeException. Could we lift the conversion onto the DTO itself so any future caller benefits?

  // src/DTO/Request/Permission/Permissions.php
  public function toPayload(): array
  {
      $payload = array_map(fn($p) => $p->toArray(), $this->getAll());
      if ($payload === []) {
          throw new RuntimeException('At least one "permission" object must be provided');
      }
      return $payload;
  }

Then both Permission::update() and ApiToken::createApiToken() call $permissions->toPayload() and the private duplicates go away.


use Mailtrap\DTO\Request\RequestInterface;

interface WebhookInterface extends RequestInterface
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WebhookInterface mixes a DTO marker with domain constants

WebhookInterface extends RequestInterface (a DTO contract) but also carries the entire webhook vocabulary (TYPE_, EVENT_, PAYLOAD_FORMAT_, SENDING_STREAM_). End-user code in examples/webhooks/all.php has to import a DTO marker just to reach WebhookInterface::EVENT_DELIVERY, and adding a new event type becomes an interface change.

Could we keep WebhookInterface as a tiny marker (no constants) and move the constants into a dedicated holder, e.g. a Webhook enum/constants class? Users would then write Webhook::EVENT_DELIVERY which reads more naturally.

Comment on lines +10 to +11
* Only `url`, `active`, `payload_format`, and `event_types` can be updated after creation.
* `webhook_type`, `sending_mode`, and `mailsend_domain_id` are immutable.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sending_mode and mailsend_domain_id don't appear anywhere in the codebase — CreateWebhook and the API use sending_stream and domain_id.

* @param int|null $domainId Scope to a specific domain id (null = all account domains)
* @param bool|null $active Defaults to true on the server side
*/
public function __construct(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor docblock says eventTypes and sendingStream are required for TYPE_EMAIL_SENDING, but both default to empty/null with no runtime check. Users only learn about the constraint via a 422 from the server — exactly the validation a typed DTO should catch:

  if ($webhookType === WebhookInterface::TYPE_EMAIL_SENDING) {
      if ($eventTypes === []) {
          throw new InvalidArgumentException('"eventTypes" is required for email_sending webhooks');
      }
      if ($sendingStream === null) {
          throw new InvalidArgumentException('"sendingStream" is required for email_sending webhooks');
      }
  }

It would also be worth validating webhookType against TYPE_EMAIL_SENDING / TYPE_AUDIT_LOG.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
examples/webhooks/all.php (1)

12-14: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add fail-fast guards for required env vars before constructing the client.

On Line 12 and Line 13, direct $_ENV[...] access can produce notices and pass null into Config when setup is incomplete. This was already flagged and is still unresolved.

Proposed fix
 require __DIR__ . '/../../vendor/autoload.php';

+foreach (['MAILTRAP_ACCOUNT_ID', 'MAILTRAP_API_KEY'] as $envVar) {
+    if (empty($_ENV[$envVar])) {
+        throw new RuntimeException(sprintf('%s must be set for this example.', $envVar));
+    }
+}
+
 $accountId = $_ENV['MAILTRAP_ACCOUNT_ID'];
 $config = new Config($_ENV['MAILTRAP_API_KEY']); `#your` API token from here https://mailtrap.io/api-tokens
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/webhooks/all.php` around lines 12 - 14, Add fail-fast checks for the
required env vars before constructing Config and MailtrapSendingClient: validate
$_ENV['MAILTRAP_ACCOUNT_ID'] and $_ENV['MAILTRAP_API_KEY'] are set and non-empty
(e.g., using isset() and !== ''), and if not, emit a clear error and exit/throw
so null isn't passed into new Config($apiKey). Update the block around
$accountId, new Config(...), and (new MailtrapSendingClient(...))->webhooks(...)
to perform these guards first and only construct Config and
MailtrapSendingClient when the checks pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/sub-accounts/all.php`:
- Around line 9-11: The code reads $_ENV values directly and may fail when vars
are missing; before constructing Config and calling
MailtrapGeneralClient->organization(), validate that MAILTRAP_API_KEY and
MAILTRAP_ORGANIZATION_ID are present, fail fast with a clear error/exception if
they are not, and normalize MAILTRAP_ORGANIZATION_ID to an integer (e.g., via
intval() or (int) cast) when assigning $organizationId so the subsequent call to
MailtrapGeneralClient($config)->organization($organizationId)->subAccounts()
receives a validated integer ID.

---

Duplicate comments:
In `@examples/webhooks/all.php`:
- Around line 12-14: Add fail-fast checks for the required env vars before
constructing Config and MailtrapSendingClient: validate
$_ENV['MAILTRAP_ACCOUNT_ID'] and $_ENV['MAILTRAP_API_KEY'] are set and non-empty
(e.g., using isset() and !== ''), and if not, emit a clear error and exit/throw
so null isn't passed into new Config($apiKey). Update the block around
$accountId, new Config(...), and (new MailtrapSendingClient(...))->webhooks(...)
to perform these guards first and only construct Config and
MailtrapSendingClient when the checks pass.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9264dc17-1ce2-4eee-ae1d-142bac6e15b6

📥 Commits

Reviewing files that changed from the base of the PR and between 743ef96 and 02a42b6.

📒 Files selected for processing (18)
  • CHANGELOG.md
  • examples/sub-accounts/all.php
  • examples/webhooks/all.php
  • src/Api/General/ApiToken.php
  • src/Api/General/Organization.php
  • src/Api/General/Permission.php
  • src/DTO/Request/Permission/Permissions.php
  • src/DTO/Request/Webhook/CreateWebhook.php
  • src/DTO/Request/Webhook/UpdateWebhook.php
  • src/DTO/Request/Webhook/Webhook.php
  • src/DTO/Request/Webhook/WebhookInterface.php
  • src/MailtrapGeneralClient.php
  • tests/Api/General/ApiTokenTest.php
  • tests/Api/General/PermissionTest.php
  • tests/Api/Organization/SubAccountTest.php
  • tests/Api/Sending/WebhookTest.php
  • tests/MailtrapGeneralClientTest.php
  • tests/MailtrapTestCase.php
✅ Files skipped from review due to trivial changes (2)
  • tests/MailtrapGeneralClientTest.php
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/Api/Organization/SubAccountTest.php
  • src/DTO/Request/Webhook/UpdateWebhook.php
  • tests/Api/General/ApiTokenTest.php
  • tests/Api/Sending/WebhookTest.php
  • src/MailtrapGeneralClient.php

Comment on lines +9 to +11
$organizationId = $_ENV['MAILTRAP_ORGANIZATION_ID'];
$config = new Config($_ENV['MAILTRAP_API_KEY']); #your API token from here https://mailtrap.io/api-tokens
$subAccounts = (new MailtrapGeneralClient($config))->organization($organizationId)->subAccounts();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard required env vars before client construction.

Direct $_ENV[...] reads here can fail noisily when vars are missing; add presence checks and normalize MAILTRAP_ORGANIZATION_ID to an integer.

Suggested patch
-$organizationId = $_ENV['MAILTRAP_ORGANIZATION_ID'];
-$config = new Config($_ENV['MAILTRAP_API_KEY']); `#your` API token from here https://mailtrap.io/api-tokens
+$apiKey = $_ENV['MAILTRAP_API_KEY'] ?? null;
+$organizationIdRaw = $_ENV['MAILTRAP_ORGANIZATION_ID'] ?? null;
+if (!$apiKey || !$organizationIdRaw || !ctype_digit((string) $organizationIdRaw)) {
+    throw new RuntimeException('Set MAILTRAP_API_KEY and numeric MAILTRAP_ORGANIZATION_ID in environment.');
+}
+$organizationId = (int) $organizationIdRaw;
+$config = new Config($apiKey); // your API token from https://mailtrap.io/api-tokens
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$organizationId = $_ENV['MAILTRAP_ORGANIZATION_ID'];
$config = new Config($_ENV['MAILTRAP_API_KEY']); #your API token from here https://mailtrap.io/api-tokens
$subAccounts = (new MailtrapGeneralClient($config))->organization($organizationId)->subAccounts();
$apiKey = $_ENV['MAILTRAP_API_KEY'] ?? null;
$organizationIdRaw = $_ENV['MAILTRAP_ORGANIZATION_ID'] ?? null;
if (!$apiKey || !$organizationIdRaw || !ctype_digit((string) $organizationIdRaw)) {
throw new RuntimeException('Set MAILTRAP_API_KEY and numeric MAILTRAP_ORGANIZATION_ID in environment.');
}
$organizationId = (int) $organizationIdRaw;
$config = new Config($apiKey); // your API token from https://mailtrap.io/api-tokens
$subAccounts = (new MailtrapGeneralClient($config))->organization($organizationId)->subAccounts();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/sub-accounts/all.php` around lines 9 - 11, The code reads $_ENV
values directly and may fail when vars are missing; before constructing Config
and calling MailtrapGeneralClient->organization(), validate that
MAILTRAP_API_KEY and MAILTRAP_ORGANIZATION_ID are present, fail fast with a
clear error/exception if they are not, and normalize MAILTRAP_ORGANIZATION_ID to
an integer (e.g., via intval() or (int) cast) when assigning $organizationId so
the subsequent call to
MailtrapGeneralClient($config)->organization($organizationId)->subAccounts()
receives a validated integer ID.

@IgorDobryn IgorDobryn requested a review from gaalferov May 11, 2026 11:51
'emailTemplates' => Api\General\EmailTemplate::class,
'billing' => Api\General\Billing::class,
'apiTokens' => Api\General\ApiToken::class,
'organization' => Api\General\Organization::class,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming convention — singular vs plural

The organization key is the only entry in API_MAPPING that uses singular form — every other entry is plural (accounts, users, permissions, contacts, emailTemplates, billing, apiTokens). Users following the established convention will instinctively write ->organizations($id) and hit BadMethodCallException.

Could we rename for consistency?

* @method Api\General\Organization  organizations(int $organizationId)
...
'organizations' => Api\General\Organization::class,

The tests/MailtrapGeneralClientTest.php:32 match arm needs the same rename.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea was that a token can't have access to multiple organizations, so I kept singular

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, makes sense

Comment thread src/Api/Organization/SubAccount.php Outdated
Comment thread src/Api/Sending/Webhook.php Outdated
Comment thread src/DTO/Request/Webhook/UpdateWebhook.php Outdated
Comment thread src/Api/General/ApiToken.php
Comment thread src/Api/Sending/Webhook.php
Comment thread src/Api/General/SubAccount.php
->method('httpGet')
->with(AbstractApi::DEFAULT_HOST . '/api/accounts/' . self::FAKE_ACCOUNT_ID . '/webhooks')
->willReturn(
new Response(200, ['Content-Type' => 'application/json'], json_encode(['data' => [$this->getExpectedWebhookResponse()]]))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent response envelope vs ApiTokenTest

Webhook fixtures wrap responses in {"data": {...}} (here and lines 69, 118, 207, 233), while tests/Api/General/ApiTokenTest.php fixtures (lines 48, 88, 140, 185) return the object directly with no wrapper. Since both test suites mock httpGet/httpPost, they're green either way — but this is implicitly asserting two different response shapes for two newly added endpoints of the same API.

If the assumed shape is wrong, production parsers in user code will hit KeyError despite green tests — classic mock fail mode. Could we verify against the live Mailtrap API (running examples/webhooks/all.php and examples/api-tokens/all.php against a real account) and align the fixtures?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"data": {} is overall convention for new endpoints. Also, I ran this code agains real API endpoints

Comment thread src/Api/Sandbox/Message.php
@IgorDobryn IgorDobryn merged commit 6f88094 into main May 18, 2026
22 checks passed
@IgorDobryn IgorDobryn deleted the MT-21863-php-sdk-add-webhooks-api-tokens-and-sub-accounts-endpoints branch May 18, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants