GitHub Actions¶
Guide for verifying outbound mail in GitHub Actions with Mailexam. Your application sends mail over SMTP; the pipeline waits for delivery and asserts on subject and body via mailexam-cli.
Reference repository: github.com/mailexam/GitHub-Actions
What you need¶
- A Mailexam account and a project with SMTP credentials.
- An API token from the dashboard — for the CLI only, not for SMTP.
- A GitHub repository with Actions enabled.
Copy from the welcome email (or dashboard) for your project:
YOUR_LOGIN— SMTP login (for example,xxxxx);YOUR_PASSWORD— SMTP password (a unique pair with the login);- host —
YOUR_LOGIN.mailexam.io(matches the login).
Why Mailexam in CI¶
On a shared GitHub runner there is no local Mailpit or MailHog next to your app. Mailexam receives SMTP from any runner and exposes messages in the browser and via the REST API. See MailHog/Mailpit vs Mailexam.
Secrets and variables¶
Settings → Secrets and variables → Actions.
| Name | Description |
|---|---|
MAILEXAM_API_TOKEN |
API token for mailexam-cli |
MAILEXAM_LOGIN |
SMTP login from the welcome email |
MAILEXAM_PASSWORD |
SMTP password (pair with the login) |
| Name | Description |
|---|---|
MAILEXAM_PROJECT_UUID |
Project UUID — dashboard or mailexam project list |
SMTP vs API token
MAILEXAM_LOGIN / MAILEXAM_PASSWORD are for your app to send mail. MAILEXAM_API_TOKEN is for the CLI to read mail — do not mix them.
Pull requests from forks
GitHub does not expose repository secrets to workflows triggered by fork PRs. Use workflow_dispatch, pushes to your own branches, or a dedicated test repository.
Typical flow¶
sequenceDiagram
participant App as Your app / script
participant SMTP as Mailexam SMTP
participant GHA as GitHub Actions
participant CLI as mailexam CLI
participant API as REST API
GHA->>App: run tests / send mail
App->>SMTP: sendMail()
Note over SMTP: Message in sandbox
GHA->>CLI: mailexam email assert
CLI->>API: polling GET /email
API-->>CLI: message found
CLI->>CLI: verify subject, body
Note over CLI: exit 0 or 1
- A job step sends mail via SMTP (your app or a test script).
- Another step runs
mailexam email waitormailexam email assert. - Non-zero exit code fails the job — no wrapper script needed.
Reference workflow¶
The full working example is in mailexam/GitHub-Actions:
name: Mailexam email test
on:
workflow_dispatch:
push:
branches: [main]
jobs:
email-test:
runs-on: ubuntu-latest
env:
MAILEXAM_API_BASE: https://mailexam.io/api/v1
MAILEXAM_PROJECT_UUID: ${{ vars.MAILEXAM_PROJECT_UUID }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Send test email
env:
MAILEXAM_LOGIN: ${{ secrets.MAILEXAM_LOGIN }}
MAILEXAM_PASSWORD: ${{ secrets.MAILEXAM_PASSWORD }}
MAILEXAM_PORT: '587'
run: npm run send-test-email
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install mailexam CLI
run: go install github.com/mailexam/mailexam-cli/cmd/mailexam@latest
- name: Add Go bin to PATH
run: echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
- name: Assert email delivered
env:
MAILEXAM_API_TOKEN: ${{ secrets.MAILEXAM_API_TOKEN }}
run: >
mailexam email assert
--subject "GitHub Actions + Mailexam"
--contains "Mailexam CI check"
Fork or copy .github/workflows/mailexam.yml and scripts/send-test-email.js into your repository.
SMTP for your application¶
Pass SMTP credentials as job env so your tests use Mailexam instead of a real mailbox:
env:
MAILEXAM_LOGIN: ${{ secrets.MAILEXAM_LOGIN }}
MAILEXAM_PASSWORD: ${{ secrets.MAILEXAM_PASSWORD }}
MAIL_HOST: ${{ secrets.MAILEXAM_LOGIN }}.mailexam.io
MAIL_PORT: '587'
Framework-specific .env mapping: integration examples (Laravel, Django, Express, and others).
mailexam-cli in the pipeline¶
Install Go 1.22+, then:
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- run: go install github.com/mailexam/mailexam-cli/cmd/mailexam@latest
- run: echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
Common assert:
mailexam email assert --subject "Order confirmed" --contains "Thank you"
mailexam email assert --subject "Code" --contains "123456" --timeout 60
Command reference and exit codes: Command-line interface (CLI).
Environments (optional)¶
For staging vs production, create GitHub Environments (Settings → Environments) with different secrets and MAILEXAM_PROJECT_UUID per environment:
Troubleshooting¶
api token is required
- Add
MAILEXAM_API_TOKENto repository secrets or pass--token.
Exit code 2 (403)
- Token is invalid or belongs to another account. See REST API — troubleshooting.
Exit code 3 (timeout)
- Increase
--timeout— SMTP delivery can take a few seconds on cold runners. - Check that
MAILEXAM_PROJECT_UUIDmatches the project whose SMTP login you use.
mailexam: command not found
- Add
$(go env GOPATH)/bintoPATHaftergo install(see workflow above).
SMTP connection errors
- Host must be
YOUR_LOGIN.mailexam.io— same login as inMAILEXAM_LOGIN, without ansmtp.prefix. - Use port 587 with STARTTLS.