docs/examples

Examples

Copy-paste recipes for the three jobs TossInbox does: sign up with a throwaway address, collect the verification code, and toss the inbox. Every snippet uses the documented JSON contract — no screen scraping, no fragile parsing.

1 · Shell: sign up, get the code, verify

The whole loop as a paste-runnable bash block. Needs jq (or swap in node -e parsing). Replace the two curl calls with whatever your target app actually does.

bash: signup + otp end to end
# 1: create the inbox and grab its address as plain text
ADDR=$(tossinbox spawn --json | jq -r .inbox.address)
echo "inbox: $ADDR"

# 2: use the address in the signup flow of the app under test
curl -fsS https://staging.example-app.dev/api/signup \
  -d "email=$ADDR" \
  -d "password=correct-horse-battery"

# 3: block until the verification email lands, print just the code
CODE=$(tossinbox wait --code --timeout 180 --json | jq -r .code)
echo "code: $CODE"

# 4: complete the verification
curl -fsS https://staging.example-app.dev/api/verify \
  -d "email=$ADDR" \
  -d "code=$CODE"

# 5: done — delete the inbox on the server and locally
tossinbox toss

Exit codes tell your script what happened: 2 = the email never arrived before the timeout, 3 = nothing matched your filters. Treat non-zero as a test failure.

2 · GitHub Actions: e2e signup on every push

A workflow that signs up a fresh account on your staging API every push and verifies it — no shared test account, no mailbox quota, no state between runs.

.github/workflows/e2e-signup.yml
name: e2e-signup
on: [push]
jobs:
  signup:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - name: Install TossInbox
        run: npm install --global tossinbox@0.1.6

      - name: Spawn inbox
        id: inbox
        run: |
          echo "address=$(tossinbox spawn --json | jq -r .inbox.address)" >> "$GITHUB_OUTPUT"

      - name: Sign up on staging
        run: |
          curl -fsS https://staging.example-app.dev/api/signup \
            -d "email=${{ steps.inbox.outputs.address }}" \
            -d "password=correct-horse-battery"

      - name: Wait for the verification code
        id: code
        run: |
          echo "code=$(tossinbox wait --code --timeout 180 --json | jq -r .code)" >> "$GITHUB_OUTPUT"

      - name: Verify the account
        run: |
          curl -fsS https://staging.example-app.dev/api/verify \
            -d "email=${{ steps.inbox.outputs.address }}" \
            -d "code=${{ steps.code.outputs.code }}"

      - name: Toss the inbox
        if: always()
        run: tossinbox toss

There is also a packaged action (- uses: mohamed-khairy-5i/tossinbox@v1) that spawns + waits in one composite step and exposes address / code outputs — handy when nothing has to run between spawn and wait. Full setup on the Agents page.

3 · Node.js: drive it from a script

Same contract, inside Node. spawnSync keeps it dependency-free; the exit code is your error channel, --json is your data channel.

signup-flow.mjs
import { spawnSync } from "node:child_process";

const run = (args) => {
  const r = spawnSync("tossinbox", args, { encoding: "utf8" });
  if (r.status !== 0) throw new Error(`tossinbox ${args.join(" ")} exited ${r.status}`);
  return JSON.parse(r.stdout);
};

// 1: a fresh inbox for this run
const { inbox } = run(["spawn", "--json"]);
console.log("inbox:", inbox.address);

// 2: your app under test does its signup with inbox.address here …

// 3: block until the verification email lands
const { code } = run(["wait", "--code", "--timeout", "180", "--json"]);
console.log("code:", code);

// 4: clean up no matter what happened
run(["toss"]);

State lives in ~/.tossinbox/state.json (0600). Point it somewhere temporary with TOSSINBOX_STATE=$(mktemp) to keep parallel runs isolated.

4 · MCP: hand the whole job to an agent

TossInbox ships a stdio MCP server. Point any MCP client at it and the agent can create inboxes, read messages, and wait for codes with tool calls.

mcp client config (claude desktop / cursor / any MCP client)
{
  "mcpServers": {
    "tossinbox": {
      "command": "npx",
      "args": ["-y", "tossinbox", "mcp"]
    }
  }
}

Then just say it in natural language:

  • “Create a disposable inbox, sign this test account up on staging, wait for the code, finish the verification, then delete the inbox.”
  • The agent maps that to create_inbox → its own signup step → wait_for_codetossinbox toss.

All four tools, plus client-by-client setup: the Agents & MCP page.

5 · When a provider is down, failover kicks in

Seven providers ship built in: mailtm (default), mailgw (mail.tm-compatible API on independent infrastructure), guerrillamail, tempmaillol, tempmailio, tempmailplus and maildrop. If the one you asked for is down, spawn retries the create against the others automatically and reports the switch — no config files, no retry scripts.

shell: automatic provider failover
# mail.gw is down? the inbox still lands on a healthy provider
tossinbox spawn -p mailgw
#   ⚠ mailgw: HTTP 502 from api.mail.gw — provider is down or having trouble…
# ✔ Inbox ready : ab12cd34ef@uberip.com
#   provider    : mailtm (failover from mailgw)

# strict mode: fail instead of switching
tossinbox spawn -p mailgw --no-failover

# or pick a healthy provider yourself
tossinbox providers
tossinbox spawn -p guerrillamail

--json makes the switch machine-readable: a failover object (requested / used) plus a warnings array with one entry per failed attempt.

6 · Wait flags: filter by sender, subject, timing

wait polls until a message matches. Tighten the match so noise from other mail never delays your run.

shell: filtered wait
# only the sender you expect, only the subject you expect
tossinbox wait --code \
  --from noreply@example-app.dev \
  --subject verification \
  --timeout 120 \
  --interval 3

Codes with letters come back uppercased (f4x9k2F4X9K2); extraction understands prompts in twelve languages — English and Arabic (رمز / كود / تفعيل / تحقق), plus French, Spanish, German, Portuguese, Italian, Russian, Turkish, Chinese, Japanese, and Korean. Flag reference on the CLI page.

7 · Watch: stream codes the moment they land

Long-lived automation should not exit after the first message. watch keeps polling, prints every new message (and its code when found), and stops on Ctrl-C. Only new arrivals are reported — restarting a watch never replays old mail.

shell: watch for codes
# watch until the code from your app lands, then keep going
tossinbox watch --from noreply@example-app.dev

# agents: one compact JSON object per line on stdout, stderr stays silent
tossinbox watch --json

8 · Attachments: pull the files to disk

Messages can carry attachments. read --save downloads all of them plus the HTML/plain-text bodies into <dir>/<message-id>/ (default dir: ./tossinbox-attachments).

shell: save attachments
tossinbox spawn                  # inbox that will receive the email
# ... the email with the attachment arrives ...
tossinbox list                   # find the message id
tossinbox read 42 --save         # ./tossinbox-attachments/42/{body.html, body.txt, report.pdf, …}
tossinbox read 42 --html         # print the raw HTML body instead of the plain text

Agents: with --json, read --save returns a saved array with the exact path of every file it wrote — parse it and read the files directly, no path guessing. Full attachment support on mailtm, mailgw and tempmailplus; tempmailio when its upstream exposes the files; the other providers have no attachments upstream.