[{"data":1,"prerenderedAt":1580},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fguides\u002Fci\u002F":72,"\u002Fdocs\u002Fguides\u002Fci\u002F-surround":1575},[4,8,22,26,43],{"title":5,"path":6,"stem":7},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started",{"title":9,"path":10,"stem":11,"children":12,"page":21},"Setup","\u002Fdocs\u002Fsetup","1.docs\u002F2.setup",[13,17],{"title":14,"path":15,"stem":16},"Domain","\u002Fdocs\u002Fsetup\u002Fdomain","1.docs\u002F2.setup\u002F2.domain",{"title":18,"path":19,"stem":20},"Email","\u002Fdocs\u002Fsetup\u002Faccount","1.docs\u002F2.setup\u002F3.account",false,{"title":23,"path":24,"stem":25},"Email Testing API","\u002Fdocs\u002Fapi","1.docs\u002F3.api",{"title":27,"path":28,"stem":29,"children":30,"page":21},"Features","\u002Fdocs\u002Ffeatures","1.docs\u002F4.features",[31,35,39],{"title":32,"path":33,"stem":34},"Account","\u002Fdocs\u002Ffeatures\u002Faccount","1.docs\u002F4.features\u002F1.account",{"title":36,"path":37,"stem":38},"Mailbox","\u002Fdocs\u002Ffeatures\u002Fmailbox","1.docs\u002F4.features\u002F2.mailbox",{"title":40,"path":41,"stem":42},"Messages","\u002Fdocs\u002Ffeatures\u002Fmessage","1.docs\u002F4.features\u002F3.message",{"title":44,"path":45,"stem":46,"children":47,"page":21},"Guides","\u002Fdocs\u002Fguides","1.docs\u002F5.guides",[48,52,56,60,64,68],{"title":49,"path":50,"stem":51},"CI","\u002Fdocs\u002Fguides\u002Fci","1.docs\u002F5.guides\u002F1.ci",{"title":53,"path":54,"stem":55},"Catch-All","\u002Fdocs\u002Fguides\u002Fcatch-all","1.docs\u002F5.guides\u002F2.catch-all",{"title":57,"path":58,"stem":59},"SPF, DKIM, DMARC","\u002Fdocs\u002Fguides\u002Fspf-dkim-dmarc","1.docs\u002F5.guides\u002F3.spf-dkim-dmarc",{"title":61,"path":62,"stem":63},"E2E Flows","\u002Fdocs\u002Fguides\u002Fe2e","1.docs\u002F5.guides\u002F4.e2e",{"title":65,"path":66,"stem":67},"Email Clients","\u002Fdocs\u002Fguides\u002Femail-clients","1.docs\u002F5.guides\u002F5.email-clients",{"title":69,"path":70,"stem":71},"AI Agents","\u002Fdocs\u002Fguides\u002Fagents","1.docs\u002F5.guides\u002F6.agents",{"id":73,"title":74,"body":75,"description":1569,"extension":1570,"meta":1571,"navigation":1572,"path":50,"seo":1573,"stem":51,"__hash__":1574},"docs\u002F1.docs\u002F5.guides\u002F1.ci.md","Testing Email in CI with GitHub Actions",{"type":76,"value":77,"toc":1560},"minimark",[78,92,97,151,155,158,332,335,1371,1379,1383,1537,1540,1556],[79,80,81,82,86,87,91],"p",{},"Your code sends over plain SMTP, your test reads the result over the ",[83,84,85],"a",{"href":24},"API",". ",[88,89,90],"code",{},"send.smtp.dev"," delivers only to accounts in your sandbox, so test emails can't reach anyone real.",[93,94,96],"h2",{"id":95},"one-time-setup","One-Time Setup",[98,99,101,106,126,130,137,141],"steps",{"level":100},"3",[102,103,105],"h3",{"id":104},"create-a-sender-and-a-receiver-account","Create a sender and a receiver account",[79,107,108,109,113,114,117,118,121,122,125],{},"On the ",[83,110,112],{"href":111},"\u002Faccounts","Accounts"," page or via the ",[83,115,85],{"href":116},"\u002Fdocs\u002Fapi#create-an-account"," - e.g. ",[88,119,120],{},"ci@example.com"," and ",[88,123,124],{},"user@example.com",".",[102,127,129],{"id":128},"create-an-api-token","Create an API token",[79,131,108,132,136],{},[83,133,135],{"href":134},"\u002Ftokens","API Keys"," page. The token is shown once.",[102,138,140],{"id":139},"store-the-secrets-in-your-repository","Store the secrets in your repository",[79,142,143,146,147,150],{},[88,144,145],{},"SMTP_DEV_API_KEY"," (the token) and ",[88,148,149],{},"SMTP_DEV_PASSWORD"," (the sender account's password) under Settings → Secrets and variables → Actions.",[93,152,154],{"id":153},"the-test","The Test",[79,156,157],{},"Point your app's SMTP transport at the sandbox. With nodemailer:",[159,160,165],"pre",{"className":161,"code":162,"language":163,"meta":164,"style":164},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import nodemailer from 'nodemailer'\n\nconst transport = nodemailer.createTransport({\n  host: 'send.smtp.dev',\n  port: 587,\n  requireTLS: true,\n  auth: { user: 'ci@example.com', pass: process.env.SMTP_DEV_PASSWORD },\n})\n","js","",[88,166,167,194,201,229,249,263,277,323],{"__ignoreMap":164},[168,169,172,176,180,183,187,191],"span",{"class":170,"line":171},"line",1,[168,173,175],{"class":174},"s7zQu","import",[168,177,179],{"class":178},"sTEyZ"," nodemailer ",[168,181,182],{"class":174},"from",[168,184,186],{"class":185},"sMK4o"," '",[168,188,190],{"class":189},"sfazB","nodemailer",[168,192,193],{"class":185},"'\n",[168,195,197],{"class":170,"line":196},2,[168,198,200],{"emptyLinePlaceholder":199},true,"\n",[168,202,204,208,211,214,217,219,223,226],{"class":170,"line":203},3,[168,205,207],{"class":206},"spNyl","const",[168,209,210],{"class":178}," transport ",[168,212,213],{"class":185},"=",[168,215,216],{"class":178}," nodemailer",[168,218,125],{"class":185},[168,220,222],{"class":221},"s2Zo4","createTransport",[168,224,225],{"class":178},"(",[168,227,228],{"class":185},"{\n",[168,230,232,236,239,241,243,246],{"class":170,"line":231},4,[168,233,235],{"class":234},"swJcz","  host",[168,237,238],{"class":185},":",[168,240,186],{"class":185},[168,242,90],{"class":189},[168,244,245],{"class":185},"'",[168,247,248],{"class":185},",\n",[168,250,252,255,257,261],{"class":170,"line":251},5,[168,253,254],{"class":234},"  port",[168,256,238],{"class":185},[168,258,260],{"class":259},"sbssI"," 587",[168,262,248],{"class":185},[168,264,266,269,271,275],{"class":170,"line":265},6,[168,267,268],{"class":234},"  requireTLS",[168,270,238],{"class":185},[168,272,274],{"class":273},"sfNiH"," true",[168,276,248],{"class":185},[168,278,280,283,285,288,291,293,295,297,299,302,305,307,310,312,315,317,320],{"class":170,"line":279},7,[168,281,282],{"class":234},"  auth",[168,284,238],{"class":185},[168,286,287],{"class":185}," {",[168,289,290],{"class":234}," user",[168,292,238],{"class":185},[168,294,186],{"class":185},[168,296,120],{"class":189},[168,298,245],{"class":185},[168,300,301],{"class":185},",",[168,303,304],{"class":234}," pass",[168,306,238],{"class":185},[168,308,309],{"class":178}," process",[168,311,125],{"class":185},[168,313,314],{"class":178},"env",[168,316,125],{"class":185},[168,318,319],{"class":178},"SMTP_DEV_PASSWORD ",[168,321,322],{"class":185},"},\n",[168,324,326,329],{"class":170,"line":325},8,[168,327,328],{"class":185},"}",[168,330,331],{"class":178},")\n",[79,333,334],{},"Then assert on what arrived:",[159,336,338],{"className":161,"code":337,"language":163,"meta":164,"style":164},"import assert from 'node:assert'\nimport { test } from 'node:test'\n\nconst API = 'https:\u002F\u002Fapi.smtp.dev'\nconst headers = { 'X-API-KEY': process.env.SMTP_DEV_API_KEY }\n\nasync function api(path) {\n  const res = await fetch(API + path, { headers })\n  assert.ok(res.ok, `${path} returned ${res.status}`)\n  return res.json()\n}\n\nasync function inboxOf(address) {\n  const { member } = await api(`\u002Faccounts?address=${encodeURIComponent(address)}`)\n  const account = member.find(a => a.address === address)\n  const { id, mailboxes } = await api(`\u002Faccounts\u002F${account.id}`)\n  return { accountId: id, mailboxId: mailboxes.find(m => m.path === 'INBOX').id }\n}\n\nasync function waitForMessage({ accountId, mailboxId }, match, timeout = 30_000) {\n  const deadline = Date.now() + timeout\n  while (Date.now() \u003C deadline) {\n    const { member } = await api(`\u002Faccounts\u002F${accountId}\u002Fmailboxes\u002F${mailboxId}\u002Fmessages`)\n    const found = member.find(match)\n    if (found)\n      return api(`\u002Faccounts\u002F${accountId}\u002Fmailboxes\u002F${mailboxId}\u002Fmessages\u002F${found.id}`)\n    await new Promise(resolve => setTimeout(resolve, 2000))\n  }\n  throw new Error(`no matching message after ${timeout}ms`)\n}\n\ntest('welcome email', async () => {\n  const subject = `Welcome ${Date.now()}`\n  await transport.sendMail({\n    from: 'ci@example.com',\n    to: 'user@example.com',\n    subject,\n    text: 'Confirm your address: https:\u002F\u002Fexample.com\u002Fconfirm\u002Fabc123',\n  })\n\n  const inbox = await inboxOf('user@example.com')\n  const message = await waitForMessage(inbox, m => m.subject === subject)\n\n  assert.equal(message.from.address, 'ci@example.com')\n  assert.match(message.text, \u002F\\\u002Fconfirm\\\u002F\u002F)\n})\n",[88,339,340,356,378,382,398,432,436,459,497,544,560,565,570,589,627,666,710,769,774,779,818,845,873,921,944,957,1000,1035,1041,1073,1078,1083,1110,1140,1158,1174,1190,1198,1215,1223,1228,1252,1290,1295,1328,1364],{"__ignoreMap":164},[168,341,342,344,347,349,351,354],{"class":170,"line":171},[168,343,175],{"class":174},[168,345,346],{"class":178}," assert ",[168,348,182],{"class":174},[168,350,186],{"class":185},[168,352,353],{"class":189},"node:assert",[168,355,193],{"class":185},[168,357,358,360,362,365,368,371,373,376],{"class":170,"line":196},[168,359,175],{"class":174},[168,361,287],{"class":185},[168,363,364],{"class":178}," test",[168,366,367],{"class":185}," }",[168,369,370],{"class":174}," from",[168,372,186],{"class":185},[168,374,375],{"class":189},"node:test",[168,377,193],{"class":185},[168,379,380],{"class":170,"line":203},[168,381,200],{"emptyLinePlaceholder":199},[168,383,384,386,389,391,393,396],{"class":170,"line":231},[168,385,207],{"class":206},[168,387,388],{"class":178}," API ",[168,390,213],{"class":185},[168,392,186],{"class":185},[168,394,395],{"class":189},"https:\u002F\u002Fapi.smtp.dev",[168,397,193],{"class":185},[168,399,400,402,405,407,409,411,414,416,418,420,422,424,426,429],{"class":170,"line":251},[168,401,207],{"class":206},[168,403,404],{"class":178}," headers ",[168,406,213],{"class":185},[168,408,287],{"class":185},[168,410,186],{"class":185},[168,412,413],{"class":234},"X-API-KEY",[168,415,245],{"class":185},[168,417,238],{"class":185},[168,419,309],{"class":178},[168,421,125],{"class":185},[168,423,314],{"class":178},[168,425,125],{"class":185},[168,427,428],{"class":178},"SMTP_DEV_API_KEY ",[168,430,431],{"class":185},"}\n",[168,433,434],{"class":170,"line":265},[168,435,200],{"emptyLinePlaceholder":199},[168,437,438,441,444,447,449,453,456],{"class":170,"line":279},[168,439,440],{"class":206},"async",[168,442,443],{"class":206}," function",[168,445,446],{"class":221}," api",[168,448,225],{"class":185},[168,450,452],{"class":451},"sHdIc","path",[168,454,455],{"class":185},")",[168,457,458],{"class":185}," {\n",[168,460,461,464,467,470,473,476,478,480,483,486,488,490,493,495],{"class":170,"line":325},[168,462,463],{"class":206},"  const",[168,465,466],{"class":178}," res",[168,468,469],{"class":185}," =",[168,471,472],{"class":174}," await",[168,474,475],{"class":221}," fetch",[168,477,225],{"class":234},[168,479,85],{"class":178},[168,481,482],{"class":185}," +",[168,484,485],{"class":178}," path",[168,487,301],{"class":185},[168,489,287],{"class":185},[168,491,492],{"class":178}," headers",[168,494,367],{"class":185},[168,496,331],{"class":234},[168,498,500,503,505,508,510,513,515,517,519,522,524,526,529,532,534,536,539,542],{"class":170,"line":499},9,[168,501,502],{"class":178},"  assert",[168,504,125],{"class":185},[168,506,507],{"class":221},"ok",[168,509,225],{"class":234},[168,511,512],{"class":178},"res",[168,514,125],{"class":185},[168,516,507],{"class":178},[168,518,301],{"class":185},[168,520,521],{"class":185}," `${",[168,523,452],{"class":178},[168,525,328],{"class":185},[168,527,528],{"class":189}," returned ",[168,530,531],{"class":185},"${",[168,533,512],{"class":178},[168,535,125],{"class":185},[168,537,538],{"class":178},"status",[168,540,541],{"class":185},"}`",[168,543,331],{"class":234},[168,545,547,550,552,554,557],{"class":170,"line":546},10,[168,548,549],{"class":174},"  return",[168,551,466],{"class":178},[168,553,125],{"class":185},[168,555,556],{"class":221},"json",[168,558,559],{"class":234},"()\n",[168,561,563],{"class":170,"line":562},11,[168,564,431],{"class":185},[168,566,568],{"class":170,"line":567},12,[168,569,200],{"emptyLinePlaceholder":199},[168,571,573,575,577,580,582,585,587],{"class":170,"line":572},13,[168,574,440],{"class":206},[168,576,443],{"class":206},[168,578,579],{"class":221}," inboxOf",[168,581,225],{"class":185},[168,583,584],{"class":451},"address",[168,586,455],{"class":185},[168,588,458],{"class":185},[168,590,592,594,596,599,601,603,605,607,609,612,615,617,620,623,625],{"class":170,"line":591},14,[168,593,463],{"class":206},[168,595,287],{"class":185},[168,597,598],{"class":178}," member",[168,600,367],{"class":185},[168,602,469],{"class":185},[168,604,472],{"class":174},[168,606,446],{"class":221},[168,608,225],{"class":234},[168,610,611],{"class":185},"`",[168,613,614],{"class":189},"\u002Faccounts?address=",[168,616,531],{"class":185},[168,618,619],{"class":221},"encodeURIComponent",[168,621,622],{"class":178},"(address)",[168,624,541],{"class":185},[168,626,331],{"class":234},[168,628,630,632,635,637,639,641,644,646,648,651,654,656,658,661,664],{"class":170,"line":629},15,[168,631,463],{"class":206},[168,633,634],{"class":178}," account",[168,636,469],{"class":185},[168,638,598],{"class":178},[168,640,125],{"class":185},[168,642,643],{"class":221},"find",[168,645,225],{"class":234},[168,647,83],{"class":451},[168,649,650],{"class":206}," =>",[168,652,653],{"class":178}," a",[168,655,125],{"class":185},[168,657,584],{"class":178},[168,659,660],{"class":185}," ===",[168,662,663],{"class":178}," address",[168,665,331],{"class":234},[168,667,669,671,673,676,678,681,683,685,687,689,691,693,696,698,701,703,706,708],{"class":170,"line":668},16,[168,670,463],{"class":206},[168,672,287],{"class":185},[168,674,675],{"class":178}," id",[168,677,301],{"class":185},[168,679,680],{"class":178}," mailboxes",[168,682,367],{"class":185},[168,684,469],{"class":185},[168,686,472],{"class":174},[168,688,446],{"class":221},[168,690,225],{"class":234},[168,692,611],{"class":185},[168,694,695],{"class":189},"\u002Faccounts\u002F",[168,697,531],{"class":185},[168,699,700],{"class":178},"account",[168,702,125],{"class":185},[168,704,705],{"class":178},"id",[168,707,541],{"class":185},[168,709,331],{"class":234},[168,711,713,715,717,720,722,724,726,729,731,733,735,737,739,742,744,747,749,751,753,755,758,760,762,764,766],{"class":170,"line":712},17,[168,714,549],{"class":174},[168,716,287],{"class":185},[168,718,719],{"class":234}," accountId",[168,721,238],{"class":185},[168,723,675],{"class":178},[168,725,301],{"class":185},[168,727,728],{"class":234}," mailboxId",[168,730,238],{"class":185},[168,732,680],{"class":178},[168,734,125],{"class":185},[168,736,643],{"class":221},[168,738,225],{"class":234},[168,740,741],{"class":451},"m",[168,743,650],{"class":206},[168,745,746],{"class":178}," m",[168,748,125],{"class":185},[168,750,452],{"class":178},[168,752,660],{"class":185},[168,754,186],{"class":185},[168,756,757],{"class":189},"INBOX",[168,759,245],{"class":185},[168,761,455],{"class":234},[168,763,125],{"class":185},[168,765,705],{"class":178},[168,767,768],{"class":185}," }\n",[168,770,772],{"class":170,"line":771},18,[168,773,431],{"class":185},[168,775,777],{"class":170,"line":776},19,[168,778,200],{"emptyLinePlaceholder":199},[168,780,782,784,786,789,792,794,796,798,801,804,806,809,811,814,816],{"class":170,"line":781},20,[168,783,440],{"class":206},[168,785,443],{"class":206},[168,787,788],{"class":221}," waitForMessage",[168,790,791],{"class":185},"({",[168,793,719],{"class":451},[168,795,301],{"class":185},[168,797,728],{"class":451},[168,799,800],{"class":185}," },",[168,802,803],{"class":451}," match",[168,805,301],{"class":185},[168,807,808],{"class":451}," timeout",[168,810,469],{"class":185},[168,812,813],{"class":259}," 30_000",[168,815,455],{"class":185},[168,817,458],{"class":185},[168,819,821,823,826,828,831,833,836,839,842],{"class":170,"line":820},21,[168,822,463],{"class":206},[168,824,825],{"class":178}," deadline",[168,827,469],{"class":185},[168,829,830],{"class":178}," Date",[168,832,125],{"class":185},[168,834,835],{"class":221},"now",[168,837,838],{"class":234},"() ",[168,840,841],{"class":185},"+",[168,843,844],{"class":178}," timeout\n",[168,846,848,851,854,857,859,861,863,866,868,871],{"class":170,"line":847},22,[168,849,850],{"class":174},"  while",[168,852,853],{"class":234}," (",[168,855,856],{"class":178},"Date",[168,858,125],{"class":185},[168,860,835],{"class":221},[168,862,838],{"class":234},[168,864,865],{"class":185},"\u003C",[168,867,825],{"class":178},[168,869,870],{"class":234},") ",[168,872,228],{"class":185},[168,874,876,879,881,883,885,887,889,891,893,895,897,899,902,904,907,909,912,914,917,919],{"class":170,"line":875},23,[168,877,878],{"class":206},"    const",[168,880,287],{"class":185},[168,882,598],{"class":178},[168,884,367],{"class":185},[168,886,469],{"class":185},[168,888,472],{"class":174},[168,890,446],{"class":221},[168,892,225],{"class":234},[168,894,611],{"class":185},[168,896,695],{"class":189},[168,898,531],{"class":185},[168,900,901],{"class":178},"accountId",[168,903,328],{"class":185},[168,905,906],{"class":189},"\u002Fmailboxes\u002F",[168,908,531],{"class":185},[168,910,911],{"class":178},"mailboxId",[168,913,328],{"class":185},[168,915,916],{"class":189},"\u002Fmessages",[168,918,611],{"class":185},[168,920,331],{"class":234},[168,922,924,926,929,931,933,935,937,939,942],{"class":170,"line":923},24,[168,925,878],{"class":206},[168,927,928],{"class":178}," found",[168,930,469],{"class":185},[168,932,598],{"class":178},[168,934,125],{"class":185},[168,936,643],{"class":221},[168,938,225],{"class":234},[168,940,941],{"class":178},"match",[168,943,331],{"class":234},[168,945,947,950,952,955],{"class":170,"line":946},25,[168,948,949],{"class":174},"    if",[168,951,853],{"class":234},[168,953,954],{"class":178},"found",[168,956,331],{"class":234},[168,958,960,963,965,967,969,971,973,975,977,979,981,983,985,988,990,992,994,996,998],{"class":170,"line":959},26,[168,961,962],{"class":174},"      return",[168,964,446],{"class":221},[168,966,225],{"class":234},[168,968,611],{"class":185},[168,970,695],{"class":189},[168,972,531],{"class":185},[168,974,901],{"class":178},[168,976,328],{"class":185},[168,978,906],{"class":189},[168,980,531],{"class":185},[168,982,911],{"class":178},[168,984,328],{"class":185},[168,986,987],{"class":189},"\u002Fmessages\u002F",[168,989,531],{"class":185},[168,991,954],{"class":178},[168,993,125],{"class":185},[168,995,705],{"class":178},[168,997,541],{"class":185},[168,999,331],{"class":234},[168,1001,1003,1006,1009,1013,1015,1018,1020,1023,1025,1027,1029,1032],{"class":170,"line":1002},27,[168,1004,1005],{"class":174},"    await",[168,1007,1008],{"class":185}," new",[168,1010,1012],{"class":1011},"sBMFI"," Promise",[168,1014,225],{"class":234},[168,1016,1017],{"class":451},"resolve",[168,1019,650],{"class":206},[168,1021,1022],{"class":221}," setTimeout",[168,1024,225],{"class":234},[168,1026,1017],{"class":178},[168,1028,301],{"class":185},[168,1030,1031],{"class":259}," 2000",[168,1033,1034],{"class":234},"))\n",[168,1036,1038],{"class":170,"line":1037},28,[168,1039,1040],{"class":185},"  }\n",[168,1042,1044,1047,1049,1052,1054,1056,1059,1061,1064,1066,1069,1071],{"class":170,"line":1043},29,[168,1045,1046],{"class":174},"  throw",[168,1048,1008],{"class":185},[168,1050,1051],{"class":221}," Error",[168,1053,225],{"class":234},[168,1055,611],{"class":185},[168,1057,1058],{"class":189},"no matching message after ",[168,1060,531],{"class":185},[168,1062,1063],{"class":178},"timeout",[168,1065,328],{"class":185},[168,1067,1068],{"class":189},"ms",[168,1070,611],{"class":185},[168,1072,331],{"class":234},[168,1074,1076],{"class":170,"line":1075},30,[168,1077,431],{"class":185},[168,1079,1081],{"class":170,"line":1080},31,[168,1082,200],{"emptyLinePlaceholder":199},[168,1084,1086,1089,1091,1093,1096,1098,1100,1103,1106,1108],{"class":170,"line":1085},32,[168,1087,1088],{"class":221},"test",[168,1090,225],{"class":178},[168,1092,245],{"class":185},[168,1094,1095],{"class":189},"welcome email",[168,1097,245],{"class":185},[168,1099,301],{"class":185},[168,1101,1102],{"class":206}," async",[168,1104,1105],{"class":185}," ()",[168,1107,650],{"class":206},[168,1109,458],{"class":185},[168,1111,1113,1115,1118,1120,1123,1126,1128,1130,1132,1134,1137],{"class":170,"line":1112},33,[168,1114,463],{"class":206},[168,1116,1117],{"class":178}," subject",[168,1119,469],{"class":185},[168,1121,1122],{"class":185}," `",[168,1124,1125],{"class":189},"Welcome ",[168,1127,531],{"class":185},[168,1129,856],{"class":178},[168,1131,125],{"class":185},[168,1133,835],{"class":221},[168,1135,1136],{"class":178},"()",[168,1138,1139],{"class":185},"}`\n",[168,1141,1143,1146,1149,1151,1154,1156],{"class":170,"line":1142},34,[168,1144,1145],{"class":174},"  await",[168,1147,1148],{"class":178}," transport",[168,1150,125],{"class":185},[168,1152,1153],{"class":221},"sendMail",[168,1155,225],{"class":234},[168,1157,228],{"class":185},[168,1159,1161,1164,1166,1168,1170,1172],{"class":170,"line":1160},35,[168,1162,1163],{"class":234},"    from",[168,1165,238],{"class":185},[168,1167,186],{"class":185},[168,1169,120],{"class":189},[168,1171,245],{"class":185},[168,1173,248],{"class":185},[168,1175,1177,1180,1182,1184,1186,1188],{"class":170,"line":1176},36,[168,1178,1179],{"class":234},"    to",[168,1181,238],{"class":185},[168,1183,186],{"class":185},[168,1185,124],{"class":189},[168,1187,245],{"class":185},[168,1189,248],{"class":185},[168,1191,1193,1196],{"class":170,"line":1192},37,[168,1194,1195],{"class":178},"    subject",[168,1197,248],{"class":185},[168,1199,1201,1204,1206,1208,1211,1213],{"class":170,"line":1200},38,[168,1202,1203],{"class":234},"    text",[168,1205,238],{"class":185},[168,1207,186],{"class":185},[168,1209,1210],{"class":189},"Confirm your address: https:\u002F\u002Fexample.com\u002Fconfirm\u002Fabc123",[168,1212,245],{"class":185},[168,1214,248],{"class":185},[168,1216,1218,1221],{"class":170,"line":1217},39,[168,1219,1220],{"class":185},"  }",[168,1222,331],{"class":234},[168,1224,1226],{"class":170,"line":1225},40,[168,1227,200],{"emptyLinePlaceholder":199},[168,1229,1231,1233,1236,1238,1240,1242,1244,1246,1248,1250],{"class":170,"line":1230},41,[168,1232,463],{"class":206},[168,1234,1235],{"class":178}," inbox",[168,1237,469],{"class":185},[168,1239,472],{"class":174},[168,1241,579],{"class":221},[168,1243,225],{"class":234},[168,1245,245],{"class":185},[168,1247,124],{"class":189},[168,1249,245],{"class":185},[168,1251,331],{"class":234},[168,1253,1255,1257,1260,1262,1264,1266,1268,1271,1273,1275,1277,1279,1281,1284,1286,1288],{"class":170,"line":1254},42,[168,1256,463],{"class":206},[168,1258,1259],{"class":178}," message",[168,1261,469],{"class":185},[168,1263,472],{"class":174},[168,1265,788],{"class":221},[168,1267,225],{"class":234},[168,1269,1270],{"class":178},"inbox",[168,1272,301],{"class":185},[168,1274,746],{"class":451},[168,1276,650],{"class":206},[168,1278,746],{"class":178},[168,1280,125],{"class":185},[168,1282,1283],{"class":178},"subject",[168,1285,660],{"class":185},[168,1287,1117],{"class":178},[168,1289,331],{"class":234},[168,1291,1293],{"class":170,"line":1292},43,[168,1294,200],{"emptyLinePlaceholder":199},[168,1296,1298,1300,1302,1305,1307,1310,1312,1314,1316,1318,1320,1322,1324,1326],{"class":170,"line":1297},44,[168,1299,502],{"class":178},[168,1301,125],{"class":185},[168,1303,1304],{"class":221},"equal",[168,1306,225],{"class":234},[168,1308,1309],{"class":178},"message",[168,1311,125],{"class":185},[168,1313,182],{"class":178},[168,1315,125],{"class":185},[168,1317,584],{"class":178},[168,1319,301],{"class":185},[168,1321,186],{"class":185},[168,1323,120],{"class":189},[168,1325,245],{"class":185},[168,1327,331],{"class":234},[168,1329,1331,1333,1335,1337,1339,1341,1343,1346,1348,1351,1354,1357,1359,1362],{"class":170,"line":1330},45,[168,1332,502],{"class":178},[168,1334,125],{"class":185},[168,1336,941],{"class":221},[168,1338,225],{"class":234},[168,1340,1309],{"class":178},[168,1342,125],{"class":185},[168,1344,1345],{"class":178},"text",[168,1347,301],{"class":185},[168,1349,1350],{"class":185}," \u002F",[168,1352,1353],{"class":178},"\\\u002F",[168,1355,1356],{"class":189},"confirm",[168,1358,1353],{"class":178},[168,1360,1361],{"class":185},"\u002F",[168,1363,331],{"class":234},[168,1365,1367,1369],{"class":170,"line":1366},46,[168,1368,328],{"class":185},[168,1370,331],{"class":178},[79,1372,1373,1374,1378],{},"Put a unique value in the subject (a timestamp, the commit SHA). Parallel runs share the inbox, and ",[83,1375,1377],{"href":1376},"\u002Fdocs\u002Fapi#list-messages","List Messages"," returns 30 messages per page, newest first.",[93,1380,1382],{"id":1381},"the-workflow","The Workflow",[159,1384,1388],{"className":1385,"code":1386,"language":1387,"meta":164,"style":164},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","name: email-tests\non: push\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v5\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version: 24\n      - run: npm ci\n      - run: npm test\n        env:\n          SMTP_DEV_API_KEY: ${{ secrets.SMTP_DEV_API_KEY }}\n          SMTP_DEV_PASSWORD: ${{ secrets.SMTP_DEV_PASSWORD }}\n","yaml",[88,1389,1390,1400,1410,1414,1422,1429,1439,1446,1459,1470,1477,1487,1499,1510,1517,1527],{"__ignoreMap":164},[168,1391,1392,1395,1397],{"class":170,"line":171},[168,1393,1394],{"class":234},"name",[168,1396,238],{"class":185},[168,1398,1399],{"class":189}," email-tests\n",[168,1401,1402,1405,1407],{"class":170,"line":196},[168,1403,1404],{"class":273},"on",[168,1406,238],{"class":185},[168,1408,1409],{"class":189}," push\n",[168,1411,1412],{"class":170,"line":203},[168,1413,200],{"emptyLinePlaceholder":199},[168,1415,1416,1419],{"class":170,"line":231},[168,1417,1418],{"class":234},"jobs",[168,1420,1421],{"class":185},":\n",[168,1423,1424,1427],{"class":170,"line":251},[168,1425,1426],{"class":234},"  test",[168,1428,1421],{"class":185},[168,1430,1431,1434,1436],{"class":170,"line":265},[168,1432,1433],{"class":234},"    runs-on",[168,1435,238],{"class":185},[168,1437,1438],{"class":189}," ubuntu-latest\n",[168,1440,1441,1444],{"class":170,"line":279},[168,1442,1443],{"class":234},"    steps",[168,1445,1421],{"class":185},[168,1447,1448,1451,1454,1456],{"class":170,"line":325},[168,1449,1450],{"class":185},"      -",[168,1452,1453],{"class":234}," uses",[168,1455,238],{"class":185},[168,1457,1458],{"class":189}," actions\u002Fcheckout@v5\n",[168,1460,1461,1463,1465,1467],{"class":170,"line":499},[168,1462,1450],{"class":185},[168,1464,1453],{"class":234},[168,1466,238],{"class":185},[168,1468,1469],{"class":189}," actions\u002Fsetup-node@v4\n",[168,1471,1472,1475],{"class":170,"line":546},[168,1473,1474],{"class":234},"        with",[168,1476,1421],{"class":185},[168,1478,1479,1482,1484],{"class":170,"line":562},[168,1480,1481],{"class":234},"          node-version",[168,1483,238],{"class":185},[168,1485,1486],{"class":259}," 24\n",[168,1488,1489,1491,1494,1496],{"class":170,"line":567},[168,1490,1450],{"class":185},[168,1492,1493],{"class":234}," run",[168,1495,238],{"class":185},[168,1497,1498],{"class":189}," npm ci\n",[168,1500,1501,1503,1505,1507],{"class":170,"line":572},[168,1502,1450],{"class":185},[168,1504,1493],{"class":234},[168,1506,238],{"class":185},[168,1508,1509],{"class":189}," npm test\n",[168,1511,1512,1515],{"class":170,"line":591},[168,1513,1514],{"class":234},"        env",[168,1516,1421],{"class":185},[168,1518,1519,1522,1524],{"class":170,"line":629},[168,1520,1521],{"class":234},"          SMTP_DEV_API_KEY",[168,1523,238],{"class":185},[168,1525,1526],{"class":189}," ${{ secrets.SMTP_DEV_API_KEY }}\n",[168,1528,1529,1532,1534],{"class":170,"line":668},[168,1530,1531],{"class":234},"          SMTP_DEV_PASSWORD",[168,1533,238],{"class":185},[168,1535,1536],{"class":189}," ${{ secrets.SMTP_DEV_PASSWORD }}\n",[79,1538,1539],{},"Nothing here is GitHub-specific. Jenkins, GitLab CI, CircleCI: same two env vars, same SMTP host, same API.",[1541,1542,1543],"note",{},[79,1544,1545,1546,1550,1551,1555],{},"Messages stay for your plan's retention window, or ",[83,1547,1549],{"href":1548},"\u002Fdocs\u002Ffeatures\u002Fmessage#message-expiry","until you delete them",". Unique subjects keep old runs out of the way; ",[83,1552,1554],{"href":1553},"\u002Fdocs\u002Fapi#delete-a-message","delete messages"," after each test if you'd rather keep inboxes empty.",[1557,1558,1559],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":164,"searchDepth":196,"depth":196,"links":1561},[1562,1567,1568],{"id":95,"depth":196,"text":96,"children":1563},[1564,1565,1566],{"id":104,"depth":203,"text":105},{"id":128,"depth":203,"text":129},{"id":139,"depth":203,"text":140},{"id":153,"depth":196,"text":154},{"id":1381,"depth":196,"text":1382},"Send from your test suite through send.smtp.dev, poll the REST API for the message, and assert on subject and body. Works on any CI.","md",{},{"title":49},{"title":74,"description":1569},"FAqUoxn9Zu2sdZYSpUnyKaMsCcXFPbLAr5jkP_3sH-c",[1576,1578],{"title":40,"path":41,"stem":42,"description":1577,"children":-1},"HTML, plain text, and raw source views. SPF\u002FDKIM\u002FDMARC results on received messages, deliverability audits, and a composer for test emails.",{"title":53,"path":54,"stem":55,"description":1579,"children":-1},"A * account catches mail for every address on your domain that has no account of its own. A fresh address per test, no setup per address.",1784164786180]