📱 OpenInbox is now on Android & iOS! Get the app today.

OpenInbox on Telegram

Get a temp inbox right inside Telegram.

Open Telegram bot

Custom domains

Use your own domain on paid plans.

Learn More
Developer API • /php

/php Temp Email API

Create disposable inboxes, receive verification emails, and extract OTP codes from PHP — with a small Guzzle client, a runnable PHPUnit test, a zero-dependency cURL version, and a Laravel snippet.

Testing email-dependent flows in PHP — Laravel or Symfony signups, password resets, magic links — usually stalls on one thing: the verification email lands in a mailbox your test can't read. The OpenInbox API solves that with a disposable inbox you create and poll over plain HTTP. This guide gives you a reusable Guzzle client, a PHPUnit integration test, a dependency-free cURL variant, and the Laravel equivalent. Every call targets the real OpenInbox API.

How the flow works

  • Create a disposable inbox: POST /api/inbox (no auth) → ["id" => ..., "email" => ..., "expiresAt" => ...].
  • Use that address in your signup/reset form so your app emails it.
  • Poll GET /api/inbound/api/emails?inboxEmail=<email> with the X-API-KEY header (premium) until an email arrives.
  • preg_match a 4–8 digit code (or a URL) out of the email textBody.
  • Submit the code / visit the link and assert the result.

Setup

Guzzle is the most common PHP HTTP client; install it with Composer. Polling the inbox is a premium endpoint, so read the key from the environment rather than committing it. (Prefer no dependencies? The cURL version further down uses only built-in functions.)

terminalbash
composer require guzzlehttp/guzzle

export OPENINBOX_API_KEY="your-premium-api-key"

A reusable OpenInbox client (Guzzle)

Wrap the two calls you need — create and poll — in a small class. createInbox returns the decoded inbox; waitForOtp polls the inbox and returns the first 4–8 digit code it finds in textBody. Full URLs are used so the routing is unambiguous.

tests/Support/OpenInbox.phpphp
<?php

namespace Tests\Support;

use GuzzleHttp\Client;
use RuntimeException;

final class OpenInbox
{
    private string $base = 'https://api.openinbox.io/api';
    private Client $http;
    private string $apiKey;

    public function __construct(?string $apiKey = null)
    {
        $this->apiKey = $apiKey ?? (string) getenv('OPENINBOX_API_KEY');
        $this->http = new Client(['http_errors' => false]);
    }

    /** Create a disposable inbox (no auth required). */
    public function createInbox(): array
    {
        $res = $this->http->post($this->base . '/inbox');
        return json_decode((string) $res->getBody(), true); // ['id','email','expiresAt',...]
    }

    /** Poll until an email arrives, then return the OTP from textBody. */
    public function waitForOtp(string $inboxEmail, int $timeout = 30): string
    {
        $deadline = time() + $timeout;
        while (time() < $deadline) {
            $res = $this->http->get($this->base . '/inbound/api/emails', [
                'query'   => ['inboxEmail' => $inboxEmail],
                'headers' => ['X-API-KEY' => $this->apiKey],
            ]);
            $emails = json_decode((string) $res->getBody(), true)['emails'] ?? [];
            if ($emails && preg_match('/\b\d{4,8}\b/', $emails[0]['textBody'] ?? '', $m)) {
                return $m[0];
            }
            sleep(2);
        }
        throw new RuntimeException('No OTP email arrived in time');
    }
}

Using it in a PHPUnit test

With the client in place, an integration test reads like the user story: create an inbox, trigger your signup, wait for the code, and assert. Swap the cURL trigger for a call to your own application or HTTP client.

tests/SignupVerificationTest.phpphp
<?php

use PHPUnit\Framework\TestCase;
use Tests\Support\OpenInbox;

final class SignupVerificationTest extends TestCase
{
    public function test_user_verifies_email_with_otp(): void
    {
        $openInbox = new OpenInbox();
        $inbox = $openInbox->createInbox();

        // Trigger your app so it emails the verification code
        $ch = curl_init('https://staging.yourapp.com/api/register');
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
            CURLOPT_POSTFIELDS     => json_encode([
                'email'    => $inbox['email'],
                'password' => 'Str0ng!Pass',
            ]),
        ]);
        curl_exec($ch);

        // Pull the OTP from the disposable inbox
        $otp = $openInbox->waitForOtp($inbox['email']);
        $this->assertMatchesRegularExpression('/^\d{4,8}$/', $otp);

        // …submit $otp to your verify endpoint and assert the success state
    }
}

