{"id":29152923,"url":"https://github.com/walimorris/sendur","last_synced_at":"2026-05-05T09:31:36.362Z","repository":{"id":298231073,"uuid":"999279184","full_name":"walimorris/sendur","owner":"walimorris","description":"Sendur is an AI-powered lead generation and outreach automation platform that combines n8n workflows and human-in-the-loop review to streamline prospecting, email generation, and follow-up.","archived":false,"fork":false,"pushed_at":"2025-08-06T04:14:25.000Z","size":14006,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-08-06T06:14:23.551Z","etag":null,"topics":["automation","generative-ai","java","mongodb","mongodb-atlas","n8n","n8n-workflow","openai-api","sendgrid","serp-api","spring-ai","spring-ai-openai","springboot"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/walimorris.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-10T02:46:06.000Z","updated_at":"2025-08-06T04:14:28.000Z","dependencies_parsed_at":"2025-07-09T04:23:32.712Z","dependency_job_id":"03efdb5a-ccc2-4744-a8ff-39d0cad0e670","html_url":"https://github.com/walimorris/sendur","commit_stats":null,"previous_names":["walimorris/sendur"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/walimorris/sendur","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walimorris%2Fsendur","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walimorris%2Fsendur/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walimorris%2Fsendur/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walimorris%2Fsendur/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/walimorris","download_url":"https://codeload.github.com/walimorris/sendur/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/walimorris%2Fsendur/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32643518,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-04T10:08:07.713Z","status":"online","status_checked_at":"2026-05-05T02:00:06.033Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","generative-ai","java","mongodb","mongodb-atlas","n8n","n8n-workflow","openai-api","sendgrid","serp-api","spring-ai","spring-ai-openai","springboot"],"created_at":"2025-07-01T01:03:32.810Z","updated_at":"2026-05-05T09:31:36.319Z","avatar_url":"https://github.com/walimorris.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- PROJECT LOGO --\u003e\n\u003cbr /\u003e\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://bitbucket.org/intelligence-opensent/opensentop/src/master/\"\u003e\n    \u003cimg src=\"/images/sendur_worm.png\" alt=\"Logo\" width=\"400\" height=\"300\"\u003e\n  \u003c/a\u003e\n\n\u003ch3 align=\"center\"\u003eSender\u003c/h3\u003e\n\n  \u003cp align=\"center\"\u003e\n    Automate business lead generation and outreach with Java and GenAI\n    \u003cbr /\u003e\n    \u003ca href=\"#\"\u003eView Demo\u003c/a\u003e\n    ·\n    \u003ca href=\"#\"\u003eReport Bug\u003c/a\u003e\n    ·\n    \u003ca href=\"#\"\u003eRequest Feature\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\nSendur is an AI-augmented lead generation automation platform that leverages n8n workflows and AI agents to streamline the process of identifying, reviewing, and engaging potential business leads.\n\n## How It Works\n\n### Automated Lead Discovery\nSendur uses \u003ca href=\"https://n8n.io/\"\u003en8n\u003c/a\u003e, an AI Agent, and SerpAPI to search the web, on an automated schedule, for businesses based on a given \ndescription or industry profile. We've all heard of prompting... yes, you need to build a prompt to share with your AI \nAgent. Be warned, AI Agents can, and do, get things wrong so it's best that you are explicit in your details. In this \nsetup, we ask our Agent to look up the business details of small businesses around the areas above North Seattle. \nWe want our agent to send these leads back in a nicely built JSON structure (we can use this for persistence). The Agent\nuses \u003ca href=\"https://openai.com/\"\u003eOpenAI\u003c/a\u003e and SerpAPI as a tool. Tools are nice-to-haves that are available for your AI Agent to use, however your\nAgent must have access to utilize these tools (API keys or other information you can let your Agent know about). If\nyou haven't, check out what \u003ca href=\"https://serpapi.com/\"\u003eSerpAPI\u003c/a\u003e is. You see the node called `Structure the Leads`, well \nn8n allows you to manipulate any data output you receive. In this case our Agent returns some JSON, hopefully a list of \nleads. This Code node allows us to conduct any other parsing, validation or structure manipulation on this output data. \nHonestly, we just want to ensure the AI Agent is giving us some useful data, in the right structure we can use in our \nSpringboot application. Which leads us to the last `Receive Scheduled Leads` node. This output is sent via a `POST` request to\nSendur (`/sendur/api/leads/receive-scheduled-leads`). You might be asking how we remove data duplication, I mean we don't \nwant the same leads showing up all the time. You could use a `Memory` node on the AI Agent, however this is generally used\nfor chat history context, and unfortunately we're not building a chatbot. So, we filter. Going back,\nyou see the first `Get All leads` node? Here's where we send a `GET` request to Sendur (`/sendur/api/leads/find-all`) \nto receive all our leads. Next, in the `Blocked Leads` node, we parse all the business names, and we pass this to our AI Agent.\nWe tell our agent, \"hey, you see this list of business names? If you run up on them while you're searching the web, \nignore them and don't add them to the final output.\" I'm sure you can come up with a better prompt...\nLastly, as a final filter we do something similar in the Sendur application code to ensure we're not persisting \nduplicates.\n\n\u003ca href=\"https://n8n.io/integrations/agent/\"\u003e\n  \u003cimg src=\"images/scheduled_lead_generator_agent_v1.png\" alt=\"Logo\" width=\"800\" height=\"350\"\u003e\n\u003c/a\u003e\n\n### Lead \u0026 Email Generation\nOnce discovered, leads are enriched with relevant details and paired with AI-generated email drafts. Maybe you will \ngenerate some nice cash flow with automation, or maybe you have deep pockets. Either way, be aware: your AI Agent \nuses API keys for tools like OpenAI and SerpAPI and this can become costly! However, maybe you only run these schedules\nonce a day, or maybe you change this to be manually triggered. Just run some calculations before you set this loose. Many \ntools have free tiers so check those out too, and be creative!\n\n\u003ca href=\"https://n8n.io/integrations/agent/\"\u003e\n  \u003cimg src=\"images/n8n_ai_agent_prompt.png\" alt=\"Logo\" width=\"800\" height=\"350\"\u003e\n\u003c/a\u003e\n\n### Automating Data Record Updates\nWe discussed how AI Agents can get things wrong and create random false data (\u003ca href=\"https://cloud.google.com/discover/what-are-ai-hallucinations\"\u003ehallucinate\u003c/a\u003e)\nand/or incomplete data! Well, incorrect data is definitely a possibility when we use AI to automate actions. Part of the goal\nis to receive correct, complete, and valuable business leads. So, we use n8n nodes like the `Code` node for validation and \nformatting, but what happens when our data is correct but unknown? Our AI Agent will still produce something. What we did\nwas protect our data output by explicitly telling our agent what to put when it can not find what it searches for: `\"email\": \"Not available\"`.\nWhat if our search just wasn't deep enough, but we really want to produce as many business leads as possible? One option is to run \nanother, deeper and more intentional, search. I don't know about you, but I don't want to do that work. We can, again, use the services of n8n and our\nAI Agent. Here's another workflow, except this time we pull leads without emails from our datastore by calling our springboot API \n`/sendur/api/leads/no-email-scheduler`, we validate our data with the `Code` node, and send to our Agent. But we only want leads whose \nemail has been found. Let's use another `Code` node to truncate the JSON returned from our Agent. Lastly, we can send these updated leads\nback to our springboot api endpoint `/sendur/api/leads/update-emails` to update our datastore. Next, we'll talk about how we send off the \ngenerated emails with a click of a button. \n\n\u003ca href=\"https://n8n.io/integrations/agent/\"\u003e\n  \u003cimg src=\"images/scheduled_lead_update_agent.png\" alt=\"Logo\" width=\"800\" height=\"350\"\u003e\n\u003c/a\u003e\n\n### Human-in-the-Loop Review\nYou log into the Sendur web application to review and approve both the leads and their associated emails.\n\n\u003ca href=\"https://mui.com/material-ui/\"\u003e\n  \u003cimg src=\"images/sendur_landing_page.png\" alt=\"Logo\" width=\"800\" height=\"350\"\u003e\n\u003c/a\u003e\n\n### Email Dispatch \u0026 Webhook Integration\nApproved emails are sent via an n8n webhook trigger, initiating the outbound communication process.\n\n\u003ca href=\"https://n8n.io/integrations/agent/\"\u003e\n  \u003cimg src=\"images/send_approved_emails.png\" alt=\"Logo\" width=\"800\" height=\"350\"\u003e\n\u003c/a\u003e\n\n### Lead Persistence \u0026 Workflow Expansion\nAll lead data is stored and updated in the system. As the project grows, additional automations and workflows (e.g., follow-ups, CRM sync, analytics) can be layered on top.\n\n### Deploying n8n on AWS EC2\n1. Spin up an EC2 instance (t2.micro is free 🤑)\n- Chose Ubuntu 22.04 or Amazon Linux 2\n- Ensure to configure security groups to allow traffic on port 5678 (n8n's default port)\n- Keep in mind: server(s) running the application will need inbound permission, and you may want to access n8n locally\n- Create and download the SSH key pair to access your instance\n2. Install nodejs on EC2\n- ssh into your instance\n- Linux 2: (`curl -sL https://rpm.nodesource.com/setup_24.x | sudo bash -`) then run (`sudo yum install -y nodejs`)\n- Ubuntu : (`sudo apt update \u0026\u0026 sudo apt install -y nodejs npm`)\n3. Install and run n8n\n- Install: `sudo npm install -g n8n`\n- To test your setup (which is currently insecure: no TLS/HTTPS) for development you should disable secure cookies for the meantime\n- Later we will add a load balancer to make this publicly accessible for (yourself, your team, your org)\n- In Terminal : `export N8N_SECURE_COOKIE=false`\n- Run         : `n8n`\n4. Accessing n8n\n- Browser     : ec2-server-ip:5678\n- If you're slowly migrating from local to AWS be sure to update your application's configuration settings\n- You'll also want to ensure your n8n Request Nodes are updated to point to your application server or local ip\n5. Security Tips\n- Restrict inbound traffic to n8n server using tight security groups. In dev mode this should be your IP address\n- Later, we'll enable AWS WAF (Web Application Firewall) and tight log accounting\n- n8n has useful AWS Nodes like the \u003ca href=\"https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.awslambda/\"\u003eLambda Node\u003c/a\u003e - we need to use IAM roles with least privilege for AWS integrations\n- Our EC2 dev instance won't run up a charge (if you used t2.micro 🤑) but shut n8n down when not in use\n- When we first log in we create server credentials. Please don't use admin/admin or admin/password.\n\n### Building and Running the Project\n\n#### 1. Build the Project\nNote: Ensure application.properties file is populated!\nTo build the backend and frontend, run the following commands:\n``` bash\nmvn clean install\n```\n```bash\nnpm run build\n```\n\n#### 2. Running the Project\nAfter building, you can run the backend Spring Boot application using:\n```bash\nmvn spring-boot:run\n```\n\nThe frontend can be served running:\n```bash\nnpm run watch\n```\n\n#### 3. Testing the Project\nOnce the project is running, open up a browser and navigate to `localhost:8082` and you should see text saying `All is Good!`.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwalimorris%2Fsendur","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwalimorris%2Fsendur","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwalimorris%2Fsendur/lists"}