[{"data":1,"prerenderedAt":1213},["ShallowReactive",2],{"navigation":3,"\u002Fbooki-documentation\u002Fguides\u002Fguides-gmail-setup":303,"\u002Fbooki-documentation\u002Fguides\u002Fguides-gmail-setup-surround":1208},[4,55,105],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":54},"Nuxt Guide","i-lucide-layout","\u002Fnuxt-guide","1.nuxt-guide",[10,14,19,24,29,34,39,44,49],{"title":11,"path":12,"stem":13,"icon":6},"Overview","\u002Fnuxt-guide\u002Foverview","1.nuxt-guide\u002F1.overview",{"title":15,"path":16,"stem":17,"icon":18},"Folder Structure","\u002Fnuxt-guide\u002Ffolder-structure","1.nuxt-guide\u002F2.folder-structure","i-lucide-folder-tree",{"title":20,"path":21,"stem":22,"icon":23},"Components","\u002Fnuxt-guide\u002Fcomponents","1.nuxt-guide\u002F3.components","i-lucide-component",{"title":25,"path":26,"stem":27,"icon":28},"Composables","\u002Fnuxt-guide\u002Fcomposables","1.nuxt-guide\u002F4.composables","i-lucide-puzzle",{"title":30,"path":31,"stem":32,"icon":33},"Pages & Routing","\u002Fnuxt-guide\u002Fpages-routing","1.nuxt-guide\u002F5.pages-routing","i-lucide-file-text",{"title":35,"path":36,"stem":37,"icon":38},"Middleware","\u002Fnuxt-guide\u002Fmiddleware","1.nuxt-guide\u002F6.middleware","i-lucide-shield-check",{"title":40,"path":41,"stem":42,"icon":43},"Plugins","\u002Fnuxt-guide\u002Fplugins","1.nuxt-guide\u002F7.plugins","i-lucide-plug",{"title":45,"path":46,"stem":47,"icon":48},"State Management","\u002Fnuxt-guide\u002Fstate-management","1.nuxt-guide\u002F8.state-management","i-lucide-database",{"title":50,"path":51,"stem":52,"icon":53},"TypeScript","\u002Fnuxt-guide\u002Ftypescript","1.nuxt-guide\u002F9.typescript","i-lucide-braces",false,{"title":56,"icon":57,"path":58,"stem":59,"children":60,"page":54},"Node \u002F Express Guide","i-lucide-server","\u002Fnode-express-guide","2.node-express-guide",[61,64,69,72,77,82,86,91,96,100],{"title":11,"path":62,"stem":63,"icon":57},"\u002Fnode-express-guide\u002Foverview","2.node-express-guide\u002F1.overview",{"title":65,"path":66,"stem":67,"icon":68},"Error Handling","\u002Fnode-express-guide\u002Ferror-handling","2.node-express-guide\u002F10.error-handling","i-lucide-alert-triangle",{"title":15,"path":70,"stem":71,"icon":18},"\u002Fnode-express-guide\u002Ffolder-structure","2.node-express-guide\u002F2.folder-structure",{"title":73,"path":74,"stem":75,"icon":76},"Controllers","\u002Fnode-express-guide\u002Fcontrollers","2.node-express-guide\u002F3.controllers","i-lucide-cpu",{"title":78,"path":79,"stem":80,"icon":81},"Services","\u002Fnode-express-guide\u002Fservices","2.node-express-guide\u002F4.services","i-lucide-workflow",{"title":83,"path":84,"stem":85,"icon":48},"Repositories","\u002Fnode-express-guide\u002Frepositories","2.node-express-guide\u002F5.repositories",{"title":87,"path":88,"stem":89,"icon":90},"Models","\u002Fnode-express-guide\u002Fmodels","2.node-express-guide\u002F6.models","i-lucide-boxes",{"title":92,"path":93,"stem":94,"icon":95},"Routes","\u002Fnode-express-guide\u002Froutes","2.node-express-guide\u002F7.routes","i-lucide-route",{"title":35,"path":97,"stem":98,"icon":99},"\u002Fnode-express-guide\u002Fmiddleware","2.node-express-guide\u002F8.middleware","i-lucide-shield",{"title":101,"path":102,"stem":103,"icon":104},"Validation","\u002Fnode-express-guide\u002Fvalidation","2.node-express-guide\u002F9.validation","i-lucide-check-circle",{"title":106,"icon":107,"path":108,"stem":109,"children":110,"page":54},"Booki Documentation","i-lucide-book-open","\u002Fbooki-documentation","3.booki-documentation",[111,115,120,125,130,229,244,257],{"title":112,"path":113,"stem":114,"icon":107},"Project Overview","\u002Fbooki-documentation\u002Foverview","3.booki-documentation\u002F1.overview",{"title":116,"path":117,"stem":118,"icon":119},"Installation","\u002Fbooki-documentation\u002F1.installation","3.booki-documentation\u002F2.1.installation","i-lucide-download",{"title":121,"path":122,"stem":123,"icon":124},"Development Overview","\u002Fbooki-documentation\u002F2.architecture","3.booki-documentation\u002F2.2.architecture","i-lucide-layers",{"title":126,"path":127,"stem":128,"icon":129},"Local Development","\u002Fbooki-documentation\u002F3.local-development","3.booki-documentation\u002F2.3.local-development","i-lucide-monitor",{"title":131,"icon":57,"path":132,"stem":133,"children":134,"page":54},"API","\u002Fbooki-documentation\u002Fapi","3.booki-documentation\u002F3.api",[135,139,144,149,154,159,163,168,173,178,183,188,192,197,202,206,211,216,220,224],{"title":136,"path":137,"stem":138,"icon":57},"Booki API Reference","\u002Fbooki-documentation\u002Fapi\u002Fbooki-api","3.booki-documentation\u002F3.api\u002F1.booki-api",{"title":140,"path":141,"stem":142,"icon":143},"Auth - Login & Refresh","\u002Fbooki-documentation\u002Fapi\u002Fauth","3.booki-documentation\u002F3.api\u002F2.auth","i-lucide-key",{"title":145,"path":146,"stem":147,"icon":148},"Auth - Customer Registration","\u002Fbooki-documentation\u002Fapi\u002Fauth-customer-register","3.booki-documentation\u002F3.api\u002F2.auth-customer-register","i-lucide-user-check",{"title":150,"path":151,"stem":152,"icon":153},"Auth - Owner Registration","\u002Fbooki-documentation\u002Fapi\u002Fauth-owner-register","3.booki-documentation\u002F3.api\u002F2.auth-owner-register","i-lucide-user-plus",{"title":155,"path":156,"stem":157,"icon":158},"Bookings","\u002Fbooki-documentation\u002Fapi\u002Fbookings","3.booki-documentation\u002F3.api\u002F3.bookings","i-lucide-calendar-check",{"title":160,"path":161,"stem":162,"icon":158},"Bookings - Authenticated","\u002Fbooki-documentation\u002Fapi\u002Fbookings-auth","3.booki-documentation\u002F3.api\u002F3.bookings-auth",{"title":164,"path":165,"stem":166,"icon":167},"Bookings - Public & Guest","\u002Fbooki-documentation\u002Fapi\u002Fbookings-public","3.booki-documentation\u002F3.api\u002F3.bookings-public","i-lucide-calendar-plus",{"title":169,"path":170,"stem":171,"icon":172},"Booking Services","\u002Fbooki-documentation\u002Fapi\u002Fbooking-services","3.booki-documentation\u002F3.api\u002F4.booking-services","i-lucide-box",{"title":174,"path":175,"stem":176,"icon":177},"Organizations - Branches","\u002Fbooki-documentation\u002Fapi\u002Fbranches","3.booki-documentation\u002F3.api\u002F5.branches","i-lucide-git-branch",{"title":179,"path":180,"stem":181,"icon":182},"Organizations - Business Hours","\u002Fbooki-documentation\u002Fapi\u002Fbusiness-hours","3.booki-documentation\u002F3.api\u002F5.business-hours","i-lucide-clock",{"title":184,"path":185,"stem":186,"icon":187},"Organizations - Base","\u002Fbooki-documentation\u002Fapi\u002Forganizations","3.booki-documentation\u002F3.api\u002F5.organizations","i-lucide-building-2",{"title":189,"path":190,"stem":191,"icon":172},"Organizations - Packages","\u002Fbooki-documentation\u002Fapi\u002Fpackages","3.booki-documentation\u002F3.api\u002F5.packages",{"title":193,"path":194,"stem":195,"icon":196},"Organizations - Payments & Integrations","\u002Fbooki-documentation\u002Fapi\u002Fpayments","3.booki-documentation\u002F3.api\u002F5.payments","i-lucide-credit-card",{"title":198,"path":199,"stem":200,"icon":201},"Users - Profile & Settings","\u002Fbooki-documentation\u002Fapi\u002Fusers","3.booki-documentation\u002F3.api\u002F6.users","i-lucide-user",{"title":203,"path":204,"stem":205,"icon":196},"Maya Payments","\u002Fbooki-documentation\u002Fapi\u002Fmaya","3.booki-documentation\u002F3.api\u002F7.maya",{"title":207,"path":208,"stem":209,"icon":210},"Owner - Booking Management","\u002Fbooki-documentation\u002Fapi\u002Fowner-bookings","3.booki-documentation\u002F3.api\u002F7.owner-bookings","i-lucide-calendar",{"title":212,"path":213,"stem":214,"icon":215},"Owner - Customer Management","\u002Fbooki-documentation\u002Fapi\u002Fowner-customers","3.booki-documentation\u002F3.api\u002F7.owner-customers","i-lucide-users",{"title":217,"path":218,"stem":219,"icon":196},"Admin - Billing & Subscriptions","\u002Fbooki-documentation\u002Fapi\u002Fadmin-billing","3.booki-documentation\u002F3.api\u002F8.admin-billing",{"title":221,"path":222,"stem":223,"icon":187},"Admin - Organizations Management","\u002Fbooki-documentation\u002Fapi\u002Fadmin-organizations","3.booki-documentation\u002F3.api\u002F8.admin-organizations",{"title":225,"path":226,"stem":227,"icon":228},"Admin - User Management","\u002Fbooki-documentation\u002Fapi\u002Fadmin-users","3.booki-documentation\u002F3.api\u002F8.admin-users","i-lucide-shield-admin",{"title":230,"icon":231,"path":232,"stem":233,"children":234,"page":54},"Shared","i-lucide-package","\u002Fbooki-documentation\u002Fshared","3.booki-documentation\u002F4.shared",[235,240],{"title":236,"path":237,"stem":238,"icon":239},"codi-node-utils","\u002Fbooki-documentation\u002Fshared\u002Fcodi-node-utils","3.booki-documentation\u002F4.shared\u002F1.codi-node-utils","i-lucide-wrench",{"title":241,"path":242,"stem":243,"icon":124},"codi-layer","\u002Fbooki-documentation\u002Fshared\u002Fcodi-layer","3.booki-documentation\u002F4.shared\u002F2.codi-layer",{"title":245,"path":246,"stem":247,"children":248,"page":54},"Flowcharts","\u002Fbooki-documentation\u002Fflowcharts","3.booki-documentation\u002F5.flowcharts",[249,253],{"title":250,"path":251,"stem":252,"icon":158},"Owner Booking Management Flow","\u002Fbooki-documentation\u002Fflowcharts\u002Fowner-booking-management","3.booki-documentation\u002F5.flowcharts\u002F1.owner-booking-management",{"title":254,"path":255,"stem":256,"icon":167},"Customer Booking Flow","\u002Fbooki-documentation\u002Fflowcharts\u002Fcustomer-booking-flow","3.booki-documentation\u002F5.flowcharts\u002F2.customer-booking-flow",{"title":258,"path":259,"stem":260,"children":261,"page":54},"Guides","\u002Fbooki-documentation\u002Fguides","3.booki-documentation\u002Fguides",[262,267,271,275,280,285,290,294,298],{"title":263,"path":264,"stem":265,"icon":266},"Guide - Authentication Flow & Sessions","\u002Fbooki-documentation\u002Fguides\u002Fguides-auth-flow","3.booki-documentation\u002Fguides\u002Fguides-auth-flow","i-lucide-lock",{"title":268,"path":269,"stem":270,"icon":177},"Guide - Branch Management","\u002Fbooki-documentation\u002Fguides\u002Fguides-branch-management","3.booki-documentation\u002Fguides\u002Fguides-branch-management",{"title":272,"path":273,"stem":274,"icon":196},"Guide - Customer Payment Methods","\u002Fbooki-documentation\u002Fguides\u002Fguides-customer-payments","3.booki-documentation\u002Fguides\u002Fguides-customer-payments",{"title":276,"path":277,"stem":278,"icon":279},"Guide - Environment Setup","\u002Fbooki-documentation\u002Fguides\u002Fguides-environment-setup","3.booki-documentation\u002Fguides\u002Fguides-environment-setup","i-lucide-settings",{"title":281,"path":282,"stem":283,"icon":284},"Guide - Common Error Responses","\u002Fbooki-documentation\u002Fguides\u002Fguides-errors","3.booki-documentation\u002Fguides\u002Fguides-errors","i-lucide-alert-circle",{"title":286,"path":287,"stem":288,"icon":289},"Guide - Gmail SMTP Setup","\u002Fbooki-documentation\u002Fguides\u002Fguides-gmail-setup","3.booki-documentation\u002Fguides\u002Fguides-gmail-setup","i-lucide-mail",{"title":291,"path":292,"stem":293,"icon":124},"Guide - Multi-Tenancy & Tenant Slug Resolution","\u002Fbooki-documentation\u002Fguides\u002Fguides-multi-tenancy","3.booki-documentation\u002Fguides\u002Fguides-multi-tenancy",{"title":295,"path":296,"stem":297,"icon":48},"Guide - Redis Caching","\u002Fbooki-documentation\u002Fguides\u002Fguides-redis","3.booki-documentation\u002Fguides\u002Fguides-redis",{"title":299,"path":300,"stem":301,"icon":302},"Guide - Subscription Billing","\u002Fbooki-documentation\u002Fguides\u002Fguides-subscription-billing","3.booki-documentation\u002Fguides\u002Fguides-subscription-billing","i-lucide-receipt",{"id":304,"title":286,"body":305,"description":1201,"extension":1202,"links":1203,"meta":1204,"navigation":1205,"path":287,"seo":1206,"stem":288,"__hash__":1207},"docs\u002F3.booki-documentation\u002Fguides\u002Fguides-gmail-setup.md",{"type":306,"value":307,"toc":1188},"minimark",[308,325,330,348,351,355,370,374,416,425,429,436,446,452,457,464,983,986,1005,1008,1012,1021,1025,1028,1043,1077,1080,1084,1098,1112,1121,1133,1139,1143,1166,1170,1184],[309,310,311,312,316,317,320,321,324],"p",{},"This guide explains how to generate a Gmail App Password and configure the ",[313,314,315],"code",{},"GMAIL_USER"," and ",[313,318,319],{},"GMAIL_APP_PASSWORD"," environment variables for the ",[313,322,323],{},"booki-api"," Node service. It covers Nodemailer usage, CI secrets, testing, troubleshooting, and security recommendations.",[326,327,329],"h2",{"id":328},"summary","Summary",[331,332,333,343],"ul",{},[334,335,336,338,339,342],"li",{},[313,337,315],{},": your full Gmail address (for example ",[313,340,341],{},"you@gmail.com",").",[334,344,345,347],{},[313,346,319],{},": a 16-character App Password generated from your Google account (used instead of your regular Google password when 2-Step Verification is enabled).",[309,349,350],{},"App Passwords let you authenticate SMTP clients when 2-Step Verification is enabled. They are safer than using your primary Google password and can be revoked independently.",[326,352,354],{"id":353},"prerequisites","Prerequisites",[331,356,357,360,363],{},[334,358,359],{},"Enable 2-Step Verification (2FA) on the Google account you intend to use.",[334,361,362],{},"App Passwords are available for personal Google accounts. For Google Workspace (G Suite) accounts your admin may need to enable or allow App Passwords.",[334,364,365,366,369],{},"Never commit credentials to source control. Use ",[313,367,368],{},".env"," for local development and a secret manager for production\u002FCI.",[326,371,373],{"id":372},"create-an-app-password","Create an App Password",[375,376,377,386,389,392],"ol",{},[334,378,379,380],{},"Open: ",[381,382,383],"a",{"href":383,"rel":384},"https:\u002F\u002Fmyaccount.google.com\u002Fsecurity",[385],"nofollow",[334,387,388],{},"Under \"Signing in to Google\" enable \"2-Step Verification\" (if not already enabled).",[334,390,391],{},"After enabling 2-Step Verification, click \"App passwords\".",[334,393,394,395],{},"Create a new app password:\n",[331,396,397,410,413],{},[334,398,399,400,403,404,407,408,342],{},"App: choose ",[313,401,402],{},"Mail"," (or ",[313,405,406],{},"Other"," and give a descriptive name like ",[313,409,323],{},[334,411,412],{},"Device: choose an appropriate device or enter a custom name.",[334,414,415],{},"Click \"Generate\" and copy the 16-character password (groups of 4). You won't be able to view it again.",[309,417,418,419,421,422,424],{},"Store the value somewhere secure and add it to your ",[313,420,368],{}," as ",[313,423,319],{},".",[326,426,428],{"id":427},"local-setup","Local Setup",[309,430,431,432,435],{},"Create or update ",[313,433,434],{},"booki-api\u002F.env"," (do not commit this file):",[437,438,443],"pre",{"className":439,"code":441,"language":442},[440],"language-text","GMAIL_USER=you@gmail.com\nGMAIL_APP_PASSWORD=abcdefghijklmnop\n","text",[313,444,441],{"__ignoreMap":445},"",[447,448,449],"blockquote",{},[309,450,451],{},"Do not include spaces — Google displays the password in groups of 4 for readability, but enter the 16 characters without spaces.",[453,454,456],"h3",{"id":455},"minimal-nodemailer-example-test-script","Minimal Nodemailer Example (test script)",[309,458,459,460,463],{},"Save this as ",[313,461,462],{},"booki-api\u002Ftest-email.ts"," and run it to verify SMTP connectivity.",[437,465,469],{"className":466,"code":467,"language":468,"meta":445,"style":445},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F booki-api\u002Ftest-email.ts\nimport nodemailer from \"nodemailer\";\nimport dotenv from \"dotenv\";\ndotenv.config();\n\nasync function sendTest() {\n  const transporter = nodemailer.createTransport({\n    host: \"smtp.gmail.com\",\n    port: 587,\n    secure: false, \u002F\u002F use TLS via STARTTLS\n    auth: {\n      user: process.env.GMAIL_USER,\n      pass: process.env.GMAIL_APP_PASSWORD,\n    },\n  });\n\n  const info = await transporter.sendMail({\n    from: `Booking App \u003C${process.env.GMAIL_USER}>`,\n    to: process.env.GMAIL_USER, \u002F\u002F send to yourself for quick test\n    subject: \"booki-api: SMTP test\",\n    text: \"This is a test email sent from booki-api using an App Password.\",\n  });\n\n  console.log(\"Message sent:\", info.messageId);\n}\n\nsendTest().catch((err) => {\n  console.error(\"Failed to send test email:\", err);\n  process.exit(1);\n});\n","js",[313,470,471,480,508,527,543,550,568,595,614,628,646,656,678,698,704,715,720,744,783,806,823,840,849,854,887,893,898,926,954,974],{"__ignoreMap":445},[472,473,476],"span",{"class":474,"line":475},"line",1,[472,477,479],{"class":478},"sHwdD","\u002F\u002F booki-api\u002Ftest-email.ts\n",[472,481,483,487,491,494,498,502,505],{"class":474,"line":482},2,[472,484,486],{"class":485},"s7zQu","import",[472,488,490],{"class":489},"sTEyZ"," nodemailer ",[472,492,493],{"class":485},"from",[472,495,497],{"class":496},"sMK4o"," \"",[472,499,501],{"class":500},"sfazB","nodemailer",[472,503,504],{"class":496},"\"",[472,506,507],{"class":496},";\n",[472,509,511,513,516,518,520,523,525],{"class":474,"line":510},3,[472,512,486],{"class":485},[472,514,515],{"class":489}," dotenv ",[472,517,493],{"class":485},[472,519,497],{"class":496},[472,521,522],{"class":500},"dotenv",[472,524,504],{"class":496},[472,526,507],{"class":496},[472,528,530,532,534,538,541],{"class":474,"line":529},4,[472,531,522],{"class":489},[472,533,424],{"class":496},[472,535,537],{"class":536},"s2Zo4","config",[472,539,540],{"class":489},"()",[472,542,507],{"class":496},[472,544,546],{"class":474,"line":545},5,[472,547,549],{"emptyLinePlaceholder":548},true,"\n",[472,551,553,557,560,563,565],{"class":474,"line":552},6,[472,554,556],{"class":555},"spNyl","async",[472,558,559],{"class":555}," function",[472,561,562],{"class":536}," sendTest",[472,564,540],{"class":496},[472,566,567],{"class":496}," {\n",[472,569,571,574,577,580,583,585,588,592],{"class":474,"line":570},7,[472,572,573],{"class":555},"  const",[472,575,576],{"class":489}," transporter",[472,578,579],{"class":496}," =",[472,581,582],{"class":489}," nodemailer",[472,584,424],{"class":496},[472,586,587],{"class":536},"createTransport",[472,589,591],{"class":590},"swJcz","(",[472,593,594],{"class":496},"{\n",[472,596,598,601,604,606,609,611],{"class":474,"line":597},8,[472,599,600],{"class":590},"    host",[472,602,603],{"class":496},":",[472,605,497],{"class":496},[472,607,608],{"class":500},"smtp.gmail.com",[472,610,504],{"class":496},[472,612,613],{"class":496},",\n",[472,615,617,620,622,626],{"class":474,"line":616},9,[472,618,619],{"class":590},"    port",[472,621,603],{"class":496},[472,623,625],{"class":624},"sbssI"," 587",[472,627,613],{"class":496},[472,629,631,634,636,640,643],{"class":474,"line":630},10,[472,632,633],{"class":590},"    secure",[472,635,603],{"class":496},[472,637,639],{"class":638},"sfNiH"," false",[472,641,642],{"class":496},",",[472,644,645],{"class":478}," \u002F\u002F use TLS via STARTTLS\n",[472,647,649,652,654],{"class":474,"line":648},11,[472,650,651],{"class":590},"    auth",[472,653,603],{"class":496},[472,655,567],{"class":496},[472,657,659,662,664,667,669,672,674,676],{"class":474,"line":658},12,[472,660,661],{"class":590},"      user",[472,663,603],{"class":496},[472,665,666],{"class":489}," process",[472,668,424],{"class":496},[472,670,671],{"class":489},"env",[472,673,424],{"class":496},[472,675,315],{"class":489},[472,677,613],{"class":496},[472,679,681,684,686,688,690,692,694,696],{"class":474,"line":680},13,[472,682,683],{"class":590},"      pass",[472,685,603],{"class":496},[472,687,666],{"class":489},[472,689,424],{"class":496},[472,691,671],{"class":489},[472,693,424],{"class":496},[472,695,319],{"class":489},[472,697,613],{"class":496},[472,699,701],{"class":474,"line":700},14,[472,702,703],{"class":496},"    },\n",[472,705,707,710,713],{"class":474,"line":706},15,[472,708,709],{"class":496},"  }",[472,711,712],{"class":590},")",[472,714,507],{"class":496},[472,716,718],{"class":474,"line":717},16,[472,719,549],{"emptyLinePlaceholder":548},[472,721,723,725,728,730,733,735,737,740,742],{"class":474,"line":722},17,[472,724,573],{"class":555},[472,726,727],{"class":489}," info",[472,729,579],{"class":496},[472,731,732],{"class":485}," await",[472,734,576],{"class":489},[472,736,424],{"class":496},[472,738,739],{"class":536},"sendMail",[472,741,591],{"class":590},[472,743,594],{"class":496},[472,745,747,750,752,755,758,761,764,766,768,770,772,775,778,781],{"class":474,"line":746},18,[472,748,749],{"class":590},"    from",[472,751,603],{"class":496},[472,753,754],{"class":496}," `",[472,756,757],{"class":500},"Booking App \u003C",[472,759,760],{"class":496},"${",[472,762,763],{"class":489},"process",[472,765,424],{"class":496},[472,767,671],{"class":489},[472,769,424],{"class":496},[472,771,315],{"class":489},[472,773,774],{"class":496},"}",[472,776,777],{"class":500},">",[472,779,780],{"class":496},"`",[472,782,613],{"class":496},[472,784,786,789,791,793,795,797,799,801,803],{"class":474,"line":785},19,[472,787,788],{"class":590},"    to",[472,790,603],{"class":496},[472,792,666],{"class":489},[472,794,424],{"class":496},[472,796,671],{"class":489},[472,798,424],{"class":496},[472,800,315],{"class":489},[472,802,642],{"class":496},[472,804,805],{"class":478}," \u002F\u002F send to yourself for quick test\n",[472,807,809,812,814,816,819,821],{"class":474,"line":808},20,[472,810,811],{"class":590},"    subject",[472,813,603],{"class":496},[472,815,497],{"class":496},[472,817,818],{"class":500},"booki-api: SMTP test",[472,820,504],{"class":496},[472,822,613],{"class":496},[472,824,826,829,831,833,836,838],{"class":474,"line":825},21,[472,827,828],{"class":590},"    text",[472,830,603],{"class":496},[472,832,497],{"class":496},[472,834,835],{"class":500},"This is a test email sent from booki-api using an App Password.",[472,837,504],{"class":496},[472,839,613],{"class":496},[472,841,843,845,847],{"class":474,"line":842},22,[472,844,709],{"class":496},[472,846,712],{"class":590},[472,848,507],{"class":496},[472,850,852],{"class":474,"line":851},23,[472,853,549],{"emptyLinePlaceholder":548},[472,855,857,860,862,865,867,869,872,874,876,878,880,883,885],{"class":474,"line":856},24,[472,858,859],{"class":489},"  console",[472,861,424],{"class":496},[472,863,864],{"class":536},"log",[472,866,591],{"class":590},[472,868,504],{"class":496},[472,870,871],{"class":500},"Message sent:",[472,873,504],{"class":496},[472,875,642],{"class":496},[472,877,727],{"class":489},[472,879,424],{"class":496},[472,881,882],{"class":489},"messageId",[472,884,712],{"class":590},[472,886,507],{"class":496},[472,888,890],{"class":474,"line":889},25,[472,891,892],{"class":496},"}\n",[472,894,896],{"class":474,"line":895},26,[472,897,549],{"emptyLinePlaceholder":548},[472,899,901,904,906,908,911,913,915,919,921,924],{"class":474,"line":900},27,[472,902,903],{"class":536},"sendTest",[472,905,540],{"class":489},[472,907,424],{"class":496},[472,909,910],{"class":536},"catch",[472,912,591],{"class":489},[472,914,591],{"class":496},[472,916,918],{"class":917},"sHdIc","err",[472,920,712],{"class":496},[472,922,923],{"class":555}," =>",[472,925,567],{"class":496},[472,927,929,931,933,936,938,940,943,945,947,950,952],{"class":474,"line":928},28,[472,930,859],{"class":489},[472,932,424],{"class":496},[472,934,935],{"class":536},"error",[472,937,591],{"class":590},[472,939,504],{"class":496},[472,941,942],{"class":500},"Failed to send test email:",[472,944,504],{"class":496},[472,946,642],{"class":496},[472,948,949],{"class":489}," err",[472,951,712],{"class":590},[472,953,507],{"class":496},[472,955,957,960,962,965,967,970,972],{"class":474,"line":956},29,[472,958,959],{"class":489},"  process",[472,961,424],{"class":496},[472,963,964],{"class":536},"exit",[472,966,591],{"class":590},[472,968,969],{"class":624},"1",[472,971,712],{"class":590},[472,973,507],{"class":496},[472,975,977,979,981],{"class":474,"line":976},30,[472,978,774],{"class":496},[472,980,712],{"class":489},[472,982,507],{"class":496},[309,984,985],{},"Run the script:",[437,987,991],{"className":988,"code":989,"language":990,"meta":445,"style":445},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npx ts-node test-email.ts\n","bash",[313,992,993],{"__ignoreMap":445},[472,994,995,999,1002],{"class":474,"line":475},[472,996,998],{"class":997},"sBMFI","npx",[472,1000,1001],{"class":500}," ts-node",[472,1003,1004],{"class":500}," test-email.ts\n",[309,1006,1007],{},"If it succeeds you should see a Message ID in the console and an email in the recipient inbox.",[453,1009,1011],{"id":1010},"optional-using-oauth2-recommended-for-production","Optional: Using OAuth2 (recommended for production)",[309,1013,1014,1015,1020],{},"App Passwords are convenient for simple setups. For production-grade Gmail usage prefer OAuth2 with a service account or delegated OAuth flow — more secure, revocable, and auditable. Nodemailer supports OAuth2; see the ",[381,1016,1019],{"href":1017,"rel":1018},"https:\u002F\u002Fnodemailer.com\u002Fabout\u002F",[385],"Nodemailer docs"," for details.",[326,1022,1024],{"id":1023},"github-actions-ci","GitHub Actions \u002F CI",[309,1026,1027],{},"In CI or production, store credentials in repository or organization secrets — never in code.",[375,1029,1030,1033,1040],{},[334,1031,1032],{},"Go to GitHub repository → Settings → Secrets and variables → Actions → New repository secret.",[334,1034,1035,1036,316,1038,424],{},"Add ",[313,1037,315],{},[313,1039,319],{},[334,1041,1042],{},"Reference them in workflows as environment variables:",[437,1044,1048],{"className":1045,"code":1046,"language":1047,"meta":445,"style":445},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","env:\n  GMAIL_USER: ${{ secrets.GMAIL_USER }}\n  GMAIL_APP_PASSWORD: ${{ secrets.GMAIL_APP_PASSWORD }}\n","yaml",[313,1049,1050,1057,1067],{"__ignoreMap":445},[472,1051,1052,1054],{"class":474,"line":475},[472,1053,671],{"class":590},[472,1055,1056],{"class":496},":\n",[472,1058,1059,1062,1064],{"class":474,"line":482},[472,1060,1061],{"class":590},"  GMAIL_USER",[472,1063,603],{"class":496},[472,1065,1066],{"class":500}," ${{ secrets.GMAIL_USER }}\n",[472,1068,1069,1072,1074],{"class":474,"line":510},[472,1070,1071],{"class":590},"  GMAIL_APP_PASSWORD",[472,1073,603],{"class":496},[472,1075,1076],{"class":500}," ${{ secrets.GMAIL_APP_PASSWORD }}\n",[309,1078,1079],{},"Use these env vars in any CI step that needs to send email (for example integration tests that send notifications).",[326,1081,1083],{"id":1082},"troubleshooting","Troubleshooting",[309,1085,1086],{},[1087,1088,1089,1090,1093,1094,1097],"strong",{},"Authentication errors (",[313,1091,1092],{},"535",", ",[313,1095,1096],{},"Authentication unsuccessful","):",[331,1099,1100,1103,1106,1109],{},[334,1101,1102],{},"Confirm the App Password was copied exactly (no leading\u002Ftrailing spaces).",[334,1104,1105],{},"Verify 2-Step Verification is enabled on the Google account.",[334,1107,1108],{},"For Workspace accounts, check with the admin to ensure App Passwords are permitted.",[334,1110,1111],{},"Inspect recent security events in the Google account (Google may block suspicious sign-ins).",[309,1113,1114],{},[1087,1115,1116,1117,1120],{},"Connection errors (",[313,1118,1119],{},"Could not connect to SMTP host",", timeouts):",[331,1122,1123,1126],{},[334,1124,1125],{},"Ensure outbound SMTP (ports 587 or 465) is allowed from your environment (firewall, cloud provider, or corporate network may block these).",[334,1127,1128,1129,1132],{},"Try using port 465 with ",[313,1130,1131],{},"secure: true"," if 587 fails with STARTTLS issues.",[309,1134,1135,1138],{},[1087,1136,1137],{},"Lost App Password:"," Revoke it in Google Security → App passwords and generate a new one.",[326,1140,1142],{"id":1141},"security-recommendations","Security Recommendations",[331,1144,1145,1157,1160,1163],{},[334,1146,1147,1148,1150,1151,1153,1154,424],{},"Never commit ",[313,1149,368],{}," files or credentials to version control. Add ",[313,1152,368],{}," to ",[313,1155,1156],{},".gitignore",[334,1158,1159],{},"Prefer a dedicated email provider (SendGrid, Mailgun, SES) for production workloads; they offer better deliverability and analytics.",[334,1161,1162],{},"Rotate and revoke App Passwords periodically.",[334,1164,1165],{},"Store secrets in a secrets manager (GitHub Secrets, cloud provider secret manager, HashiCorp Vault).",[326,1167,1169],{"id":1168},"links","Links",[331,1171,1172,1179],{},[334,1173,1174],{},[381,1175,1178],{"href":1176,"rel":1177},"https:\u002F\u002Fsupport.google.com\u002Faccounts\u002Fanswer\u002F185833",[385],"Google: App Passwords",[334,1180,1181],{},[381,1182,1019],{"href":1017,"rel":1183},[385],[1185,1186,1187],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}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 .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}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 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 .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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":445,"searchDepth":475,"depth":482,"links":1189},[1190,1191,1192,1193,1197,1198,1199,1200],{"id":328,"depth":482,"text":329},{"id":353,"depth":482,"text":354},{"id":372,"depth":482,"text":373},{"id":427,"depth":482,"text":428,"children":1194},[1195,1196],{"id":455,"depth":510,"text":456},{"id":1010,"depth":510,"text":1011},{"id":1023,"depth":482,"text":1024},{"id":1082,"depth":482,"text":1083},{"id":1141,"depth":482,"text":1142},{"id":1168,"depth":482,"text":1169},"How to generate a Gmail App Password and configure GMAIL_USER and GMAIL_APP_PASSWORD for the booki-api email service.","md",null,{},{"icon":289},{"title":286,"description":1201},"IVy_kwSWic7TMTw9YOSfywNorn5fI2ZlTrHqAQECw4c",[1209,1211],{"title":281,"path":282,"stem":283,"description":1210,"icon":284,"children":-1},"Standard HTTP error codes, error response formats, and how to handle them.",{"title":291,"path":292,"stem":293,"description":1212,"icon":124,"children":-1},"Understanding multi-tenant architecture, data isolation, and tenant slug resolution strategies.",1777787844708]