No dependencies? Use built-in cURL

If you would rather not pull in Guzzle, the built-in cURL functions do the same job. This standalone script creates an inbox and polls until the code arrives.

otp.phpphp
<?php
$base = 'https://api.openinbox.io/api';
$apiKey = getenv('OPENINBOX_API_KEY');

// 1. Create an inbox (no auth required)
$ch = curl_init("$base/inbox");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true]);
$inbox = json_decode(curl_exec($ch), true);
echo "Inbox: {$inbox['email']}" . PHP_EOL;

// 2. Poll for the email (X-API-KEY required)
for ($i = 0; $i < 15; $i++) {
    sleep(2);
    $url = "$base/inbound/api/emails?inboxEmail=" . urlencode($inbox['email']);
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => ["X-API-KEY: $apiKey"],
    ]);
    $emails = json_decode(curl_exec($ch), true)['emails'] ?? [];
    if ($emails) {
        preg_match('/\b\d{4,8}\b/', $emails[0]['textBody'], $m);
        echo "OTP: {$m[0]}" . PHP_EOL;
        break;
    }
}

Laravel

In a Laravel app, use the Http facade instead of Guzzle directly: Http::post("https://api.openinbox.io/api/inbox")->json() creates the inbox, and Http::withHeaders(["X-API-KEY" => config("services.openinbox.key")])->get("https://api.openinbox.io/api/inbound/api/emails", ["inboxEmail" => $email])->json("emails") polls it. Extract the OTP from $emails[0]["textBody"] with preg_match exactly as above. Keep the key in config/services.php, sourced from the OPENINBOX_API_KEY env var.

Symfony

In Symfony, inject HttpClientInterface and make the same two calls. $client->request("POST", "https://api.openinbox.io/api/inbox")->toArray() creates the inbox; $client->request("GET", "https://api.openinbox.io/api/inbound/api/emails", ["query" => ["inboxEmail" => $email], "headers" => ["X-API-KEY" => $apiKey]])->toArray()["emails"] polls it. Extract the OTP from $emails[0]["textBody"] with preg_match, just like the Guzzle client. The endpoints, the X-API-KEY header, and the textBody field are identical — only the HTTP client wrapper changes.

Common pitfalls

  • Always bound the polling loop (a for-loop or a deadline) — an unbounded while will hang your test suite if the email never arrives.
  • Creating an inbox needs no auth, but GET /api/inbound/api/emails is premium. A 401/403 means the X-API-KEY header is missing or the account is not on a paid plan.
  • Read $email["textBody"] — there is no pre-parsed code field. preg_match("/\b\d{4,8}\b/", ...) grabs a 4–8 digit run; tighten it if the body has other numbers.
  • Guzzle throws on 4xx/5xx by default; set http_errors => false (as shown) so you can inspect the body and retry instead of catching exceptions in the loop.
  • urlencode() the inbox address when building the poll URL by hand so a + or . in the local part is transmitted correctly.

Frequently Asked Questions

How do I get a temporary email in PHP?

Send a POST request to https://api.openinbox.io/api/inbox (no auth) — with Guzzle, the Http facade, or built-in cURL — and read the returned email field. The response also includes id and expiresAt.

Do I need an API key?

Creating an inbox is unauthenticated. Polling received emails (GET /api/inbound/api/emails) requires a premium X-API-KEY, which you should read from the OPENINBOX_API_KEY environment variable.

How do I extract the OTP code in PHP?

Run preg_match("/\b\d{4,8}\b/", $email["textBody"], $m) against the email body returned by the poll endpoint, then use $m[0]. There is no separate parsed field — it is a one-line regex on textBody.

Does this work with Laravel or Symfony?

Yes. In Laravel use the Http facade; in Symfony use HttpClientInterface. The endpoints, headers, and textBody field are identical — only the HTTP client wrapper changes.

Can I run this in PHPUnit and CI?

Yes — the examples are written as a PHPUnit integration test and only make HTTPS calls, so they run on any CI runner. Store the key as a secret exposed via OPENINBOX_API_KEY.

How long does a disposable inbox last?

Free inboxes expire after one hour, which is plenty for a test run. Create a fresh inbox per test so parallel tests never share a mailbox.

Related Pages

Explore More

Your private inbox is one click away

Free, instant, no registration needed. Works with any service. Expires automatically after 1 hour.

View API Docs