PHPackages                             mailcapture/mailcapture-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Mail &amp; Notifications](/categories/mail)
4. /
5. mailcapture/mailcapture-php

ActiveLibrary[Mail &amp; Notifications](/categories/mail)

mailcapture/mailcapture-php
===========================

Official PHP SDK for the MailCapture email testing API

00PHP

Since May 8Pushed 1mo agoCompare

[ Source](https://github.com/MailCapture/mailcapture-php)[ Packagist](https://packagist.org/packages/mailcapture/mailcapture-php)[ RSS](/packages/mailcapture-mailcapture-php/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

mailcapture-php
===============

[](#mailcapture-php)

Official PHP SDK for [MailCapture](https://mailcapture.app) — a real email capture API for integration testing OTP codes, verification links, and other transactional emails.

Requirements
------------

[](#requirements)

- PHP 8.1+
- Guzzle 7 (`guzzlehttp/guzzle`)

Installation
------------

[](#installation)

```
composer require mailcapture/mailcapture-php
```

Quick start
-----------

[](#quick-start)

```
use MailCapture\MailCapture;

$mc = new MailCapture($_ENV['MAILCAPTURE_API_KEY']);
$mc->ping();  // validates key, caches username

// In your test:
$mc->delete('signup');
$yourApp->register($mc->address('signup'));   // e.g. "alice-signup@mailcapture.app"
$email = $mc->waitFor('signup', timeout: 15.0);

echo $email->subject;   // "Verify your account"
echo $email->otp;       // "123456" — extracted automatically
```

The pattern for integration tests
---------------------------------

[](#the-pattern-for-integration-tests)

```
use MailCapture\MailCapture;
use MailCapture\Model\Capture;
use PHPUnit\Framework\TestCase;

class UserRegistrationTest extends TestCase
{
    private static MailCapture $mc;

    public static function setUpBeforeClass(): void
    {
        self::$mc = new MailCapture($_ENV['MAILCAPTURE_API_KEY']);
        self::$mc->ping();
    }

    protected function setUp(): void
    {
        self::$mc->delete('signup');   // clean inbox before every test
    }

    public function testUserReceivesVerificationEmail(): void
    {
        $inbox = self::$mc->inbox('signup');

        $this->app->register($inbox->getAddress()); // "alice-signup@mailcapture.app"

        $email = $inbox->waitFor(timeout: 10.0);

        $this->assertSame('Verify your account', $email->subject);
        $this->assertMatchesRegularExpression('/^\d{6}$/', $email->otp);
        $this->assertLessThan(5000, $email->latencyMs);
    }
}
```

API reference
-------------

[](#api-reference)

### `new MailCapture($apiKey, ...)`

[](#new-mailcaptureapikey-)

```
$mc = new MailCapture(
    apiKey: $_ENV['MAILCAPTURE_API_KEY'],
    baseUrl: 'http://localhost:3002',   // override for local dev (default: https://mailcapture.app)
    requestTimeout: 15.0,              // seconds, default 10.0
    username: 'alice',                 // pre-set to skip ping() (optional)
);
```

---

### `ping()` → `PingResult`

[](#ping--pingresult)

Validates your API key and returns your address template. Caches your `username` so `address()` works without a network call.

```
$result = $mc->ping();
echo $result->username;          // "alice"
echo $result->addressTemplate;   // "alice-{tag}@mailcapture.app"
echo $result->example;           // "alice-signup@mailcapture.app"
```

---

### `waitFor(tag, timeout, pollTimeout, after)` → `Capture`

[](#waitfortag-timeout-polltimeout-after--capture)

Long-polls the API and returns the first email captured for the given tag. The server holds the connection open — no busy-waiting.

```
// Named arguments (recommended)
$email = $mc->waitFor('signup', timeout: 15.0);

// Full options
$email = $mc->waitFor(
    tag: 'signup',
    timeout: 15.0,           // total seconds to wait (default 30)
    pollTimeout: 5,           // per-poll server timeout in seconds, max 30 (default 10)
    after: new DateTime('-30 seconds'),  // only captures after this time
);
```

Throws `MailCaptureTimeoutException` if no email arrives in time.

---

### `inbox(tag)` → `Inbox`

[](#inboxtag--inbox)

Returns a scoped `Inbox` for a tag. Keeps test code clean.

```
$inbox = $mc->inbox('password-reset');

$inbox->getAddress()                    // "alice-password-reset@mailcapture.app"
$inbox->waitFor(timeout: 10.0)          // Capture
$inbox->list(limit: 5)                  // CaptureList
$inbox->clear()                         // deletes all captures for this tag
```

---

### `address(tag)` → `string`

[](#addresstag--string)

Generates the capture email address synchronously. Requires `ping()` first (or `username` in the constructor).

```
$mc->ping();
echo $mc->address('signup');  // "alice-signup@mailcapture.app"
```

---

### `list(tag, limit, after)` → `CaptureList`

[](#listtag-limit-after--capturelist)

Lists recent captures (newest first).

```
$result = $mc->list(tag: 'signup', limit: 10);
foreach ($result->items as $email) {
    echo $email->subject . PHP_EOL;
}
```

---

### `get(captureId)` → `Capture`

[](#getcaptureid--capture)

Get a single capture by ID. Throws `MailCaptureNotFoundException` if not found.

---

### `delete(tag)` → `void`

[](#deletetag--void)

Deletes all captures for a tag. Use in `setUp()` for test isolation.

---

The `Capture` object
--------------------

[](#the-capture-object)

```
$email->id          // string — UUID
$email->tag         // string — e.g. "signup"
$email->subject     // string — email subject line
$email->otp         // ?string — extracted code, null if none detected
$email->bodyText    // ?string — plain-text body
$email->bodyHtml    // ?string — HTML body
$email->latencyMs   // int — send-to-capture time in ms
$email->status      // string — e.g. "captured"
$email->receivedAt  // string — ISO 8601 timestamp
```

The `otp` field is extracted automatically. If your OTP is embedded in a sentence, the service finds it for you.

---

Exception handling
------------------

[](#exception-handling)

All exceptions extend `MailCaptureException extends \RuntimeException` and have `getErrorCode()`.

```
use MailCapture\Exception\{
    MailCaptureAuthException,
    MailCaptureTimeoutException,
    MailCaptureNotFoundException,
    MailCaptureNetworkException,
};

try {
    $email = $mc->waitFor('signup', timeout: 10.0);
} catch (MailCaptureTimeoutException $e) {
    echo "Waited {$e->getWaitedSeconds()}s for tag: {$e->getTag()}" . PHP_EOL;
    echo "Did the email send? Check your email service logs." . PHP_EOL;
} catch (MailCaptureAuthException $e) {
    echo "Check your MAILCAPTURE_API_KEY environment variable." . PHP_EOL;
} catch (MailCaptureNetworkException $e) {
    echo "Network error: " . $e->getPrevious()?->getMessage() . PHP_EOL;
}
```

Exception`getErrorCode()`When`MailCaptureAuthException``UNAUTHORIZED`Invalid or revoked API key`MailCaptureTimeoutException``TIMEOUT``waitFor` exceeded timeout`MailCaptureNotFoundException``NOT_FOUND``get()` — capture not found`MailCaptureNetworkException``NETWORK_ERROR`Could not reach the API`MailCaptureApiException`variesUnexpected API error---

Testing with a mock HTTP client
-------------------------------

[](#testing-with-a-mock-http-client)

Inject a Guzzle mock client to test your code without hitting the real API:

```
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use MailCapture\MailCapture;

$mock  = new MockHandler([
    new Response(200, ['Content-Type' => 'application/json'], json_encode([
        'items' => [[
            'id' => 'test-id', 'tag' => 'signup', 'subject' => 'Welcome',
            'otp' => '123456', 'body_text' => null, 'body_html' => null,
            'latency_ms' => 50, 'status' => 'captured',
            'received_at' => '2024-01-01T00:00:00+00:00',
        ]],
        'count' => 1,
        'next_after' => '2024-01-01T00:00:01+00:00',
    ])),
]);
$stack = HandlerStack::create($mock);
$mc    = new MailCapture('mc_testkey', httpClient: new Client(['handler' => $stack]));

$email = $mc->waitFor('signup', timeout: 5.0);
assert($email->otp === '123456');
```

---

Laravel / Symfony integration
-----------------------------

[](#laravel--symfony-integration)

```
// Laravel: AppServiceProvider
$this->app->singleton(MailCapture::class, fn() =>
    new MailCapture(config('services.mailcapture.key'))
);

// Symfony: services.yaml
services:
  MailCapture\MailCapture:
    arguments:
      $apiKey: '%env(MAILCAPTURE_API_KEY)%'
```

---

Local development
-----------------

[](#local-development)

```
$mc = new MailCapture($_ENV['MAILCAPTURE_API_KEY'], baseUrl: 'http://localhost:3002');
```

###  Health Score

19

—

LowBetter than 9% of packages

Maintenance60

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/4589530?v=4)[backupbrain](/maintainers/backupbrain)[@backupbrain](https://github.com/backupbrain)

---

Top Contributors

[![backupbrain](https://avatars.githubusercontent.com/u/4589530?v=4)](https://github.com/backupbrain "backupbrain (3 commits)")

### Embed Badge

![Health badge](/badges/mailcapture-mailcapture-php/health.svg)

```
[![Health](https://phpackages.com/badges/mailcapture-mailcapture-php/health.svg)](https://phpackages.com/packages/mailcapture-mailcapture-php)
```

###  Alternatives

[mattketmo/email-checker

Throwaway email detection library

2752.1M5](/packages/mattketmo-email-checker)[sarfraznawaz2005/noty

Laravel package to incorporate noty flash notifications into laravel.

324.5k](/packages/sarfraznawaz2005-noty)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
