{
  "video_id": "8FBGb0NS_zc",
  "channel_slug": "techwithtim",
  "channel_handle": "Tech With Tim",
  "title": "Build an AI Email Assistant with Code | Full AI Tutorial",
  "duration_seconds": 5336,
  "url": "https://www.youtube.com/watch?v=8FBGb0NS_zc",
  "upload_date": "20260422",
  "transcript": "In this video, I'll show you how to build\nan AI assistant that you can trigger via email.\nThis means you can send an email\nto a particular email address and it can reply.\nYou also can have it\nautomatically send emails to people, send\nwelcome emails, send emails at certain times.\nA lot of cool stuff that I'm going to show you\nhow to set up here in this video.\nSo what I want to do\nis give you a quick demo of the finished product.\nThen we'll start going through the full tutorial\nand we'll build it out step by step.\nSo by the end of the video\nyou have a fully functioning AI email assistant.\nOkay. So let's have a quick look at the demo.\nI just have an email address here.\nThis comes from postmark because that's the service\nwe're going to be using in this video\nto handle all of the emails sending, replying, etc..\nSo we have this email address.\nAnd what I can do is say something\nlike tell me the meaning of life.\nTell me the meaning of life and happiness. Right?\nAnd then let's see what it shows us here.\nAnd I'm going to go ahead and press on. Send.\nNow you can see right away, because this was\nthe first email that I sent to the email assistant.\nIt actually sends me a welcome email\nand it says thanks for trying email\nassistant reply to this email anytime.\nWe're happy to help.\nSo I just set it up so you get this welcome email.\nYou could change this to be anything that you want.\nAnd then in one second,\nonce the AI comes up with the response,\nit will give us another email with the answer okay.\nAnd you can see here\nthat I just received the inbound email notice\nthat's coming from me,\nbecause that's the address that I set up.\nSo it's my own email that is sending it to me from.\nAnd then it says the meaning of life is blah\nblah blah and gives me the answer.\nAnd what we're also going to do for this\nproject is just build this cool dashboard\nwhere it keeps track of all of the contacts,\nwhat time they sent an email,\nso we can track all of those\nand maybe add them to something like an email list.\nAnd then you can see that we have the activity here\nwhere it has\nall of the information\nof all of the messages that have been sent.\nAnd then if we want, we could actually send\na broadcast email to all of the contacts on our list.\nSo once we gather all of these contacts\nthat are chatting with the assistant,\nmaybe we want to send them something,\ngive them a discount code, say hi whatever.\nWe can do that from this application.\nThis is super cool.\nYou're going to learn a lot.\nWe're going to be using Next.js postmark as you saw.\nMassive\nshout out to them for sponsoring today's video.\nAnd then a few other things like the anthropic API\nto generate the AI response.\nLet's get into it.\nAll right.\nSo we're going to go ahead and get started here.\nNow I've just opened up cursor.\nThis is the code editor\nI'm going to use for this video.\nBut you can use pretty much anything that you want.\nAnd what I'm going to do is just make a new project.\nSo I'm going to go file open folder.\nI'm just going to go to desktop.\nAnd I'm just going to go email\nI don't know tutorial or something.\nOkay.\nMake a new folder and then open that up.\nNow again you can use any editor you'd like.\nJust make sure you make a new folder.\nAnd we're actually not going to use any\nAI features here.\nSo I'll just close that tab\nand then we can start coding.\nNow we need to get everything set up here.\nSo what I'm going to do is just open up\nthe terminal here inside of VS code.\nAnd at this point I will mention that this tutorial\nis not designed for complete beginners.\nWell, I will go through everything line by line.\nIt is suggested that you understand\nJavaScript, TypeScript\nand a little bit of Next.js because that's going\nto make it a lot easier to follow along.\nIf you don't. Don't worry,\nyou can still follow through.\nBut obviously I'm not going to explain what\na for loop is.\nOkay, now the first thing we're going to do\nis just create a new project here.\nSo we're going to type Npx create Dash next Dash app.\nAnd then this is going to be at latest.\nAnd we're just going to call this\nI don't know maybe project or something.\nOkay.\nSo let's run that command.\nWhen we do that\nit's going to ask us to install some dependencies.\nLet's go with yes.\nAnd then let's go through\nsome of the different settings here.\nSo from here\nI'm just going to go to Customize Settings.\nI'm going to go\nyes I would like to create or use TypeScript.\nSorry I would like to use ESLint.\nI don't want to use the react compiler\nfor tailwind CSS.\nWe'll go with yes.\nWhat do we like to put our code\ninside of the c directory we're going to go with? No.\nBut we like to use the app router.\nWe're going to go with yes.\nAnd we like to customize the import alias.\nWe're going to go with no.\nAnd then it says do you want to include an agent\nstormed to guide the coding agents.\nI mean we can go ahead and do that. That's fine.\nAnd then we'll go ahead and create the project.\nOkay.\nSo this is going to take a second here to spin up.\nOnce it's done we're going to go into this directory\nand we're going to start\nsetting up the rest of the project.\nAll right so the app has been created.\nSo what we're going to do now\nis we're going to CD into the project directory.\nFrom here.\nWhat we're going to do is install\nthe other dependencies that we need.\nNow for this project\nwe're going to be using better SQL Lite.\nSo we're going to do better.\nAnd then SQL Lite like so.\nAnd then three we're\nalso going to use the drizzle ORM.\nThis is something that is going to be a database\nwrapper for us effectively.\nSo we don't need to write raw SQL code.\nVery popular.\nLet's build drizzle correctly\nthough because we need to use that.\nThen we're going to install the anthropic.\nSame thing.\nWe got to spell that correctly.\nDash I slash SDK\nand we are going to install the drizzle dash\nkit as well as ad types slash.\nAnd then better dash SQL Lite three\nokay so these are all the dependencies that we need.\nBetter SQLite three drizzle LRM anthropic\nSDK drizzle kit\nand then the better SQLite three types.\nSo we're going to\ninstall those inside of our project.\nAnd then we'll continue.\nAnd also I almost forgot.\nBut we're also going to need to install postmark\nbecause that's what we're going to be using\nfor all of the email related features\nfor this application.\nAnd it's worth noting the postmark works\nwith various other languages, frameworks,\nSDKs, whatever as well.\nBut in this case, obviously\nwe're just using it with Next.js.\nOkay.\nSo all of that is installed.\nI'm now going to clear\nand I'm going to go inside of my project here.\nWe're going to start setting a few things up.\nNow what we need to keep in mind is that for\nthis project we have a few different components.\nWe're going to have that dashboard.\nThat's where we're going to be able\nto view all of the different contacts,\nsee the history of all of the emails.\nWe're going to have the email handling.\nThe way that we're going to do the email handling\nis we're going to connect to a post\nmark inbound message stream,\nwhich we'll talk about in a second,\nand we're going to have a webhook on our server,\nour next JS backend\nthat's going to be able\nto receive any of the incoming emails, and then send\na request to postmark to send an email response back.\nWe're also going to be using anthropic,\nwhich means we'll need an anthropic\nAPI key, which again we'll look at in one second.\nAnd then I think the last step is we're going to have\na simple database, which is going to keep\ntrack of all of the contacts and whether or not\nwe've sent a welcome message to them,\nbecause when someone joins or it's their first time\nsending a message\nto this assistant, we just want to send them\nthat kind of welcome email message.\nThe point here is to give you a solid base.\nAnd then from there you can extend this\nand make it really anything that you want.\nOnce you have all of the pieces of the puzzle\nand kind of the tools that you would need\nfor a cool email assistant project.\nNow what I'm going to do at this stage\nis just start setting up some of the different config\nand the environment variables that we need,\nand then what we'll do is work\nto actually build the application code.\nNow keep in mind that all of the code I write in\nthis video will be available\nfrom the link in the description.\nSo if you're getting lost or you just want to copy\nanything, don't worry, you can just\nlook at the GitHub, which I'll leave available down\nbelow completely for free.\nOkay, so first things first.\nWe're going to go into our package dot Json file.\nNow because we're using drizzle\nwhich is a database ORM.\nWhat we're going to do\nis just update the script section here.\nLet me just make this a little bit smaller\nso we can read some more code so that before\nwe start running the project,\nwe automatically update our database schema.\nThe way the drizzle works\nis that we can define a database schema,\nbut we need to kind of run\na manual update to update the database.\nIf we make any changes to it.\nSo rather than us having to do that\nmanually, we're just going to have it done\nautomatically for us in this script section.\nSo we're going to make a new script down here\ninside of scripts in package.json.\nAnd this is going to be DB colon push.\nOkay.\nNow from here\nwhat we're going to do is just say drizzle\ndash kit and then push okay.\nSo that's perfect.\nThen what we're going to do is for our dev\ncommand we're going to type npm run.\nAnd then this is going to be db colon\npush and end next dev.\nAnd we're just going to do the same thing here\nfor the build command.\nSo that automatically we just run this push command\nwhich is going to update the database schema okay.\nSo we're going to have npm run db push and\nand next build okay.\nThat's it.\nSo that's all we need to adjust for the scripts.\nNow we're going to make a new file\ninside of the root of our project.\nAnd this file is going to be called\ndrizzle dot config dot.\nAnd then this is I believe tz okay.\nNow inside of this file\nwe're just going to put a very basic config\nto define the location of our database.\nSo we're going to say import.\nAnd then this is going to be define config okay.\nYou can see the\nAI is giving me the response here from a drizzle kit.\nAnd then what we can do is export default\ndefine config.\nWe're going to specify the location of our schema\nwhich we're going to change here in just one\nsecond to be slash.\nAnd this is going to be lib and then slash debates\nwhich is a file that we'll create in one second.\nWe actually don't need this out file.\nSo we'll just remove that okay.\nThen where it says driver we're actually just going\nto change this to say dialect okay.\nAnd for the DB credentials\nwe're going to change this from DB SQL Lite\nto be emails dot db\nbecause that's the file that we want okay.\nThen we're going to take the drizzle dot config.\nWe're just going to make sure\nit's inside of the projects.\nI had it in the wrong directory there.\nAnd then we're good to go.\nAnd we've defined what we need for the database okay.\nNow last thing.\nWe're going to make another new file in here.\nAnd we're gonna call this dot env.\nAnd we're going to start putting in some environment\nvariables that we're going to need for this project.\nSo the first environment variable we're going to need\nis the postmark server token okay.\nSo server underscore token like so we also are going\nto need the anthropic underscore API underscore key.\nThat's for handling all of the AI operations.\nWe're going to need the sender email\nwhich we'll have a look at a second.\nAnd then we're going to need the postmark underscore\nwelcome underscore template underscore alias.\nBecause in postmark we can actually create\nemail templates that we can send.\nThat's the welcome email that you saw.\nSo we're going to specify that here in a minute.\nNow for now we'll leave them empty.\nBut we're going to fill them in in just one second.\nAll right. So now let's start\nfilling in these environment variables.\nNow the first thing we're going to do\nis get our postmark server token.\nNow as mentioned\nI teamed up with postmark for this video.\nBut they are free to use.\nYou can create a free trial, see if you like them\nbefore you commit to anything further.\nBut they are one of the best email delivery services\nthat's very easy\nto set up from a developer perspective,\nas you're going to see in this video.\nAnd I'll walk you through all of the core features\nthat they have as we go through,\nand we start implementing them.\nBut effectively\nwithin all of the major languages and frameworks,\nas you're going to see,\nyou can very easily send emails.\nYou're not going to get blocked\nwhen you send the emails,\nthey're not going to be going to spam,\nand you're going to be able to send\nall different types of emails\nlike transactional emails, broadcast emails,\nand then even have the inbound message stream,\nwhich is what we're going to set up here, to be able\nto actually receive the emails\nand then send the message back in.\nWhat do you call it, ARP. Can I email assistant?\nSo what I'm going to do\nis leave a link to this in the description.\nYou can create a free account here.\nAnd then once you've created\nyour free account, you should be brought to a page\nthat looks something like this.\nSo you want to find servers\nin your postmarked dashboard.\nFrom here,\nwhat we're going to do is create a new server.\nNow for this server, just give it a name.\nIn this case I'm going to call it Email assistant.\nYou can give it a color code if you want.\nAnd then what we're going to do\nis just put it in the live mode for now,\nbecause we actually want to use this.\nIf you were just purely testing it,\nyou can go in the sandbox mode.\nOkay. So we're going to go with Create server.\nFrom here.\nYou're going to see that once you make the server\nyou're going to get a few different message\nstreams here.\nNow we just want to quickly\nexplain what these message streams are.\nNow the broadcast stream is\nwhen you're sending a broadcast\nor a batch of emails to a ton of different people.\nSo for example, every Monday at 9:00 am,\nI want to send an email\nto my newsletter, for example.\nThen I would use a broadcast stream.\nThe transactional stream is used when you want\nto send an email based on some kind of event.\nSo for example, if someone signed in to the website\nor if someone's card got declined or\nif they're payments coming up, then you would use\nthe transactional stream and then the inbound stream.\nPretty straightforward.\nBut that's what you're going to be using when\nyou want to actually receive emails from someone.\nNow once this server is created,\nwe're going to go to API tokens here.\nAnd we're going to copy this server API token.\nMake sure you don't show this with anyone.\nWhat we're going to do then is go back to our code,\nand we're going to paste that here\nfor our postmark server token.\nNow we'll deal with the template in one second.\nBut for now we're going to stick with that.\nNow one thing to keep in mind here\nis that in order for this to actually work\neffectively for us,\nwe do need to set up a verified sender signature.\nNow, this is the email that we're going to be sending\nemails from, and that we could\nif we wanted to receive emails from as well.\nSo we're going to go to sender signatures here.\nNow from here,\nwhat we're going to do is add a domain or signature.\nNow if you're using your own custom domain\nyou may want to add the entire domain.\nAnd this means that you can actually set up to send\nwith any email address from that particular domain.\nOtherwise what you could do\nis just send with a single email address.\nLike you could use a Gmail,\nyou could use anything that you want effectively.\nSo what we're going to do for now, just because it's\nsimpler, is I'm going to go with the add sender\nsignature\nrather than adding the domain for the from email.\nI'm just going to put my email\nwhich is Tim ed tech with Tim Dot net.\nAnd then we'll just put the name\nwhich is Tim Rashica.\nAnd then same thing with the reply to email.\nYou can make it different or you can just leave it\nthe same and you can add a note if you want.\nSo you can remember what this signature is.\nThen we're going to go ahead\nand press on Create Signature.\nAnd from there what you'll see is that it\nactually sends us an email to this address.\nWhat we're going to do is go find that email\nand then just verify our email address\nthat we should be good to go.\nSo you can see that this is the email\nthat was received.\nSo I'm just going to press on confirm sender\nsignature.\nFrom here. It's\ngoing to bring me back to the postmark page.\nAnd then we're good to go.\nAnd we can now use this to send out the email.\nIf you're going to be sending tons of emails,\nit is better to add a custom domain here\nand to configure the DNS settings here\nwe don't need to do that for this tutorial,\nso I'm going to skip it for right now.\nBut again, if you're going to use this in production\nand send hundreds of thousands of emails,\nyou probably want a separate domain\nand you want to go through this process and verify\neverything, especially if you want to receive mail\nto this particular address.\nFor now,\nthis is just going to allow us to send emails.\nIf you want to receive emails\nthat I believe you need to verify this.\nOkay. So now we're going to go back to servers.\nWe can go back into our email assistant.\nAnd at this point we can just leave this page open.\nBut we're pretty much done.\nNow the next step from here is\nwe want to go in an anthropic or Claude API key.\nSo what I'm going to do is just go to the anthropic\nconsole.\nYou can just search it on Google.\nYou're looking for platform.com.\nFrom here you will need an account.\nYou will need a credit card enabled.\nThis is not going to cost you a lot of money.\nSo don't worry too much about it.\nAnd what we're going to do\nis just go and create a new API token.\nNow it looks like they've changed this page\nquite a bit, but if you go under manage\nand you go to API keys,\nyou should be able to create a new one.\nSo I'm going to go create new\nkey and I'm going to go email\nassistant like so.\nAnd then add.\nAnd you can see even\nwhen I was testing for this video it cost me $0.03.\nSo don't worry about it too much okay.\nSo now I'm going to copy the key.\nI'm going to go back I'm going to paste it here.\nObviously don't share that with anyone.\nI will delete it after the video.\nNow from our environment variable file.\nWe're also going to specify the sender\nemail that we had.\nSo my case it was Tim at Tech with Tim dot net.\nBut you can change this to be anything that you want\nobviously and whatever you verified.\nAnd then for the postmark welcome template alias\nwe can actually just put welcome for right now\nbecause that's what we're going to call the template.\nBut we'll create that template in a little bit okay.\nSo at this point we have everything\npretty much set up in our project.\nWe have the scripts.\nWe have the database config,\nwe have the environment variables.\nAnd we can start writing some code\nand start building this iteratively okay.\nSo what I like to do when I'm building\nour applications is start by stubbing them.\nWhat that means is creating\nall of the individual files that we're going to need.\nAnd then we can fill in the files one by one.\nSo we kind of know how the pieces connect\nbefore we start.\nJust coding everything out.\nSo we have this app folder. We'll keep that there.\nBut then we're going to create a few more directory.\nSo not inside of app but inside of project.\nWe're going to create a folder called components.\nThis is we're going to put a few React or Next.js\ncomponents.\nWe're also going to make another new folder.\nAnd we're going to call this lib.\nAnd then we'll have one more.\nAnd we're going to call this styles.\nSo you should have styles lib components and app.\nThese are the four main directories\nwe're going to work inside of.\nNow you also see this public directory.\nYou can leave the files here if you want.\nBut I'm just going to delete them because we don't\nactually need them for this project okay.\nNow first things first let's talk about the styles.\nNow I'm not really in the mood\nto write a massive amount of CSS, and I imagine\nyou guys probably aren't as well.\nSo what I do in all my tutorials,\nI just leave all of the completed style sheets\nat the link in the description\nin the GitHub repository, and you can just copy them\ndirectly in here, and then we'll name the components\nappropriately so they have the right class name.\nSo just links up with the styles\nthat are already written.\nFeel free to write your own styles or skip that step.\nBut what I'm going to do at this point\nis I'm just going to copy and four style sheets\ninto styles dots or into this styles folder. Sorry.\nSo we have base index tokens and UI UX.\nThey're not too large,\nbut you can see there are quite a few styles.\nSo what I would suggest doing\nis just copying these. Again.\nYou can find them from the link in the description.\nYou can just go in the styles directory in GitHub,\ndownload these four files\nand then just add them to your local code base.\nOkay, so the styles are now created\nand are successfully there.\nAnd what we're going to do next\nis we're going to start working on our lip.\nSo we're going to go inside of Lib.\nFirst we're going to make a new folder\ninside of here called prompts, which is where\nwe're going to put the prompt for the kind\nof responding to the message that the user sends\nor the system prompt.\nWe're then going to make a few files.\nSo the first file we're going to have is debugging.\nYes. This is going to be for our database.\nWe're then going to have another new file.\nThis is going to be email x\nfor all of the email related operations.\nWe're then going to have typed dots,\nwhich is going to be for all of our TypeScript types.\nAnd then inside of prompts\nwe're just going to make a new file here.\nAnd this is going to be the inbound assistant.\nSo we're going to say inbound dash okay.\nDash assistant.\nLike that's okay.\nSo that's going to handle our lib.\nWe'll start writing those in a minute.\nWe're then going to go to components.\nAnd we're going to write the components that we need.\nSo the first component is going to be\nthe broadcast form dot TSX.\nWe're then going to have another new file.\nThis is going to be the contact list okay dot TSX.\nAnd we are going to have the email.\nAnd this is going to be log dot TSX okay.\nSo broadcast form contact list and email log I think\nthose are pretty straightforward with the name.\nThen inside of our app\nwe're going to start setting up our API.\nSo we're going to create an API folder.\nWe're going to create another new folder\ncalled actions.\nAnd inside of API we're going to make a new route.\nSo not new file but a new folder called inbound.\nNow if you're unfamiliar with Next.js,\nthe way that this works is that when you have an API\nfolder, when you go to the slash API route\nand then slash whatever this folder is.\nSo in this case it's called inbound.\nIt's going to automatically route\nto run the code inside of a file called routes.\nSo inside of routes we're going to create\nwhat's actually the inbound webhook.\nSo anytime an email is sent to that email address\nthat I'm going to show you\nin a second and postmark,\nit will be forwarded to our application\nwhere our application can handle process the email\nand then send a response back okay.\nAnd then the last file we're going to need\nis going to go inside of actions.\nAnd instead of actions\nwe're going to call this the broadcast dots okay.\nThen I'm just going to delete the global dot CSS file\nbecause we don't need that.\nWe already have a defined in our styles directory.\nAll right.\nSo I'm going to close all of these for right now.\nThese are all of the files that we're going to need\nand where we're going to begin with\nis just by writing everything inside of lib.\nSo these are the core components\nthat we kind of need for our application.\nAnd then once these are defined\nwe have the database set up.\nWe can start handling the inbound webhook.\nI can show you how to receive the emails\nrespond to the emails.\nThen we can build up the user interface.\nAnd again we'll build a piece by piece.\nSo we're going to start inside of DB dot txt.\nThis is for defining our database.\nAnd we're going to start with our imports.\nSo we're going to import drizzle again.\nWe're going to spell that correctly from.\nAnd this is going to be the drizzle\nor a better SQLite three.\nWe're then going to import the database.\nYou can see that\nthe AI is doing a lot of work for me here.\nSo I'm going to say import database\nfrom better SQLite three.\nWe're then going to import the SQL SQLite table\nas well as text and integer okay.\nFrom the drizzle over SQL Lite core\nwe're then going to import.\nAnd this is going to be EC, ASC and DSC.\nSo equals ascend descends from the drizzle. Awesome.\nBecause we're going to need to be able\nto sort emails and contacts in a minute.\nAnd then we're going to import some types\nthat will actually define right now before we go\nany further\nokay. So let's actually go to type status.\nI think it's going to make sense to start with this\nbecause there isn't any dependencies.\nAnd then we can go back to the other files okay.\nSo here's going to be all of the TypeScript types\nthat we need.\nSo we're going to say export type.\nAnd this is going to be inbound payload okay.\nAnd this is going to be equal to an object.\nAnd from here\nwe're going to specify all of the details\nthat will receive whenever a new email is sent okay.\nSo whenever someone sends us an email\nthis is the data we'll have access to.\nSo we're going to have from this is obviously\nthe sender we're going to have from full.\nAnd this is going to be email string\nand then name string okay.\nSame thing.\nThen we're going to have to\nand this is going to be a string.\nWe're then going to have the subject\nthis will be a string.\nWe're going to have the text body.\nThis will be a string.\nWe're going to have the HTML body\nokay.\nBody like that. This will be a string as well.\nWe're going to have the message ID okay.\nColon. This will be a string.\nWe're going to have the headers okay.\nAnd this is going to be name string.\nAnd then value string.\nAnd this is going to be an array okay.\nSo that's going to be our inbound payload.\nNow again this is what's received\nwhen a new email is sent to us.\nNext we're going to move on\nand we're going to write the message log row.\nSo we're gonna say expert type message log Rome.\nThis is the type we're going to use\nwhen we are logging different messages\nthat were sent or received.\nSo we're going to have an ID this will be a number.\nWe're going to have a thread ID so we can keep\ntrack of the thread which is going to be a string.\nWe're going to have a role.\nSo we know whether it was a user\nokay or an assistant.\nWe're going to have a content\nwhich will be the string.\nAnd then we're going to have the createdat,\nwhich will be a note number actually.\nBut a date okay.\nSo that's going to be the message log row.\nWe're then going to have the contact row\nfor storing the contact.\nSo we're going to say expert type contact row.\nThis is going to be ID\nnumber.\nWe're then going to say email string.\nWe're going to say display name.\nAnd this is going to be string or no.\nWe're then going to have the welcome sent ad.\nAnd this is going to be the date or no.\nAnd then we're going to have the createdat\nwhich is going to be the date\nand the updated out,\nwhich will be a date as well for our context.\nAnd notice with our context we want to determine,\nokay, did we send them a welcome message or not.\nBecause if we send them a welcome message\nthen we don't need to send it to them again.\nThen we're going to have an export type,\nand this is going to be a broadcast and then contact.\nAnd this is simply going to be email string okay.\nAnd then display name\nwhich will be string or no okay.\nThe broadcast contact is\njust something that we'll use\nwhen we're sending emails for the broadcast.\nWe're almost done with this file.\nWe're going to go export type.\nLast one is going to be the broadcast.\nSo let's spell this correctly.\nBroad cast state which is equal to.\nAnd this is going to be error question\nmark colon string.\nBecause is there an error or not.\nWe're not sure. Success.\nSame thing okay with a string and then sent count\nquestion mark colon number okay.\nSo that's going to determine\nwas the broadcast successful.\nEssentially we'll use that in a minute.\nAnd then the last thing\nwe're going to have is export type.\nAnd this is going to be the assistant reply\nemail parameters.\nSo that we know what we need to send here.\nWe're going to say this is two string.\nWe're going to say subject string okay.\nWe're going to say let's go back here\ntext body string.\nAnd then we're going to say message ID\nstring okay cool.\nSo now we have all of the types that we need.\nWhat we can do is go back to DB.\nAnd we're just going to import those types\nthat we need in order to fill this in.\nSo we're going to say import.\nAnd then we're just going to say type like this.\nAnd we're going to bring in the contact row\nas well as the message log row okay.\nBecause we're going to store these in the database.\nWhen we have some certain operations again\nthen we're going to have slash types okay.\nAnd this can actually be ad lib.\nSo ad lib slash types okay.\nSo now that we have all of that\nwe can start defining the database schema.\nNow there's two main database tables that we're going\nto need one for messages and one for context.\nSo let's write those out.\nSo we're going to say export const\nmessages is going to be equal to the SQL light table.\nAnd this is going to be of messages.\nNow it looks like that hey I is doing a pretty\ngood job at actually filling these in already.\nSo let me actually just allow it to do that\nbecause it looks like it's pretty much perfect.\nWith the exception of a few small things\nthat we'll just change.\nOkay.\nSo for the message table we're going to have an ID.\nThis is going to be an integer of field\nID primary key auto increment true.\nMeaning it's\nautomatically going to increment the ID for us.\nWe're then going to have thread.\nThis will be of type text\nwhich is thread ID and not no okay.\nCorrect. We're going to have role.\nThis is of type role.\nHowever we're going to make this an enum.\nSo we're going to say enum.\nAnd then we're going to have a list.\nAnd we're going to say user colon assistant like so.\nAnd then same thing that's going to be not no.\nThen we're going to have content text content not no.\nPerfect.\nAnd then we're going to have created out this will be\nan integer Createdat mode will be timestamp.\nSame thing.\nIt will be not null.\nAnd then default.\nWe'll actually not put SQL here.\nWe're going to put a JavaScript function.\nSo we're just going to put a function\nand then go new date like so okay.\nSo I think that should be okay.\nBut I need to put this as default function\nand just put a dollar sign like that\nso that it matches the correct syntax okay.\nSo default function new date. We're good to go.\nAnd that is our messages table.\nNext we're going to have the context.\nSo we're going to say export const contacts.\nSame thing.\nLet's let the I do some work here.\nSo we're gonna have SQLite and then contacts.\nWe're gonna have an ID an email a display name.\nWelcome send that created out.\nAnd that update it out.\nLet's run that.\nAnd I think we are good to go again\nwith the exception of this\nwhere we just need to change the default function\nokay.\nSo let's just go through this quickly.\nID looks correct email that looks correct.\nHowever, we also want to make sure that the email\nis going to be unique.\nSo let's add that field the display name.\nWe can just go text display name.\nIt could be now.\nSo I'm going to remove this here and allow it to\npotentially be no we're going to have welcome center.\nSame thing. This will be an integer.\nHowever I'm going to remove the\nnot null and the default because it's possible\nthat we haven't sent the welcome message at all.\nSo let's make sure we get rid of that.\nAnd then for Createdat and update it out.\nThis is correct.\nIt just is\nthe same as what we had for messages. Okay.\nSo now we have our contacts and we have our messages.\nDB now what we're going to do\nis we're going to actually set up the database.\nSo we're going to say\nconst SQLite is equal to new database.\nAnd then this is going to be emails dot db.\nFrom here we're going to say SQLite dot pragma okay.\nAnd we're just going to go in quotes.\nAnd we're going to say journal underscore mode equals\nwell don't worry too much about this\nif you haven't seen it before.\nIt's just for storing records for the database.\nAnd then we're going to say export\nconst db is equal to drizzle.\nAnd we're going to say SQLite.\nAnd then for the schema\nthis is going to have messages okay.\nLike this. Let's put this correctly.\nSo why did that mess up.\nSo this needs to be another object.\nMessages and contacts.\nSo we're just specifying the different database\ntables that we want in our database schema.\nNow because we're using drizzle and drizzle kit\nthis is going to allow us\nto automatically create the database\nwhenever we run the code.\nSo if we do npm run dev\nto run the development server, then it will\nautomatically essentially see this update the schema,\npush the changes and we are good to go okay.\nSo let's keep going here\nI just want to write some other operations\nwe're going to need in this database file\njust to get them out of the way.\nAnd then we can move on\nto handling the inbound webhook.\nSo first things first\nwe're going to say export async.\nAnd then this is going to be function save message.\nNow to save a message\nwe're going to take in a thread ID\nwhich is going to be a string.\nWe're also going to take in a role\nwhich is going to be user\nor assistant okay.\nWe're then going to have content\nwhich will be of type string.\nAll right.\nFrom here we're going to say await DB dot insert.\nWe're going to insert into messages\nbecause that's the name of our table.\nAnd the values that we're going to insert\nis going to be the following.\nSo we're going to insert in.\nSo we're going to insert\nthe thread ID the role and the content.\nAnd we can remove that.\nAnd we just need to spell existing correctly\nso that we get rid of this type error.\nNext we're going to have a function\nthat gets the recent messages.\nSo we're going to say export async.\nAnd this is going to be function\nget recent message log okay.\nThis is going to have a limit\nwhich will be a number which is equal to 200.\nAnd this is going to return a promise.\nThe promise is going to be of type message log row\nwhich will be a list.\nAnd then inside of here what we're going to do\nis get all of the rows that we want to return.\nSo we're going to say const rows is equal to\nand this is going\nto be await db dot select.\nWhen we select what we're going to do here\nselect the following.\nSo we're going to say ID and this is going to be\nmessages.id we're going to say\nlet's go down here thread ID messages\ndot thread ID roll content Createdat like so.\nAll right now what we're going to add to\nthe end of this is dot from,\nbecause we need to specify the table\nthat we want to search from.\nSo we're going to go from messages.\nWe're then going to have another.\nAnd this is going to be dot order by.\nAnd we are going to order by descending.\nAnd then this is going to be messages\ndot created at okay.\nSo created at like so.\nAnd then we're going to have dot limit.\nAnd that is going to be the limit.\nNow the I has already gone ahead\nand filled in the rest for me.\nBut effectively what we're going to do is we're going\nto take the rows and we're now going to map them.\nSo we just get the values\nthat we care about in an object.\nSo we say rows that map\nand then we have id thread, id\nrole content and createdat we return that.\nAnd that's going to give us our message log row\nfor the recent message logs okay.\nSo that's going to handle that function right there.\nWe're now going to move on to the next function\nwhich is going to be to update the contacts.\nSo we're going to have a function.\nIt's going to say export async function.\nThis is going to be Upsert okay.\nSo Upsert contact from inbound.\nSo essentially anytime someone sends a message to us\nwe're going to update their contact.\nSo we're going to say email string.\nAnd we're going to say display name.\nAnd this is going to be string or no.\nThen what we're going to do is have the function\nbody.\nWe're going to say const now is equal to new date.\nSo we can track when they sent us the message.\nAnd we're just going to insert a new record\ninto the context table or update it if it exists.\nWe're going to say await DB dot insert.\nWe're going to insert into the contacts table.\nAnd for the values that we're going to insert\nit is going to be the following.\nSo let's put our object\nwe're going to insert the name\nthe display name\nlike so we're going to say welcome send.\nAnd this is going to be no okay.\nWe're then going to say create.\nIt is now and update.\nThat is now as well okay.\nThen we're going to say dot on conflict do update.\nSo what this means is hey, if this email already\nexists inside of this context database,\nrather than kind of clearing all these values\nand resetting,\nwe're going to target the same email value, right?\nOr the same email row.\nAnd then what we're going to set is the display name.\nAnd we're going to set updated out to now.\nBut we're not going to set the welcome center yet\nbecause it's likely that we already sent them\na welcome message\nif they're already inside of our database,\nand we actually don't need\nto return anything from this function.\nSo I'm going to remove that.\nAnd this is just going to upsert the contact\nfrom the event okay.\nNow let's keep going.\nAnd I'll just zoom out a little bit\nbecause I feel like it's a little bit large\nfor how much code we're going to have here.\nNext we're going to go export async function.\nAnd then this is going to be Mark welcome sent.\nWe're going to take in an email which is a string.\nAnd we're just going to mark it\nset for that particular user.\nSo we're going to say await DB.\nAnd then let's go down to the next row.\nWe're going to say dot update.\nWe're going to update the contacts\nand we're going to set the following.\nSo we're going to say set welcome centered\nwhich is just going to be the new date.\nAnd we're going to say\nupdate is the new date as well.\nAnd we're going to do that\nwhere we have the equal function\nand we're going to say the context email equals\nthis specific email.\nVery straightforward.\nThat's all we're doing okay.\nAnd then we just have two more functions.\nSo first we're going to have a function that just\nchecks whether we need to send the welcome message.\nSo we're going to say export\nasync function should send welcome.\nWe're going to take an email\nand string.\nAnd then what we're going to do is return a promise\nokay.\nOf the contact row.\nAnd this is going to be a list.\nAnd then what we're going to do is say const rows\nis equal to await DB.\nWe're going to go down again to the next line.\nWe're going to say dot select.\nAnd we're going to say dot from.\nAnd we're going to say dot\nwhere we're going to put the equal function.\nAnd same thing.\nWe're going to say context email and then email.\nWe're going to limit this to one.\nAnd then what we're going to do\nis return rows at zero because we know\nwe're going to have one value question mark dot\nwelcome center equal equal. No.\nSo effectively what we're doing here now.\nSo I realize that this is wrong.\nThis needs to be a boolean\nI was reading the wrong function.\nWe're gonna say const rows equal to await db.\nWe're going to select from the context table.\nWe're looking for this particular\nemail in the database limiting it to one.\nNow if this row doesn't exist or we didn't yet send\nthe welcome email, then this is going to be true.\nWhich means we should send it.\nIf this row does exist and we have sent the welcome\nemail, then of course it's going to be false.\nSo that's what we'll return.\nNow. The very last thing that we're going to do\nis just one function to get all of our contacts.\nSo we're going to say export\nasync function, get all contacts.\nAnd this is going to return a promise\nokay.\nNow the promise is going to return\nor resolve to a contact row.\nThis is the one that I had\nbefore that I was messing up.\nAnd what we're going to do here\nsay const rows is equal to await.\nAnd this is going to be let's go back here DB\nokay dot select okay.\nAnd then this is dot from contacts.\nAnd then same thing.\nWe're going to order by ascending contacts dot email.\nAnd then it's actually already filled in.\nWhat I want to have here for the return.\nSo rows dot map.\nWe're going to map every single row\nto all of the different values that we need.\nAnd I think actually let's just change this quickly.\nWe're going to say welcome sent add question\nmark question mark or no okay.\nSo that looks good to me.\nThat's everything that we need.\nSo we have a function to get all the contacts.\nShould we send the welcome email mark.\nThe welcome email is sent UPS\nor the contact or update if it doesn't exist.\nGet recent message log and save messages.\nSo that's all of our database operations\nthat we're going to need.\nWe don't need to touch the database file anymore.\nWe've specified the types.\nNow what we want to do is handle the inbound email.\nLet's deal with that first.\nAnd then what we can do is go over and start handling\nthe UI, the dashboard, all of that fun stuff.\nOkay. So we're going to go to our email.tz file.\nAnd we're just going to start setting up\nwhat we need to load postmark.\nSo we're going to import the server client\nas well as the template ID\nmessage from\npostmark okay.\nThen what we're going to do is say import\nand we're going to import type.\nAnd this is going to be the assistant reply\nemail parameters\nas well as the broadcast content from.\nAnd we need to make sure we do the correct import.\nSo it's going to be at analysis\nslash lib slash types okay.\nThen what we're going to do\nis we're going to define our postmark client.\nOnce we define that\nwe're then going to write the inbound webhook.\nWe'll deal with the I stuff after.\nSo we're going to say let client.\nAnd then this is going to be the server client\nor no equal.\nLet's type this correctly. Equal.\nNo okay.\nAnd let's spell server client correctly as well.\nThen we're going to have a function.\nWe're going to say export function get postmark.\nAnd what this is going to do\nis return a server client.\nAnd we're going to say if okay not client,\nthen what we want to do is say const\ntoken is equal to process dot env\ndot. And this is going to be\nthe postmark server token\nthat we defined above that we defined earlier.\nNow it's actually again filling in the rest.\nSo we're going to say if not token we're going to\nthrow a new error saying postmark token is not sent.\nOtherwise we're going to specify B clients.\nWe're going to say\nclient is equal to new server client.\nAnd then we can simply return the client.\nBut we're just going to do that down here.\nSo no matter what they get a return value okay.\nSo that's it.\nSo all this is going to do is we're just going\nto look for the postmark server token.\nOnce we have the postmark server token we're good.\nWe can just create a new server client\nfrom postmark here, which is going to\nallow us to receive emails, send emails,\nall of that fun stuff.\nOkay.\nAnd then we're just gonna do one more function\nwhile we're here and we'll come back to it later.\nSo actually we'll just say function.\nAnd this is going to be require sender.\nThis is going to return a string.\nAnd what this is going to do is say const\nfrom.\nAnd then\nsame thing I'm going to look in my process dot env.\nSo I'm going to say process\nsky and v dot sender email.\nAnd I'm going to say if there's no sender\nemail defined then we need to throw an error\nbecause we need that environment variable sent.\nAgain we'll use this in a minute.\nBut effectively we're just checking\nto make sure that we have the correct values\ninside of our environment.\nVariable file okay.\nSo that's that.\nNow we're going to go to our app\nour API and our inbound route.\nNow this is where we can kind of start handling\nthe fun stuff where we can receive the inbound email.\nSo in order to receive the inbound email\nwe essentially need to create an API endpoint.\nThis is going to be a post API endpoint\nwhere we can get all of the details\nof an email\nand kind of have it forwarded to our backend server.\nSo in order to do this, we're going to say import\nokay.\nAnd then we're going to say next response\nfrom the next server.\nWe're then also going to import anthropic.\nWe'll use this in a minute.\nSo we're going to say and throwback okay.\nFrom the anthropic AI SDK.\nWe're also going to import the save message\nthe Upsert contact from inbound\nas well as the should send\nwelcome and the mark welcome sent from the lib DB.\nAnd let's spell inbound correctly.\nAnd then we're going to import the type.\nSo import type.\nAnd this is going to be inbound payload\nfrom ad lib slash types.\nThere's a few other imports\nso we'll need in a second.\nBut for now we can start with this.\nSo we're going to go const anthropic.\nAnd we're just going to make a new anthropic\nobject here.\nThis is actually going to automatically\nfind the presence of the environment variable\nthat we have defined.\nAs long as it's called anthropic API key.\nSo just make sure you spell it\nexactly as I spelt it there so correctly.\nSpell to anthropic underscore API underscore key.\nNow that we have\nthat we're going to make our webhooks.\nSo we're going to say export async function.\nAnd then in all capitals\nwe're going to call this post.\nNow this is the way it works index.js is the type\nthat you want to have for the Http method.\nIt's just what you name the function.\nAnd it's going to go to slash\nAPI slash inbound on our server.\nSo we're going to have post.\nWe're going to say request.\nThis is going to be of type request.\nSo we're going to say const payload.\nAnd this is going to be of type.\nThe inbound payload is equal to await request Json.\nWe're then going to start defining some variables\nto grab out all of the data that we care about.\nSo we're going to say const sender\nemail is equal to payload okay.\nDot from full side with all capitals email\nwe're then going to say const\ndisplay name is equal to payload dot from full okay.\nSo payload dot from full question mark okay.\nAnd then dot capital name question\nmark and dot trims.\nJust remove any trailing or leading spaces\nand then otherwise no okay.\nThen we're going to have const\nsubject is equal to payload dot subject.\nOr we're just going to put like this no subject\nokay.\nBecause that's how it typically looks as the email.\nAnd let's fix that to be a semicolon.\nWe're then going to say const user message.\nThe most important one is going to be payload\ndot text body\nor an empty string okay.\nNow we're just going to do dot trim like that.\nAgain remove any leading or trailing spaces.\nAnd we're going to say const\nmessage ID is equal to payload dot message ID.\nAnd let's spell const correctly okay.\nNow that we have\nthat we're just going to first quickly check okay.\nDo we have a user message.\nWe're going to say if not user message\nthen we're just going to return a next response.\nAnd this is going to be dot Json.\nAnd from here we're just going to have a status\nthat says empty message skipped\nokay.\nJust so we can see, hey, if you don't\ngive us any message there's nothing to reply to. So.\nWell we're not going to do anything okay.\nNext we're going to say const conversation\nkey is equal to message ID.\nJust so we have a way\nto track the current conversation.\nNow here at this point we'll do this in a second.\nWe're going to send welcome email\nif we need to send the welcome email.\nSo we can say if await should send welcome email,\nthen we can effectively send the email.\nAnd then after we send the email\nso we can have like a try okay.\nTo, you know,\nsend email and we can await instead of here Mark\nwelcome send to the sender email.\nOtherwise we can just have a catch.\nWe can catch the error and then we can just console\ndot error whatever this is.\nSo we can do something like\nlet's go with a string\ninbound maybe in square brackets\nand we can say welcome failed okay.\nAnd then we can just put\nwhatever the error message was.\nAll right.\nSo we'll deal with that in a second.\nBut effectively we're just going to send the\nwelcome message inside of here if we should send it.\nBut we don't have that function yet.\nSo we'll write that in a minute.\nThen we're going to save the message.\nSo we're going to say await save message.\nWe're going to save it using the conversation key.\nWe're going to save it as a user type.\nAnd we're going to save it,\nthe user's message whatever that is.\nSo we have those details\nand we save that to the database.\nThen what we need to do\nis need to generate a response.\nSo we're going to generate a response from anthropic.\nWe can actually do that right now.\nAnd then we can send the response back.\nSo we're going to say const response is equal\nto await\nanthropic dot messages dot create.\nAnd to create a message\nwe need to specify the model we want to use.\nSo I'm going to go with Claude Dash sonnet dash 4-6.\nYou can go with the higher end model if you want.\nBut this is fine.\nIt's not going to cost us that much.\nI'm going to go with max tokens 1024, because I don't\nwant it to respond with too many tokens.\nI'm going to go with system, and this is going to be\nthe system prompt, which will specify in a second.\nAnd then we're going to say messages.\nAnd what we're going to do here\nis we're just going to give the most recent message.\nSo we're going to say roll.\nAnd this is going to be user.\nAnd we're going to say\nthe content is the user message okay.\nSo this is effectively a\nyou generate a response using the anthropic API.\nWe specify the model maximum number of tokens,\na system prompt which we'll write in a second.\nThis specifies how the model should respond.\nSo kind of like a base prompt\nbefore we tell it what to respond to.\nAnd then what we can do is specify multiple messages.\nIn this case I'm just putting one, which is the most\nrecent message that the user sent us.\nIf you wanted the whole conversation\nchain, you could do that.\nBut I'm just not going to add that right now.\nAnd then it will just respond to that\nand give us a response.\nSo for the reply\nwe can say const replies equal to response okay.\nLike this dot content.\nThis comes in as a list.\nSo we just take the first object.\nWe're going to say if the type of this is text,\nthen what we're going to do\nis we are going to take the response\ndot content at index zero dot text.\nOtherwise we're just going to say, sorry,\nI can't generate a response right now or something.\nOkay.\nSo if it's of type text then we're good to go.\nWe just take the text and we can reply with that.\nOtherwise we just have kind of this custom\nerror message that we'll put in.\nThen what we're going to do is same thing.\nSave the message.\nWe're going to save it to the same conversation key.\nBut this time we save it with the assistant\nand then the reply\nso that we know, okay, hey,\nthis is the new message, right?\nSo we have this conversation key.\nThis was the user's message.\nAnd then this is what we replied with.\nSo we can kind of see that chained together.\nThen of course we're going to send the email.\nSo to do you know, send email,\nwhich we'll look at in a second.\nAnd we're just going to return a next response\ndot Json where the status is okay.\nAnd that's all that we need to respond with.\nAnd also we'll just do a quick log here.\nSo we'll just say console dot log.\nAnd let's just do some backticks.\nAnd we're going to go inbound applied to.\nAnd then this is going to say the sender okay email.\nAnd then we will just put\ninbound.\nAnd then we can put like the message ID or something\njust so we can see what we're getting okay.\nAnd just because I also want to see this\nI'm going to go console.log.\nAnd then let's just print out what the reply is.\nSo same thing. Let's go Backticks.\nAnd we can say you know outbound or something okay.\nAnd we can say I replied with\nand then we're just going to put the reply\njust so we can see what the response actually was.\nAnd we we should probably put what they sent as well.\nBut I want this to be crazy long in the console.\nSo for now we just want to test\nto make sure that that works so effectively.\nNow at this point, what we have is a post end point\nwhere if we send a request to this, that includes\nall of the correct details here,\nwhat will happen is\nwe will be able to reply to that using Arc.\nNow keep in mind that this webhook is not protected.\nSo we don't have any signing secret\nor anything along those lines.\nSo anyone could just call this right now.\nAgain we can protect that using post mark, but\nI'm just not going to go through that in this video.\nSo what we need to do now is we could manually test\nthis by just sending a Post request.\nBut what I want to do is test it using postmark\nby actually sending an inbound email.\nNow in order to get that to work,\nwe need to have this endpoint\nactually running and available on the open internet.\nSo first let's run our project.\nFirst of all just make sure there's no errors\nthat everything's executing.\nThis effectively.\nWe'll just run a backend API for us right now\nbecause we don't have any front end code\nreally that we've written.\nAnd what we'll need to do\nis we'll need to use something called Ngrok,\nwhich I'm going to show you in one second\nto allow us to forward the requests\nthat get sent to a kind of public URL\nto this private, locally running server.\nBecause we're not deploying this out on the open\ninternet.\nI'll go over in a second.\nBut essentially, Ngrok is just a proxy\nthat allows you to proxy a request\nfrom a public IP to a local private\nIP on your own computer.\nSo what I'm going to do right\nnow is I'm going to type npm\nrun dev.\nThere's probably going to be some errors,\nbut let's run it and let's see if we're good to go.\nAnd it looks like we are okay.\nAnd this is running on port 3001.\nHowever it's giving me some issues because\nit's trying to import a file that doesn't exist okay.\nSo let's actually go fix that.\nWe're going to go to our layout dot.\nAnd we're just going to remove some of the stuff\nthat we don't need.\nSo actually we don't need any of the metadata\nor any of the fonts.\nSo let's get rid of all of this. Like that.\nLet's get rid of the metadata.\nAnd then for here we can get rid of these class\nnames for the HTML and just leave it like that okay.\nSo that should be good for the root layout.\nThen let's go to page\ndot TSX and it's loading a bunch of images and stuff.\nSo same thing. We can just kind of scrap all of this.\nWe'll just leave the main container and go like that\nso that we don't have any kind of unnecessary code.\nAnd if we go back here to the browser tab\nin cursor on port 3001,\nwe can see we just get an empty page,\nwhich is totally fine.\nOkay. So this is all running.\nNow what we're going to do is we're going to go\nand we're going to install Ngrok.\nNow the way you install\nNgrok is very straightforward.\nAll you have to do is just go to the anger website\nand just go to get started for free.\nYou need to sign in or create an account.\nAgain, it is completely free and then just download\nthis for your operating system.\nYou also can brew install Ngrok as you see\nwith this command, or use scoop or use docker pull.\nEither way, you simply need to install it.\nNow, once you install Ngrok, what you're going to do\nis go back to your terminal here and you're\njust going to type the command ngrok http,\nand then the port of the server that you are running.\nSo in our case it is running.\nWe can check this by going here on port 3001,\nbecause I believe I have another one\nrunning on 3000 right now.\nSo we just type Ngrok http 3001.\nWhat this means is that we're just going to forward\ntraffic to an http url to port 3001.\nSo we hit enter.\nAnd what this is going to say is\noh I already have ngrok running.\nSo I just need to close that down.\nSo I'm just on the free account.\nAnd then I can run it again and by the way,\nit might ask you to like log in or something\nusing the ngrok command.\nJust do that,\nset it up again. It's very straightforward.\nOnce you have it set up, you're good to go.\nOkay. So I just shut down my other instance of Ngrok.\nLet's run it again and you'll see that\nwhat this does here is just spin up a forwarding URL.\nSo you might need to expand the terminal\nso that you can see it.\nBut what I'm looking for is this URL right here.\nSo I'm looking for this https url\nthat ends in dot free dot app.\nAnd that goes to localhost port 3001.\nSo what we're going to do is just copy that URL.\nAnd we're now going to go over to postmark.\nWe're going to go to our default inbound stream.\nAnd we're going to start setting up\nsome of these settings here.\nSo you can see that if we go here\nit says set your server's inbound webhook URL.\nSo what we're going to do is just paste that\nURL slash API slash inbound.\nSo that's the URL route that we need to go to right.\nSlash API slash inbound. This is the ngrok URL.\nAnd one thing to keep in mind here is that\nif you shut the server down and you restart it,\nor you stop ngrok and you restart it,\nyou are going to need to get a new URL of this.\nYou will need to keep updating this if you're\ntesting it, but ideally, if you were to deploy this,\nthen the URL of the domain you have for your back end\nobviously will not change.\nSo you just put that here, you're good to go\nand everything is set.\nSo all we're going to do is just go\nsave changes here for this.\nOur webhook URL okay.\nNow what we're going to do\nis also just copy this inbound email address,\nbecause this is currently the email address\nthat we can send emails to you\nand then get a response from,\nwhat do you call it, our email system.\nSo let's copy this email address.\nLet's make sure again that our server is running\nokay.\nWe're good to go.\nAnd we're going to now go to our email.\nAnd we're just going to compose a new email.\nNow for that we're going to send it\nto the email address we copied in postmark.\nAgain this one right here.\nWe can just do something like I don't know,\nhello world okay.\nSay hello to me and then just say whatever you want.\nRight.\nBecause we're trying to test to make sure this works.\nSo let's shoot that email off.\nWe're not going to get a response right now.\nBut if we wait for a second on our server\nkind of terminal page here,\nwe should hopefully get something forwarded\nover. Okay.\nSo it just came through here.\nIt said invalid request type.\nYour credit balance is too low okay.\nSo I'm going to fix that in a second\nbecause I don't have any credit in my API account.\nBut effectively this worked.\nIt did trigger the inbound webhook to run,\nbut it just that I can't\nactually generate something because I don't have\nany credits in my anthropic account.\nSo I'm just going to go load up\nwith a few more credits, be right back.\nAll right.\nSo I just gave anthropic more money as usual.\nAnd now what we're going to do is go back to email\nand I will send another email.\nAnd let's just test if this works.\nIt does take a second to come through here.\nSo just be a little bit patient.\nAnd we should see that\nit triggers the inbound webhook here\nas soon as the email is received by postmark\nand that we can start generating the reply.\nAnd then of course\nwe'll send the email in one second.\nI just want to make sure that the inbound is working.\nOkay. So it did work.\nWe got another trigger.\nHowever, it's still telling me\nthat I'm out of credits,\nso I think it might just take a second to refresh.\nLet me just take a minute break, come right back\nand hopefully it will be working.\nOkay.\nSo unfortunately anthropic is being quite annoying\nand although I have loaded with more credits, it's\ngoing to take a minute to propagate here.\nSo rather than just sitting and doing\nnothing, let's move on\nand let's actually just code out the email responses.\nSo by the time we finish coding this,\neverything should be good to go.\nAnd we can just test it fully.\nBecause again\nI imagine for you guys this is working now\nbecause you probably have credits\nwhereas I am waiting for them to reload.\nOkay.\nSo what we're going to do now\nis we're going to go to this email file\nand we're going to start writing the code\nto actually send a response.\nNow we need to be able to send a welcome message\nas well as to send the general message.\nRight.\nSo let's start with the assistant message.\nSo I'm going to say export async function.\nAnd it's going to be send assistant reply email.\nAnd we're going to take in the arguments which\nis going to be the assistant reply email parameters.\nNow what we're going to do here is say\nconst id is equal to args dot message id.\nWe're then going to say await.\nAnd this is going to be get postmark okay.\nAnd then dot send email.\nSo this is as easy as it is\nto actually send an email with postmark.\nYou literally just write to send email.\nAnd then you put in the required fields.\nSo we're going to say from is going to be\nyou will require sender\nbecause we need to have a sender address.\nWe're going to say two.\nNotice we're starting with a capital\nis going to be asked to we're going to say\nsubject is going to be args dot subject.\nWe're going to say text body is going to be args\ndot text body.\nAnd then we're going to say headers is going to be.\nAnd we're going to include the following headers.\nNow the name header is going to be in dash reply\ndash two.\nAnd the value is going to be ID.\nAnd then we're going to have name again.\nAnd this is going to be references.\nAnd this is value\nID okay.\nSo that's all we need to do to send an email.\nNow by default\nwe should use the transactional email stream.\nYou can't actually specify\nmanually the type of stream that you want to use,\nbut by default it will go to transactional\nand then it will just send email.\nSo that's pretty much it\nfor sending the assistant reply\nnow, as well as that,\nwe can handle sending the welcome email.\nSo let's go back to postmark.\nBecause what we're going to do\nfor sending the welcome\nemail is we're actually going\nto create a email template.\nSo if you go back to your message streams here\nand you go to templates,\nwhat we can do is create a new template.\nSo a template is essentially a reusable email.\nI'm sure you guys know what a template is.\nWhat we're going to do is just press on. Welcome.\nBut you can see there's a bunch of them here.\nOr you can just create your own template\nmanually from scratch.\nAnd this is something that has some dynamic variables\nthat you can inject.\nSo you don't need to keep regenerating the same email\nover and over again.\nYou can just dynamically create them\nbased on these custom HTML templates.\nSo I'm going to go to welcome for the layout.\nI'm not going to use a layout,\nbut you could add a layout so that the templates\nare rendered within some kind of layout\nto keep your branding styling, etc.\nconsistent for your company.\nBut I'm going to go don't use a layout\nand just press on, continue\nand you're going to see that it gives me\nthis kind of like, you know, default welcome email.\nNow, I don't really like this.\nSo what I'm going to do is go to edit,\nand I'm just going to completely change this\nwith a new template\nthat I'm going to include in the GitHub repository.\nSo again,\nrather than writing a completely from scratch,\nI'll just include it in the GitHub repository\nso you guys can just copy it in.\nOkay.\nSo I've just put it here inside of Curser.\nAnd again I will include it in the GitHub repository.\nBut you can see that it's\njust a very simple with inline styled,\nkind of, you know,\nwhat do you call it HTML template here.\nAnd anytime you have a variable, you can just put it\ninside of these two sets of braces.\nSo I have a name, I have a product name.\nAnd you can include other variables\nlike support, email or really any other variables\nthat you want inside of this template.\nSo I'm just going to copy this.\nWe're going to go back to postmark.\nAnd what we're going to do is we're\njust going to replace what was there with this okay.\nSo now we have this new HTML template.\nIf we want to look and see what it looks like,\nwe can go to the preview.\nIt will just take a second to render.\nAnd you can see that this is what it looks like.\nWelcome name value.\nThanks for trying. Product value. Email us. Whatever.\nRight. There you go.\nNow in terms of the subject,\nwe'll just leave it as this.\nWelcome to product name. Name.\nBut you can change that if you want.\nAnd then go ahead and press on. Save.\nNow we're going to look for here is the alias.\nSo notice the alias is welcome with lowercase.\nWe're going to have that in our environment variable.\nSo if you remember in our env we had this postmark\nwelcome template alias called welcome.\nSo we just want to match these two up\nin case you call it something else okay.\nSo now that we have that we can go back,\nwe can go to emails dots\nand we can start setting up the code\nfor actually sending the welcome emails.\nSo what we're going to do is create a function.\nAnd this is going to be\nthe welcome template reference.\nAnd this is going to be string or number.\nAnd then what we're going to do here\nis we're going to say const alias.\nSo let's spell cons\ncorrectly is equal to process dot env dot.\nAnd this is going to be the welcome okay.\nWelcome underscore template underscore alias like so.\nAnd I believe this starts with postmark.\nSo postmark welcome template alias\nI don't know if that's exactly correct.\nBut let's just go check our env and see.\nYeah. Postmark welcome. Template alias okay.\nGood to go.\nAnd we're going to say question mark.\nAnd dot trim.\nThen what we're going to do is say if\nnot or if sorry if alias\nthen we're just going to return the alias.\nOtherwise we can just return a manual value\nof like welcome okay.\nOr we could throw an error or something if we want,\nbut for now this is fine.\nOkay then.\nNow that we have the alias,\nwhat we can do is say export\nasync function, send welcome email.\nWe're going to take in arguments of the following.\nSo we're going to say to string and display name\nis going to be string or no.\nNow from here\nwhat we're going to do is we're going to say const.\nSo let's tab in const name\nis equal to display name okay.\nQuestion mark dot trim.\nOr we're just going to put there.\nSo if that doesn't exist\nthen we'll just put there and that is enough okay.\nThen we're going to say\nconst product name is equal to.\nAnd we'll just hardcode this as email assistant.\nBut you can adjust this to be anything that you want.\nAnd then for the supports email same thing.\nWe'll just hardcode it.\nBut you could change anything you want.\nAnd I'll just go support at example.com\nfor right now.\nThen to send the email we're just going to say await.\nGet postmark okay.\nAnd to send with the template\nwe're going to say dot send email with template.\nAgain very very easy to do this.\nAnd inside of here\nwe're actually just going to put new.\nAnd then this is going to be templated message.\nRemember we imported that above from postmark okay.\nSo we're going to say new template ID message.\nLet's go back down to it for the template a message\nwe're going to go require sender\nbecause this is who the message is coming from.\nWe're going to say welcome template ref\nso that we get the reference or the alias.\nAnd then we're going to specify the template values.\nSo we're going to say name.\nWe're going to say product.\nAnd this is underscore\nname is going to be the product name.\nAnd then the support\nemail is going to be the support email.\nSo we just need to fill in the values.\nAnd then the last thing we do is specify\nthe two value which is who we want to send this to.\nAnd that's it.\nThat's how you send the welcome email.\nVery straightforward.\nSo we have the welcome template\nreference, the ability to send the welcome email\nand to send the assistant reply.\nOkay.\nSo now that we have\nthose let's go back to our routes for the inbound.\nAnd let's actually import those that we need.\nSo we're going to go to our imports.\nAnd we're going to say that we want to import\nthe send welcome email from lib email.\nWe also want to bring in the send assistant\nreply email.\nSo now we can start writing those.\nSo where we have the send welcome email\nwe can say await\nsend welcome email sender email and display name.\nAnd then once we get the reply here\nwe can send that as well.\nSo we can say await send assistant reply email\nand we can send to the sender email\nwith this object, with this text body\nand with this message ID.\nAnd let's actually just adjust the subject\nso it looks a little bit better.\nSo we're going to say subject dot starts with.\nSo if it starts with re\nthen what we're going to do\nis just go question mark subject.\nOtherwise we're going to put in Backticks re colon.\nAnd then we are going to just put the subject\nso they know that we're replying directly to them\nokay.\nSo that is pretty much it.\nSo let's just remove that to do\nit doesn't really matter.\nThe order in which we put the assistant reply.\nAnd then another thing I'm realizing is\nwe need the system prompt.\nSo let's go to our prompts to our inbound assistant.\nAnd I'm just going to copy in the prompt\nbecause I'm not going to write it out manually.\nAnd then you guys can copy it from the GitHub\nrepository.\nThis is what it looks like effectively,\nwhat I'm doing is just writing it in a way where\nwe can read it a little bit easier.\nSo everything's on a separate line.\nEffectively, what I said is your email system.\nKeep responses concise.\nEach email is handled on its own.\nThe user may include quote a text from earlier\nmail below their new text.\nIgnored quotes\nonly answer the top newly written parts.\nJust telling it.\nHey, just answering what the user just asked.\nNot, you know, the whole chicken.\nOkay,\nso now we're just going to import that so we can say\nimport the inbound assistant system prompt from this.\nAnd then what we can do is just specify the inbound\nassistant system prompt like that.\nAnd we are good to go.\nAnd that is now the new system prompt.\nNow I don't know if my credits have reloaded yet\nbut effectively the inbound should be done.\nSo let's try it again by sending another email\nand see if we can get it to work.\nOkay, so I just said, you know, say hello to me.\nLet's send it to that inbound request.\nLet's go back here\nand let's wait a second and see if we get the reply.\nOkay.\nSo now it's actually just giving me an error\nsaying that I spelt sonnet incorrectly.\nSo let's just fix that\nand then we should be good to go.\nSo let's go sonnet okay.\nAnd let's now just close this down and restart it.\nAnd let's send one more email.\nWhat I can do is just reply to this one. Hello.\nOkay. Just so that it sends back to the same address.\nAnd let's see now if this works, okay.\nAnd finally we can see that we get the inbound.\nIt says Reply detect with Tim dot net and says hello\nhow can I help you.\nAnd if we go back here to my email you can see that\nwe get hello, how can I help you today?\nNow the one thing that I didn't get\nwas the welcome email.\nSo what I'm going to do\nis I'm going to send this from another email,\nbecause I believe what happened is when we were\ntesting before, it marked that it sent us\nthe welcome email, even though it didn't send it,\nbecause I didn't have that coded in yet.\nSo which just send from another email address and see\nif we can trigger the welcome email to be received.\nOkay, so I was just looking through the code here\nand I realized that I made a mistake in\nthese should send a welcome email function,\nwhich is why the welcome emails are not being sent.\nWell, when I did the selects\nI didn't put any fields selects,\nso I need to put the following here\nwhere I say welcome sent at.\nAnd then this is going to be context welcome sent at.\nSo that's what I'm selecting here.\nSo that this condition will actually run.\nAnd that we get, you know, this particular field,\nnot all of the fields,\nwhich means this is always going to be,\nwhat is it false.\nOkay. So let's resave this here.\nLet's just restart.\nAnd now if I set another email,\nwe should hopefully get the welcome email.\nOkay.\nSo I'm sending another email saying\ncan you welcome me.\nAnd let's see if we receive it okay.\nAnd one more silly mistake here\nbecause that didn't work\nI need this to be two equal signs,\nnot three equal signs.\nAnd then hopefully this will work.\nThen let's restart it and try one more time.\nApologize for the errors.\nBut this happens when you're coding large projects.\nSo I usually just like to keep it in\nto keep it more realistic.\nNow let's go back to the email and try one last time.\nAll right.\nAnd there we go.\nWe got the welcome email finally. So it is all good.\nIt is working perfect. All right.\nSo now that all of that is functioning let's go back\nto the code and let's move on to the next steps.\nLet me just close all of this and let's continue.\nAll right.\nSo now that we finished\nthat it's time to move on to create the dashboard.\nNow what I want to do here is I want to make it\nso that we can view all of the context that we have,\nsee a history of all of the messages, and then\nbe able to send a broadcast email to all of them.\nNow, in fact, let's actually just quickly go back\nto postmark because I just want to show you this.\nIf we go to the email assistant,\nyou can see that we have the transactional stream.\nSo we have the number of emails sent,\nwe have the inbound stream.\nAnd then we'll have the broadcast stream here as well\nwhen we start using that.\nSo the transactional stream is working.\nWe can always of course view the stats here.\nIf we want to enable the tracking link tracking,\nall of that kind of stuff.\nThere's a lot of interesting features.\nWe can see the activity all of the emails\nand suppressions set up the webhooks, right?\nLike there's a bunch more stuff\nthat we can get into here.\nPoint is, you can see it all from the dashboard.\nAnd then same thing with the inbound.\nAgain, we can see these emails\nif we want to and look at the individual activity.\nBut I want to just be able to see it from our\nwhat do you call it here dashboard.\nOkay.\nSo let's go back.\nWhat we're going to do\nis we're going to go to components.\nWe're going to start writing\nthe individual components.\nAnd then we'll render them onto the screen.\nNow probably the easiest to start with\nis going to be our email log.\nSo what we're going to do\nis we're going to say import type.\nAnd this is going to be the message\nlog row from ad lib slash types.\nWe're then just going to do a quick function\nto format the time.\nSo we're going to say format time.\nThis is going to be the date.\nAnd what this is going to do\nis just return d dot to local.\nSo let's take this correctly to local string.\nWe're going to put in undefined.\nAnd then we're going to pass the date\nstyle which is going to be\nmedium.\nAnd the time style which is going to be short okay.\nWe're then going to say export function email log.\nWe're going to take in the various entries.\nThese entries are going to be of the following type.\nSo we're going to say entries are message log row.\nAnd then we're going to have our component.\nNow from here\nwe're just going to return a simple list.\nSo we're going to return UL.\nThe class name for this is going to be e dash\nlog underscore underscore list like so.\nThese are already predefined in the style sheet.\nAnd then what we're going to do is say entries\ndot map.\nAnd we're going to map the following.\nSo we're going to take in our row okay.\nAnd from here we are going to say const\ninbound equals road dot roll is equal to user.\nSo we're just going to check okay.\nIs it a user message or an assistant message.\nBecause based on that I want to\nwhat do you call it here.\nKind of color it or style it differently.\nAnd then I'm going to return from here the following.\nSo I'm going to put a set of parentheses.\nAnd we're going to put a list element.\nSo we're going to put or list\nitems. We're going to go li.\nLet's spell li correctly.\nFor the li we're going to have qi is equal to.\nAnd then this is going to be the row.id.\nAnd the\nclass name is going to be equal to e dash log\nand then underscore underscore item like so.\nThen inside of here we're going to have a div.\nThe div is going to have the class name.\nAnd it's going to be equal to e\ndash log underscore underscore meta.\nAnd then inside of the div\nwe're going to have a span.\nThe span is going to have the following.\nSo it's going to have class name equal to.\nAnd then we're going to see okay\ninbound question mark.\nSo are we inbound or not. That's the rule right.\nSo if we're inbound\nthen the class name is going to be a dash\nlog underscore underscore badge\nokay let's spell badge correctly.\nAnd then e dash log underscore underscore badge\ndash dash received like so.\nOtherwise.\nSo otherwise it is going to be E log badge.\nAnd then E log badge sent okay.\nAnd let's put this on another line.\nNow inside of the span\nwe're just going to say inbound question mark.\nIf it's inbound we're going to say received.\nOtherwise we're going to say sent okay.\nThen we're going to put the time.\nSo we're going to say time\nlike so we're going to say the\nclass name\nis equal to e dash log underscore underscore time.\nAnd then the date time is going to be wrote\ncreatedat dot to iso string.\nAnd then inside of this time here okay.\nSo let's end the time field.\nWe are going to put as a variable the format time.\nAnd then this is going to be row dot created at like.\nSo let's make sure that we leave that open\nand then that is closed okay.\nCool okay.\nThen underneath we're going to have a span.\nFor this span\nwe're going to have the class name is equal to the\nI dash log underscore underscore thread okay.\nNow the title is going to be equal\nto the row dot thread ID.\nAnd then what we're going to do\nis the following inside of this span.\nSo we're going to say inbound.\nAnd then we're just going to put a space here.\nAnd then we're going to say as a variable row dot.\nAnd this is going to be thread ID\ndot length is greater than 12.\nSo if it's greater than 12\nthen what we're going to do just to separate out\na little bit here is we're going to put a backtick,\nwe're going to put a dollar\nsign, we're going to put row dot thread ID.\nThis needs to be a variable. So let's put that here.\nAnd then this is going to be dot slice.\nAnd we're just going to slice 0 to 12 okay.\nRemove this.\nAnd we're just going to show the first 12 characters.\nAnd then put a dot dot dot.\nOtherwise we can just put the row dot thread ID\nand let's just fix the formatting a little bit\nso it's more proper okay.\nSo that's where we're going to show the thread.\nAnd then the very last thing we're going to show\ninside of here\nis just going to be the actual content.\nSo we're going to say class name\nis equal to e log underscore underscore preview.\nAnd we'll just show the row content\nI know that's a little bit complicated.\nAgain if you were having trouble\nfollowing that then just copy it\ndirectly from the GitHub in the description.\nSo that is the email log.\nThe other ones should be a little bit easier.\nSo let's go to our contact list for our contact list\nwe're going to say import type.\nAnd then this is going to be contact row\nfrom ad slash lib slash types.\nThen what we're going to do is say function format\nwelcome.\nWe're going to say send date or no\nfrom here we're going to say if not sent\nthen we're going to return not sent yet.\nOtherwise we're going to return send dot to locale.\nOkay. Let's do this properly to local string.\nSame thing undefined.\nAnd then we are going to put here\nthe date style medium and the time style short.\nOkay.\nThen we're going to say export function contact list.\nAnd from here\nwe're going to take in the contacts.\nAnd this is going to be of type.\nSo we're going to say contacts.\nSo let's put it in an object contacts.\nAnd then this is going to be contact row as a list.\nThen what we're going to do here\nis just do a simple return.\nWe're going to return a UL.\nWe're going to say class name is equal to.\nAnd this is going to be e dash contacts\nunderscore underscore item okay.\nAnd sorry this is not item. This is list.\nThen we're going to map the contacts.\nSo we're going to say contacts dot map.\nAnd for the map we're going to map a C.\nAnd what we're going to do here\nis just return a list item okay.\nSo for the\nLi we're going to say the key is equal to c.id\nwe're then going to say the class name is equal to\ne dash contacts underscore underscore item.\nThen we're going to have our div inside of here.\nFor the div\nwe're going to say class name is equal to e\ndash context underscore name.\nAnd then we're going to put the c dot\ndisplay name like so let's make sure we spell\nthe display name correctly okay.\nSo that's going to be the div.\nBut let's make sure that they have a display name.\nSo we're going to say C dot display\nname question mark this.\nOtherwise we'll just render. No okay.\nSo now continuing inside of here\nwe are going to have a div.\nThis is going to be class name equal to e\ndash contacts underscore underscore welcome.\nAnd then inside of here\nwe're going to have a span for the span.\nWe're going to say class name equals e a\ndash context underscore underscore\nwelcome dash label.\nAnd we're going to say welcome email like so.\nAnd then we're going to put a span for the span.\nWe're going to say class name is equal to e dash\ncontacts underscore underscore welcome dash value.\nAnd let's end the span.\nAnd then inside of here\nwe're going to say format welcome.\nSee welcome set at okay.\nSo again I know a little bit complicated\nbut effectively what we're doing\nis displaying all of the context and then showing\nwhether or not we have sent them the welcome email.\nSo that's what we're seeing from this component okay.\nAnd then lastly\nwe're going to go to the broadcast form.\nAnd then we're kind of in the home stretch.\nWe just need to write the logic\nfor sending the broadcast.\nSo first of all\nthis is going to be a client component.\nSo we're going to go use client.\nWe're going to import the use action\nstate from react.\nWe're then going to import the type\nwhich is going to be\nthe broadcast state broadcast like this state from.\nAnd this is at slash lib slash types.\nNow we also need to send the broadcast email\nwhich we'll do in a second.\nBut for now we're going to say const initial state\nbroadcast state is just equal to an empty object.\nWe're then going to say export function broadcast\nform.\nFor now let's just return a empty fragment.\nBecause I can't write this\nuntil we have the broadcast function that we need,\nwhich we'll do in a second. Okay.\nBut I do still want to get something on the page.\nSo let's go to layout.\nLet's just adjust this a little bit.\nSo for HTML\nwe're just going to add a class name here.\nSo we're going to say class name is equal to.\nAnd this is going to be E layout HTML.\nAnd then for body\nwe're just going to change the class name here to be\nE a dash layout dash body like so okay.\nSo that's all we need for the layout.\nNow let's just quickly import the styles.\nSo we're going to say import add\nand then slash styles\nslash index\nwhich will import all of the other sheets for us.\nNow we're going to go to page dot TSX.\nFor page dot TSX we're just going to import\nthese different components that we have.\nSo we're going to say import.\nAnd then we're going to import the broadcast form\nfrom the broadcast form component.\nWe're going to import the contact list from there\nand the email log.\nAnd then we're going to import the get all contacts\nand the get recent message log.\nOkay.\nThen what we're going to do up here\nis we're going to say export const\ndynamic is equal to force dynamic.\nAnd inside of this home components\nwe're going to start writing some state.\nSo we're going to say const.\nAnd then this is going to be entries\nand contacts is equal to await promise dot all.\nSo we can load these at the same time.\nAnd we're going to get the recent message log.\nWe're going to get up to 200.\nAnd then we're going to get all the contacts like so.\nNow we just need to render the different pages.\nSo what we're going to do is have the div\nfirst of all this div\nwe can remove all of this stuff\nand we can just call this e home.\nThen for the main container\nlet's remove all of those as well.\nAnd actually let's adjust this\nand let's just make this main okay.\nSo we're gonna have main\nand main.\nAnd then inside of here\nwe're just going to put some sections.\nSo we're going to say section\nclass name is equal to E dash section.\nAnd then what we can do is have a H2.\nSo for the H2 we could just say id\nis equal to context heading.\nWe can say class name\nis equal to e section underscore underscore title.\nAnd then we can call this context.\nAnd then we can just render the contact list\nwith the context.\nNow it's actually filling in the rest of the stuff.\nSo let's just quickly do that.\nWe're going to have the email log\nand then the broadcast form.\nThese are all sections right there.\nClass name is the E section.\nAnd then we have the broadcast email\nall of that kind of stuff.\nThe only one that I want to change\nis rather than log heading, this is going to be\nactivity heading like so okay.\nSo that should be it for the page now\nbecause our server is still running.\nIf we just open up localhost 3000 here\nit says\nawait isn't allowed inside of async functions okay.\nSo let's just go back and adjust that.\nSo I believe we just need to make this\nan async component.\nSo let's go export default async.\nAnd then if we come here we can see\nwe have the email log.\nThere is a lot\nwe have the contacts which aren't really showing.\nAnd then the broadcast.\nSo something's a little bit messed up.\nLet me quickly have a look and we'll adjust it okay.\nSo it looks like inside of our routes here\nfor the inbound we just forgot to upsert the contact.\nSo we just need to make sure that we do that.\nWe can really do that anywhere that we want.\nI mean I'll just do it down here,\nbut I'm gonna say await\nups are contact from inbound sender email\nand display name.\nSo we just have to do that\nat some point in the function.\nNow that we have that called\nwe will save the contacts.\nBefore we weren't saving them.\nSo that's why they weren't showing up.\nNow in terms of the email log, it's fine, but\nit's not looking as good as I remember it looking.\nSo what I want to do is just look and see\nif maybe I made some mistake here\nin terms of the style sheet or the names.\nOkay, so I just spelt received incorrectly.\nSo I'm just going to fix that.\nThat's a first mistake okay.\nThere we go.\nThat's what I was looking for.\nWe have sent receive sent receive sent received.\nAnd you get the idea okay. So that is the start.\nNow that we have that, let's move on\nto actually handle the broadcast form\nand sending the broadcast email.\nSo to send the broadcast email\nwe need to go to our email box.\nAnd we need to write the logic for the broadcast.\nSo what we're going to do here is we're going to say\nconst and then an all capitals broadcast.\nSo let's spell this correctly.\nBroadcast underscore stream is equal to.\nAnd then this is just going to be broadcast like.\nSo then what we're going to do is write a function.\nWe're going to say export async function\nsend broadcast okay.\nBroadcast emails like that.\nWe're going to take in our contacts,\nwhich is going to be the broadcast contacts.\nWe're going to take in our subject,\nwhich is a string okay.\nSo let's just type that correctly.\nAnd we are going to take in our body\nwhich will be a string as well.\nNow this is going to return a promise\nwhich is a number of the broadcast emails\nthat we sent.\nSo we're going to say\nconst from is equal to require sender.\nWe're going to say let sent count equals zero.\nWe're going to say four.\nAnd this is going to be can't see of contacts.\nAnd then what we're going to do\nis just send the emails.\nSo we're going to say await get postmark.\nAnd then we're going to say dot\nsend email for the email.\nThis time we're going to say from is from,\nwe're going to say two is secret email.\nWe're going to say subject\nis subject.\nWe're going to say text body is the body.\nAnd we're going to say the message\nstream is the broadcast stream.\nSo we're specifying\nwe want to use the broadcast stream,\nnot the normal stream,\nwhich is designed for broadcast emails.\nWe're then going to say send count plus plus\nso that we can keep track of that and return it.\nOkay,\nso that's all we need to do to send the broadcast.\nHowever, what we need to do now\nis we're going to go to our actions.\nSo we have actions broadcasts.\nAnd what we're going to do here is say use server.\nAnd the idea is that we'll send a request here\nto actually get the details from the database\nso that we know which context\nto send the broadcast to.\nSo we're going to say import\nand we're going to get go\nget all context from ad lib slash TV.\nWe're going to import the send broadcast emails\nand we're going to import the type.\nNow it's already filling this in,\nbut that's actually not what I want.\nI want export async function\nsend broadcast to send the broadcast.\nWe're going to take in the previous state,\nwhich is the broadcast state,\nas well as the form data,\nwhich is going to be the form data\nfor what we want to fill in with the broadcast.\nAnd this is going to return\na promise\nand this will be the broadcast state.\nOkay. So we have that there.\nAnd we're going to say\nconst subject is equal to string.\nAnd this is going to be form data dot get\nsubject okay.\nAnd then we're going to say question\nmark question mark empty string dot trim.\nWe're then going to say\nconst body is equal to string.\nSame thing.\nThis is going to be form\ndata dot get body otherwise empty and trim.\nWe're going to say if not subject or not body\nthen we're just going to return an error.\nSo we're going to say return\nerror subject and body are required.\nNow. Otherwise we're going to say\nconst rows is equal.\nTo await. Get all contacts.\nWe're going to say\nif rows dot length is equal to zero\nthen what we're going to do is just return again\nan error.\nAnd we're going to say no context found.\nOtherwise we're going to get all of the context.\nWe're going to say\nconst context is equal to rows dot map.\nWe're going to map each row to the following.\nSo we're going to say r.\nAnd then this will be parentheses.\nAnd then an object that we're going to return for\neach one of these is going to be email\nwhich is our dot email\nand display name which is our dot display name.\nWe're then going to say const send count is equal.\nTo await send broadcast emails to the context\nwith the subject and the body.\nAnd we're going to return success.\nAnd we're going to say broadcast sent\nto, you know, this number of contacts like that.\nAnd we also just return these send count okay.\nSo that is the action we're going to be using here.\nWe actually don't need the broadcast contacts.\nSo let's remove that.\nAnd now we can go to the broadcast form.\nAnd once we fill that in we should be good.\nSo for the broadcast form first of all\nwe're just going to bring in that action.\nSo we're going to say import.\nAnd then this is going to be send broadcast.\nWe're then going to go here\nand we're going to say const.\nAnd this is going to be state form action.\nAnd then rather than is pending\nlet's just call this pending\nis equal to use action state send broadcast\nwith be initial state.\nWe're then going to return the following.\nSo we're going to return a form.\nThe action is going to be equal to the form action\nlike so.\nNow the class name here\nis going to be equal to e dash\nbroadcast.\nSo let's spell that correctly.\nThen we're going to start filling in what we need.\nSo we're going to put a P and we're just going to say\nsend a broadcast email to all contacts okay.\nThat's fine.\nThen what we're going to do\nis we're going to put a label.\nThe label is going to be class\nname E broadcast label.\nAnd we will just call this subject.\nWe then are going to put an input field.\nWe're going to say input ID is equal to the broadcast\nget broadcast.\nLet's spell this correctly dash subject.\nWe're going to say that the name is subject.\nThe class is the E broadcast\ninput the placeholder subject.\nWe're going to make it required.\nAnd we're going to put the type equal\nto text like so.\nAnd let's just quickly format this okay.\nNow let's just adjust the label.\nSo it says HTML four.\nAnd that's going to be the broadcast subject okay.\nNow the next thing that we want to do\nis put in the body.\nSo actually just use the autocomplete.\nWe have e broadcast label okay.\nBroadcast body. It is body.\nWe're going to have a text area here.\nWe have ID broadcast body class name\nE broadcast text area.\nThe name is going to be the body.\nThe placeholder is going to be the body. As well.\nWe're going to make this required and we're going\nto make rows equal to six for right now okay.\nThen lastly we're just going to show the error state\nif there is any.\nSo we're going to put a variable.\nWe're going to say state dot error.\nSo if there is an error then what we can do here\nis we can put a paragraph tag for the paragraph tag.\nThe input class name is E broadcast\nmessage, E broadcast message error.\nLike so.\nFor that error we are just going to put the role\nis equal to alert.\nIf we can spell that correctly.\nAnd then we'll show the states error.\nOtherwise we can show no.\nSo let's put it okay.\nAnd let's just make this a little bit cleaner.\nOtherwise we can show no like that okay.\nNow we want to show if there is success.\nSo we're going to say state dot success\nand not state dot error question mark.\nSo if that's the case then what we can do\ndown here is we can put a paragraph tag.\nWe can say the class name is E message broadcast.\nE broadcast message success.\nThe role this time does not need to be alert.\nSo we can remove that.\nAnd we can just show the states success.\nOtherwise here we can show. No. Okay.\nSo that is for that.\nAnd then lastly we just need a button to submit.\nSo we're going to say class name is equal to E dash\nbroadcast.\nAnd this is going to be underscore underscore submit.\nThen we're going to do\nthe type of this is equal to submit.\nAnd we're going to say disabled is equal to pending.\nSo if it's loading we don't show that.\nNow if it's pending we're going to say sending\ndot dot dot.\nOtherwise we're going to say send broadcast\nfor that button okay.\nSo that is going to complete this form.\nThat's pretty much all that\nwe need in order to send the broadcast.\nAnd just the last thing I quickly want to fix\nis that if I go to my routes here,\nthe upsert contact from inbound, I believe\nwe need to actually put that a little bit earlier.\nSo yeah, needs to actually go\nbefore we check the welcome message\njust to make sure that we have the contacts.\nSo let's go here and let's do that\nright before we check the welcome message\nto make sure that we're updating the contact\nand we add it into the database okay.\nSo now that we have that let's quickly check\nand see what this looks like.\nAnd if we scroll down\nwe should see the broadcast form perfect.\nSo we can send something and send one.\nHowever in order for that to work we need a contact\nbecause we weren't saving the context before.\nI'm just going to send another email.\nWe'll save the contact\nand then we'll test the broadcast.\nOkay. We can see that a new contact was now added.\nYou can see it shows my name.\nThe welcome email was sent on this date.\nAnd if we go back here I can see that I received this\nand I should get the response in one second.\nAll right.\nWe can see that we got the response.\nI just asked it, how can I be happy?\nAnd it gave me this answer like so.\nAll right. So that's all good.\nIt's save the contact now.\nSo what I want to do is test the broadcast.\nSo I'm just going to go hello world.\nJust testing.\nLet's send the broadcast.\nAnd it says it was sent to one contact.\nSo let me check the email and see if I received it.\nAll right. And you can see that I received it.\nAnd we even have the unsubscribe link\nwhich is added here.\nObviously we can format this and make it look better,\nbut the point is the broadcast is working.\nSo after a lot of work here,\nwe have finally completed the project.\nWe've built an email AI assistant that setups\nthat anytime we send an email it sends us a response.\nWe're tracking the contacts we can send broadcast.\nWe have a full log of all of the history.\nThis is super cool\nand I hope you guys enjoyed this video.\nNow again, massive shout out to postmark\nfor making this video possible.\nThis is like one of the easiest ways\nthat I've ever used to send these types of emails.\nYou can see that you can send broadcast\ntransactional.\nYou can create other additional streams.\nYou have the inbound stream.\nAnd this is just one of the best\nemail platforms out there.\nSo definitely make sure you check it out\nand use it for projects similar to this.\nIf you guys enjoyed the video,\nmake sure leave a like.\nSubscribe to the channel\nand I will see you in the next one.",
  "transcript_chars": 96578,
  "ingested_at": "2026-05-21T19:02:37.841369+00:00",
  "source": "retry-no-transcript",
  "yt_meta": {
    "view_count": 54322,
    "like_count": 140,
    "channel_id": "UC4JX40jDee_tINbkjycV4Sg"
  }
}