/ docs / 02-architecture / functional-spec.md
functional-spec.md
  1  ---
  2  title: 'Functional Spec'
  3  category: 'architecture'
  4  last_verified: '2026-03-13'
  5  tags: ['functional', 'spec', 'testing', 'api', 'ai', 'llm', 'email', 'sms']
  6  status: 'historical'
  7  ---
  8  
  9  > **Historical document**: This is the original POC/MVP design spec. For the current production architecture (3-stage scoring, Claude Max orchestrator, circuit breakers, etc.), see [architecture.md](architecture.md).
 10  
 11  # The Process
 12  
 13  - search "\[a type of small business\] \[city or metropolitan region\]"
 14    - on the search engine that's least likely to complain about automation
 15      - perhaps a local business directory like Thumbtack, Angi, Yelp, Houzz (or equivalent for the relevant country)
 16    - ideally a search engine / directory that includes contact details
 17    - extract URL, plus contact details if included in SERP
 18  - visit each website on the first SERP
 19  - capture screenshots
 20    - desktop above-the-fold screenshot
 21    - desktop first-below-the-fold (next page down) screenshot
 22    - mobile above-the-fold screenshot (375x667px)
 23  - downsize screenshots
 24    - resize all screenshots down to 768x432px (this is still enough for text recognition)
 25    - intelligently crop out navbars, margins, redundant whitespace and non-content elements
 26    - convert all images to JPEG with quality 85
 27    - See [https://www.buildwithmatija.com/blog/reduce-image-sizes-ai-processing-costs](https://www.buildwithmatija.com/blog/reduce-image-sizes-ai-processing-costs)
 28  - store the following:
 29    - domain
 30    - landing page URL (probably the same, but you never know)
 31    - search keyword
 32    - desktop above-the-fold screenshot
 33    - desktop first-below-the-fold screenshot
 34    - mobile above-the-fold screenshot
 35    - a copy of the HTML DOM after pageload
 36  - call LLM with the above (except the below-the-fold image) asking for a conversion score, using the \[Conversion Scoring Prompt.md\](Conversion Scoring Prompt.md)
 37  - store the full JSON of the scoring result
 38  - if score is \<= B-, call the LLM with the \[Conversion Scoring Resubmit Prompt.md\](Conversion Scoring Resubmit Prompt.md), with:
 39    - the previous scoring result (the full JSON)
 40    - the first-below-the-fold screenshot
 41  - store the full JSON of the scoring result (overwrite previous)
 42    - except move the new contact details section to it's own field in the table
 43  - after all websites on the SERP for a keyword have been processed:
 44    - search the stored data to identify the highest scoring site for that keyword
 45    - record this as the primary competitor for each low-scoring site
 46  - for any low-scoring sites:
 47    - call LLM to generate 3 variants of the proposal text, along these lines:  
 48      Hi \[firstname, if found, or "Hi there" / "Hi, my name is \[sender name\]" if not\].  
 49      I was looking at your website earlier and noticed your homepage offer score is about \[score\] (don’t worry — most local businesses are around that range, although \[primary competitor, with URL\] scored \[score\]).  
 50      Nothing is “broken,” but you’re likely missing out on at least 20–40% more leads just from the way your offer and call-to-action are structured above the fold.  
 51      I do Local Offer Fix Audits that break this down and show exactly what to change for fast lead increases.  
 52      It’s a simple one-time service — takes me about 24 hours — and the audit is yours to keep.  
 53      Want me to send over what I found?  
 54      \[sender name\]
 55    - it should also generate 3 variants of the subject line
 56    - include in the prompt:
 57      - name and URL of the primary competitor
 58      - conversion score for site and competitor
 59      - samples of proposals (and their subject lines) that resulted in sales
 60        - up to 5 sample pairs for this keyword
 61        - up to 5 sample pairs for this country
 62    - Send each version of the proposal via a different contact method gathered above, in the following order of preference:
 63      - SMS if we have their mobile number, via API
 64        - we need an SMS API gateway that operates in multiple countries
 65      - contact form
 66        - in-browser automation (not headless)
 67        - best-guess prefill, pausing until operator submits form
 68        - sender name, email, phone, and company will need to be stored globally for this
 69      - WhatsApp
 70        - This can't use the Whatsapp Business API because it doesn't work for cold messaging. It requires customer opt-in.
 71        - There is a black-hat alternative using browser automation but it is more effort than it's worth
 72        - If we're planning to WhatsApp them, we've got their mobile number so it's probably easier to just SMS them.
 73        - So for now, we ignore WhatsApp for cold outreach
 74      - cold email
 75        - use a cold-email service API
 76        - hybrid plaintext with html just for images/buttons
 77        - use base64 encoding for images
 78        - unsubscribe link
 79        - maybe just avoid EU countries (GDPR requires opt-in)
 80        - include a tracking pixel (for analytics, not open rate)
 81        - append a globally-stored signature block to emails
 82      - LinkedIn (in-browser automation)
 83      - Facebook (in-browser automation)
 84      - Instagram (in-browser automation)
 85      - If we don't have at least 2 primary contact methods (email, contact form, phone), then open their homepage in a new tab, with a floating message telling the operator we need help to find their contact methods, and floating buttons to copy the text for each field
 86    - Store each outreach, including:
 87      - timestamp
 88      - proposal text
 89      - email subject line
 90      - contact method (one of: SMS, Email, Contact Form, Whatsapp, LinkedIn, Facebook, Instagram - normalised in this way for later use as an analytics dimension)
 91      - contact URI (email address, tel: phone number or URL of the form)
 92      - (there will also be a boolean field to later mark that this converted into a sale)
 93    - Consider bot detection issues around browser automation
 94      - Hence the benefit of using a real browser
 95      - Leave room for human intervention
 96        - Pause the script for human review/intervention
 97          - Add floating buttons to copy the text for each form field
 98        - Meanwhile, continue the next step in a new browser tab
 99          - Stop adding new tabs when the browser starts unloading old tabs
100      - Randomise timings and mouse movement
101      - Consider realistic mouse clicks not just calling btn.click() events
102      - I am familiar with developing in client-side vanilla JS / jQuery, so Tampermonkey might make sense here, but I'd like to be able to use VSCode's Cline to automatically test and fix errors as they appear in the browser console
103  - Provide a method for the operator to review and update the stored data
104    - Initially, just so they can flag which outreach turned into a sale
105  - Later Versions Will Add:
106    - Handle incoming responses from prospects
107      - Store against outreaches
108        - First map the email sender's domain or phone number
109        - If no match, ask LLM to extract the domain from response their message
110        - If still not sure, ask them
111        - _(also store timestamp of each response, to later identify best time of day)_
112      - Sample initial response:  
113        Awesome — here’s the deal:  
114        I offer a _Local Offer Fix Audit_ that breaks down everything:  
115        ✔ Your current “above the fold” offer  
116        ✔ Why it's not converting  
117        ✔ A competitor comparison  
118        ✔ A completely rebuilt offer (written for you)  
119        ✔ A new CTA  
120        ✔ A 72-hour fix plan  
121        It’s a _one-time $300_ service, and you get the full PDF to keep.  
122        If you want, I can start your audit today.  
123        Want the link to get started?
124      - Objection handling
125    - Feedback Loop
126      - _(To track what works, for continual improvement)_
127      - Add tracking to the payment link
128      - Analytics pixel to confirm read
129      - URL shortener with tracking
130      - _(Record timestamp for each, to identify when they check their messages)_
131    - Split Testing
132      - Add above-the-fold screenshot in first email?
133        - As well as their best competitor's screenshot?
134      - Call out specific fails in the first email?
135        - Add a cropped screenshot of the failing component?
136      - Send at different times of day (only works once fully automated)