CI with GitHub Actions
Your CI runs your app, your app sends mail to simmail, and your tests wait for it. Nothing needs to run inside the CI job except your app and its tests.
Set up once
Section titled “Set up once”- Make a universe for CI. On your account page, Universes → New universe, named for example CI. Keeping CI separate means its clock jumps and test people never touch Staging.
- Create a login for your app under Settings → Mail logins in that universe.
- Create an API key for that universe with the Member role.
- Add secrets to your GitHub repository (Settings → Secrets and variables → Actions):
| Secret | Value |
|---|---|
SIMMAIL_API_KEY | The key, smk_… |
SMTP_USER | The login’s address |
SMTP_PASS | The login’s password |
The workflow
Section titled “The workflow”name: Email tests
on: [push, pull_request]
jobs: email: runs-on: ubuntu-latest env: SIMMAIL_URL: https://c1.simmail.dev SIMMAIL_API_KEY: ${{ secrets.SIMMAIL_API_KEY }} SMTP_HOST: c1.simmail.dev SMTP_PORT: "2525" SMTP_USER: ${{ secrets.SMTP_USER }} SMTP_PASS: ${{ secrets.SMTP_PASS }} TEST_RUN: ${{ github.run_id }}-${{ github.run_attempt }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx playwright install --with-deps chromium - run: npx playwright testUse your universe’s host from the account page.
Make every address unique
Section titled “Make every address unique”Several CI runs can share one universe at the same time. Put the run id in every address your tests create, so one run never catches another run’s email:
const email = `signup-${process.env.TEST_RUN}-${Date.now()}@yourcompany.test`;const msg = await mail.waitForEmail({ to: email, subject: "verify", timeout: 30000 });Keep throwaway addresses quiet
Section titled “Keep throwaway addresses quiet”By default, when your app writes to an address nobody plays, simmail puts a stranger behind it, who may answer. Each stranger counts toward your plan’s people limit and can use AI actions. For sign-up tests that create thousands of addresses, turn strangers off for your test domain.
The recipient rules are one document per universe, and PUT replaces all of it, so read it first, change it, and write it back. This needs an admin key:
curl -s https://c1.simmail.dev/api/recipients -H "Authorization: Bearer $ADMIN_KEY" \ | jq '.domains["yourcompany.test"] = {"create": true, "stranger": "off"}' \ | curl -s -X PUT https://c1.simmail.dev/api/recipients \ -H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" -d @-Mail to @yourcompany.test addresses still lands in a mailbox, so waitForEmail works, but nobody answers it.
Keep tests fast
Section titled “Keep tests fast”- Use
waitForReplywithfast: true(the default) instead of waiting for people’s real delays. - Freeze or move the clock only in the CI universe.
- Give waits a timeout well under your test runner’s own timeout, so a missing email fails with a clear
408rather than a hung job.