[{"data":1,"prerenderedAt":201},["ShallowReactive",2],{"/blog/temporary-email-api-testing-guide":3,"/blog/temporary-email-api-testing-guide-surround":190},{"id":4,"title":5,"authors":6,"badge":12,"body":14,"contentQuality":173,"date":177,"description":178,"extension":179,"image":180,"indexable":182,"locale":183,"meta":184,"navigation":182,"path":185,"reviewedBy":186,"seo":187,"stem":188,"updated":177,"__hash__":189},"posts/3.blog/19.temporary-email-api-testing-guide.md","How to Use a Temporary Email API Without Building Flaky Tests",[7],{"name":8,"to":9,"avatar":10},"Once Email Engineering","/about",{"src":11},"/images/authors/default-cartoon-author-v2.webp",{"label":13},"API testing",{"type":15,"value":16,"toc":163},"minimal",[17,21,29,38,43,46,49,53,56,59,63,66,77,92,96,99,118,121,125,128,136,140,147,150,154,157,160],[18,19,20],"p",{},"An email test can pass for the wrong reason. A shared inbox may contain yesterday's code, a fixed 10-second sleep may work on a quiet morning, and a retry loop without a deadline may keep a CI worker occupied long after the application has failed.",[18,22,23,24,28],{},"A reliable temporary-email API test is a small state machine: ",[25,26,27],"strong",{},"create, trigger, poll, match, assert and clean up",". Each stage needs a clear owner, a deadline and evidence that does not leak the message itself.",[18,30,31,32,37],{},"Before automating the flow, use the broader ",[33,34,36],"a",{"href":35},"/blog/email-testing-checklist","email testing checklist"," to decide which delivery, rendering and security behaviours belong in the test suite.",[39,40,42],"h2",{"id":41},"give-every-test-run-its-own-inbox","Give every test run its own inbox",[18,44,45],{},"Create a new inbox for one test or one tightly related scenario. Do not let parallel workers read the same address. Record the returned inbox identifier in the test context, not just the address; subsequent requests should refer to that opaque identifier.",[18,47,48],{},"Create the inbox immediately before the action that sends mail. This narrows the time window and prevents an old message from satisfying a weak assertion. If a test runner can retry a failed job, include its run identifier in local diagnostic metadata rather than trying to choose a memorable email address.",[39,50,52],{"id":51},"trigger-one-observable-action","Trigger one observable action",[18,54,55],{},"Ask the system under test to perform exactly one action: send a confirmation link, deliver a sign-in code or issue a receipt. Capture the application's request or event identifier when it provides one. That identifier is stronger evidence than a subject-line match alone.",[18,57,58],{},"Do not test an unsolicited third-party system. Automated inboxes are for applications you own or are authorised to assess. They are not a mechanism for account farming, bypassing a platform's controls or monitoring another person's correspondence.",[39,60,62],{"id":61},"poll-with-a-deadline-and-backoff","Poll with a deadline and backoff",[18,64,65],{},"Mail delivery is asynchronous, so an immediate empty list is normal. Poll gently and stop decisively. A useful starting budget is a 60-second deadline with waits of 1, 2, 3, 5, 8 and then 10 seconds. Add a little random jitter when many workers start together.",[67,68,74],"pre",{"className":69,"code":71,"language":72,"meta":73},[70],"language-text","deadline = now + 60 seconds\ndelay = 1 second\nwhile now \u003C deadline:\n    messages = list_messages(inbox_id)\n    candidate = find_expected(messages)\n    if candidate exists: return candidate\n    sleep(delay + jitter)\n    delay = min(delay * 1.6, 10 seconds)\nfail(\"expected email did not arrive before deadline\")\n","text","",[75,76,71],"code",{"__ignoreMap":73},[18,78,79,80,83,84,87,88,91],{},"Respect ",[75,81,82],{},"429 Too Many Requests"," and any ",[75,85,86],{},"Retry-After"," value. Retrying more aggressively after a rate limit makes the test less likely to recover. Retry temporary ",[75,89,90],{},"5xx"," and network failures only within the original deadline; do not silently turn a one-minute test into a ten-minute test.",[39,93,95],{"id":94},"match-the-transaction-not-only-the-subject","Match the transaction, not only the subject",[18,97,98],{},"Subject lines are written for people and can change. Prefer a combination of evidence:",[100,101,102,106,109,112,115],"ul",{},[103,104,105],"li",{},"the message arrived after the action began;",[103,107,108],{},"the recipient is the inbox created for this run;",[103,110,111],{},"the sender domain is expected;",[103,113,114],{},"a correlation identifier or one-time link belongs to the test transaction;",[103,116,117],{},"there is exactly one candidate, or the test explicitly chooses the newest valid one.",[18,119,120],{},"Treat message HTML as untrusted input. Do not execute scripts, load remote images or open links in a normal browsing profile. Extract the target URL, parse it and assert its registered destination before a controlled test client follows it.",[39,122,124],{"id":123},"keep-secrets-out-of-test-output","Keep secrets out of test output",[18,126,127],{},"An API key, verification code and magic link are credentials even when short-lived. Put API keys in the CI secret store and send them in an authorization header. Never place them in query strings, screenshots, fixture files or repository configuration.",[18,129,130,131,135],{},"On failure, log bounded metadata: inbox identifier suffix, timestamps, message count, redacted sender domain, HTTP status and request ID. Avoid dumping headers, bodies, attachments or complete addresses. A useful test report explains ",[132,133,134],"em",{},"where"," the state machine stopped without becoming another mailbox archive.",[39,137,139],{"id":138},"clean-up-in-a-finally-block","Clean up in a finally block",[18,141,142,143,146],{},"Deletion must run whether the assertion passes or fails. Put inbox cleanup in the test framework's ",[75,144,145],{},"finally",", teardown or after-each hook. Cleanup reduces accidental retention, keeps later tests isolated and makes quota usage easier to understand.",[18,148,149],{},"If deletion fails temporarily, report it separately from the product assertion. Do not hide the original failure. A scheduled server-side expiry remains valuable as a backstop, but it should not replace deliberate cleanup.",[39,151,153],{"id":152},"plan-quotas-before-parallelising","Plan quotas before parallelising",[18,155,156],{},"Estimate calls per scenario: one inbox creation, several list requests, one detail request and one deletion. Ten workers polling every second can exhaust a shared limit without increasing delivery speed. Bound worker concurrency, share the documented rate budget across the test process and display monthly consumption in the account dashboard.",[18,158,159],{},"Once Email's planned Developer tier separates the free browser allowance from API automation. Its authentication, error, quota and pricing contracts will be published after live key handling, metering and subscription revocation pass production testing. This keeps product claims aligned with capabilities that users can actually verify.",[18,161,162],{},"The best email test is not the one that retries forever. It is the one that creates an isolated inbox, waits politely, proves that the right transaction arrived, records safe evidence and leaves no mailbox behind.",{"title":73,"searchDepth":164,"depth":164,"links":165},2,[166,167,168,169,170,171,172],{"id":41,"depth":164,"text":42},{"id":51,"depth":164,"text":52},{"id":61,"depth":164,"text":62},{"id":94,"depth":164,"text":95},{"id":123,"depth":164,"text":124},{"id":138,"depth":164,"text":139},{"id":152,"depth":164,"text":153},{"originalAnalysis":174,"trendContext":175,"practicalValue":176},"We trace one automated signup test from inbox creation through bounded polling, message selection, assertion and cleanup, including failure evidence that is useful without retaining message bodies.","Email links and codes remain common in automated signup and recovery tests, while parallel CI jobs make shared inboxes, fixed sleeps, unlimited polling and message-body logging increasingly unreliable.","Readers get a provider-neutral workflow, executable pseudocode, a bounded retry budget, status-code policy, transaction matching criteria, safe logging fields and teardown guidance.","2026-08-08","A practical design for temporary-email API tests: isolate each run, poll with backoff, identify the right message, protect secrets and always clean up.","md",{"src":181},"/images/blog/temporary-email-threat-model-cover.webp",true,"en",{},"/blog/temporary-email-api-testing-guide","Once Email engineering and security review",{"title":5,"description":178},"3.blog/19.temporary-email-api-testing-guide","noilUVfZYGsjfuxG-GnZ634BGc46ZN9aXSakl-AXTu8",[191,196],{"title":192,"path":193,"stem":194,"description":195,"children":-1},"Email Verification Codes: A Safer Way to Copy, Check and Use Them","/blog/email-verification-code-safety","3.blog/18.email-verification-code-safety","Treat an email verification code as a short-lived secret: confirm the request, inspect the destination, copy only the code and clear it when the task is finished.",{"title":197,"path":198,"stem":199,"description":200,"children":-1},"Verification Email Not Arriving? A Safe Troubleshooting Checklist","/blog/anxiety","3.blog/2.anxiety","Work through address mistakes, sender delays, retries, filtering and mailbox limits without repeatedly requesting codes or weakening account security.",1786188214726]