UGC Reactions from Real Creators
Real creator library videos at $11 and images at $8. Custom production from $25/video with a $250 minimum. Commercial licensing included.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Velho Outreach Engine turns a company website into a sourced sales pitch. Give it a domain or a Finnish company name. It reads public pages, matches the legal entity, finds the operational bottleneck, writes a 45-second video, and drafts a separate email for each decision-maker. Every number in those emails points at a sentence that was actually on a page the app fetched.
The product has two screens. / is the original five-agent demo: one domain in, one video and one CEO email out. /outreach is the working tool: company search, verified research, one email per role, and a personal video page.
Built with Next.js 14 (App Router), React 18, TypeScript, and Tailwind. There is no database and no queue. One request runs the pipeline. If a model call misses its deadline, that step is replaced with labelled fallback content and the page still renders.
flowchart TD
operator["Operator on /outreach"] --> input["Domain, company name, or trade-register pick"]
input --> orch["runOutreach"]
orch --> web["Company site: homepage, about, leadership, news, careers, sitemap"]
orch --> prh["PRH / YTJ trade register"]
orch --> jobs["Public job ads"]
orch --> tavily["Web search, only when TAVILY_API_KEY is set"]
orch --> agents["Video pipeline, four agents in order"]
agents --> d["1 Discovery"]
d --> p["2 Problem"]
p --> s["3 Solution"]
s --> v["4 Video script"]
web --> extract["Fact and leader extraction"]
tavily --> extract
jobs --> extract
extract --> verify["Quote check against fetched page text"]
prh --> verify
verify --> contacts["Work email, LinkedIn, official company accounts"]
contacts --> emails["Role emails: one model call, then validation"]
v --> page["Personal page /v/domain"]
emails --> page
emails --> draft["Copy, .eml, or dry-run send"]
A run makes at most six model calls: Discovery, Problem, Solution, Video Script, one extraction call, and one email call. The fifth agent, the original CEO-only email writer, still serves /. The outreach flow does not call it.
Each model call is aborted at 6 seconds. Research sources run in parallel under their own shorter budgets. A timeout, a missing key, or a reply that fails validation becomes mock or template content, and the screen says so. The API answers 200 with a full payload rather than an error page.
All five live in lib/agents.ts. Each one has a JSON contract, a validator, and a deterministic fallback in lib/mockData.ts. The fallback is keyed by the domain, so the same company always gets the same stand-in profile. Every result is an AgentEnvelope: the data, plus whether it ran live, how long it took, and whether it timed out.
| Step | Agent | What it produces |
|---|---|---|
| 1 | Discovery | Company profile, brand colors, how the business operates |
| 2 | Problem | The bottleneck, the symptoms, and a quantified hour cost |
| 3 | Solution | A three-step fix mapped onto that bottleneck, and a headline metric |
| 4 | Video script | Four timed scenes and the cartoon beats |
| 5 | Email draft | The short CEO email used by the / demo |
Discovery reads a plain HTTP scrape of the homepage and a few high-signal paths. It does not launch a browser. Problem, Solution, and Video Script run one after another, because each one reads the previous step. On /outreach those four run beside the register lookup and the page fetch.
/outreach is driven by lib/outreach/orchestrator.ts.
Find the company. A domain starts research immediately. A name searches the Finnish trade register (PRH / YTJ, CC BY 4.0) and shows matches with business ID, city, and status. The operator picks one. The match prefers a company whose registered website is that domain. A weak name-only match is never chosen automatically.
Read public sources. The company site, the register, and public job ads. Web search is optional and stays off without TAVILY_API_KEY. Personal social networks are not scraped. LinkedIn, Facebook, Instagram, and X results from search are dropped unless the operator asked for an unverified LinkedIn suggestion.
Extract, then prove. One model call proposes leaders, goals, hiring, and "why now" lines. A fact is kept only when its quote appears in the fetched text. Rejected quotes are counted. Register facts (age, line of business) are shown and do not count as substance. With no model key, keyword rules read the same pages and are labelled as such.
Decide who gets an email.
| Evidence | What gets written |
|---|---|
| Two verified facts and a named CEO | One email per named leader. A missing COO or CFO is covered inside the CEO email. |
| One verified fact | CEO email only, marked low confidence |
| Nothing verified | No email. The research log shows what was searched |
Write the letters. One model call drafts every role. Each draft is checked against the fact set. A draft that invents a claim is replaced by a template, and the card says Template fallback. CEO, COO, and CFO share one estimate. The other roles open on the fact closest to their job: an IT hiring ad for the CTO, a sales ad for the sales lead.
Attach a video. Each email links to /v/<domain>?r=<role>&t=<token>. Opening that page counts a view for that role.
Work addresses come only from the company's own pages: a published address, or the company's published firstname.lastname@ pattern when the mail domain has MX records. If nothing was published, a pattern can be guessed and is marked guessed — unverified. Personal inboxes are not looked up. Mail servers are never probed.
The timeline is fixed in lib/cartoonPresets.ts:
| Seconds | Beat |
|---|---|
| 0–6 | Intro, the company |
| 6–24 | The bottleneck |
| 24–38 | The fix |
| 38–45 | Book a meeting |
This is the beginning of the README. Read the rest at the original. Read the full README ↗