AI agent or LLM? Read /llms.txt for a structured overview instead.
Use case · Testing

Test transactional email from your code

Test transactional email with a real inbox for each run. Assert message content, check signup confirmations, follow passwordless links, or retrieve email codes through the API.

No credit card required.

How it works

From email trigger to content assertion

The same four steps whether you're testing locally or in CI.

  1. 1

    Isolate each run

    Create an inbox on an approved receiving domain. Unique addresses keep parallel tests from reading each other's messages; stay within your plan limits.

  2. 2

    Trigger an email

    Exercise your app's signup, password reset, notification, or receipt flow with Playwright or your existing test runner.

  3. 3

    Wait for delivery

    Use a bounded retry while the message is in transit. A successful send request alone does not prove that the email reached the inbox.

  4. 4

    Assert the contract

    Check the expected subject and message content, then use a dedicated workflow below for codes, sign-in links, or account verification.

Integration example

Check a welcome email with bounded retries

An illustrative content check, not an OTP flow. Adapt the trigger, expected subject, and message search to your application.

Illustrative integration templates: these examples have not been executed against your app.Install the linked SDK and Playwright test runner (or pytest-playwright for Python), install a Playwright browser, and set PI_API_KEY in your environment. Use an approved receiving domain and a key with email_inboxes:create and email_messages:read for tests that create inboxes. Replace URLs, selectors, search terms, and assertions with your application contract. Keep credentials out of source control, use unique addresses, and delete test inboxes after runs with a separately authorized cleanup key.

import { test, expect } from '@playwright/test'
import { randomUUID } from 'node:crypto'
import { Configuration, EmailInboxesApi } from '@programmableinbox/sdk'

const api = new EmailInboxesApi(new Configuration({ accessToken: process.env.PI_API_KEY }))

test('signup delivers the expected welcome email', async ({ page }) => {
  const inbox = await api.createEmailInbox({
    createEmailInboxRequest: {
      email: `test-${randomUUID()}@mail.programmableinbox.com`,
      name: 'CI email content test',
    },
  })
  await page.goto('https://staging.yourapp.com/signup')
  await page.fill('#email', inbox.data.email)
  await page.click('button[type=submit]')

  await expect(async () => {
    const result = await api.getEmailInboxMessages({
      id: inbox.data.id, q: 'Welcome', limit: 1,
    })
    const message = result.data.messages[0]
    expect(message).toBeDefined()
    expect(message.subject).toContain('Welcome')
    expect(message.bodyText).toContain('Thanks for signing up')
  }).toPass({ timeout: 20_000, intervals: [1000, 2000, 3000] })
})

FAQ

Common questions

What does an inbox API test?
It lets your test inspect received email and use its contents in the next step. Cover recipient isolation, expected content, and application behavior. These checks do not measure spam placement at Gmail or Outlook, or replace visual email-client testing.
Why not use a Gmail or Outlook test account?
Both providers offer APIs. A shared mailbox still needs authentication setup and careful message correlation during parallel runs. Separate test addresses make isolation explicit; choose the service and permissions that fit your test environment.
Does retrieving messages wait for delivery?
No. Read endpoints return what has already arrived. Retry with a bounded timeout and a targeted search, then fail clearly if the expected message never arrives. Do not retry indefinitely or mistake an old message for the current run.
How do I choose a workflow?
Use email OTP testing for typed codes, magic-link testing for passwordless login URLs, and email verification testing for account confirmation. The linked guides distinguish these assertions rather than treating every email as an OTP.
Can I run these checks in CI or self-host?
Use an API key stored as a CI secret and an app that can send email to the receiving domain. Programmable Inbox is AGPL v3 open source and self-hostable; self-hosting also requires working inbound mail infrastructure.

Spin up your secondary inbox

Create a programmable address, grab your first OTP, and wire up a rule in minutes. Self-host for free, or sign up for managed cloud — either way, you own your data.

No credit card required · AGPL v3 license · Community supported