Use when someone asks to run or manage AI-led outreach, prospect larger Swedish companies, prepare a daily outreach batch, or update the partner outreach process shared with Sebastian and Stina. Triggas också av "dagens 20", "kör outreach", "kör dagens outreach", "morgonbatch", "dra igång dagens utskick" eller /outreach-daily.
Resources
7Install
npx skillscat add ecomconsulting/outreach-daily Install via the SkillsCat registry.
AI-led outreach — granskningsbar batch utan förarbete för partnerteamet
Delad process för Mikael, Stina och Sebastian. AI kör kedjan i Mikaels
workspace och tar fram en granskningsbar batch. Mikael godkänner den faktiska
batchen före utskick. Sebastian och Stina kopplas inte in förrän ett prospekt
har svarat positivt. Allt bygger på VERKLIG mätdata.
Bindande ansvarsfördelning: läs references/operating-model.md före varje
körning. Den filen styr vem som gör vad och ersätter den äldre modellen med tre
dagliga operatörer.
Sluttillstånd: godkänd konfig → hela kedjan Apollo-pull → benchmark → copy →
review-sheet körs till granskningsbar sheet i ETT svar, med [steg X/Y]-statusrader.
Mikael ska inte behöva säga "fortsätt" mellan stegen — nästa beslutspunkt är
"skicka" på den färdiga sheeten. (Undantag: filtergodkännande vid ny eller
ändrad konfiguration samt stoppen i steg 1/2 vid credit-brist eller struken gate
gäller fortfarande.)
Steg 0 — hämta senaste versionen (varje körning, före allt annat)
Copy-kanon, operating model och mätpunkter ändras löpande. Kör därför alltid
detta först, från skill-mappen:
git pull --ff-onlyKör pullen bara om skill-mappen själv är en klon av det delade repot:
git -C <skill-mappen> remote get-url origin # ska ge github.com/EcomConsulting/outreach-dailyGer det något annat, eller fel, är mappen Mikaels arbetskopia i ~/HQ (den
ÄR källan och ligger i ett repo utan remote) — då hoppas steget över utan att
köra git pull. Rapportera alltid utfallet för Mikael — "redan
aktuell" eller vilka filer som ändrades — så det är känt vilken kanon dagens
batch kördes på. Ändrades references/copy-template.md, läs om den innan
copyn skrivs. CHANGELOG.md säger vad som ändrats och varför.
Din egen config.json ligger utanför repot och rörs aldrig av en pull.
Förutsättningar (kolla FÖRST, en gång per installation)
config.jsonfinns i skill-mappen. I den aktuella AI-ledda partnerprocessen
äroperatoralltidmikael, eftersom Mikael är extern avsändare och
godkännare även när AI utför arbetet.copy_segmentär obligatorisk:ecommerceför e-handelsprospekt ellertraditional-b2bför traditionella
B2B- och industribolag — saknas nyckeln stannarbuild_sheet.py(ingen tyst
default). Byt segment i config innan en batch i ny kategori körs. Fyll i
kategori och Apollo-filter för den godkända batchen.APOLLO_API_KEYfinns i miljön eller i projektets.env. Nyckeln delas av
teamet (Sebastians konto). Saknas den: stanna och be om den.python3finns (skripten kräver bara standardbiblioteket).- Google Sheets-åtkomst:
gwsCLI om installerad. Saknasgwsfaller steg 4
tillbaka till en CSV som AI lämnar för manuell import till Sheets. - För automatiskt utskick (steg 5):
sender_email+signature+send_path
ifyllda i config.json, och avsändarkontot nåbart via EN av vägarna —gwsinloggad som avsändaradressen, ELLER claude.ai:s Gmail-connector
kopplad till den. Finns ingendera stannar AI vid den godkända utskicksplanen.
Dubblettskyddet (varför ingen kan skicka till samma bolag)
- AI använder teamets gemensamma Apollo-konto. Pull-skriptet filtrerar på
prospected_by_current_team = nooch sparar varje hämtad person som kontakt
— nästa pull ser dem aldrig igen. - Prospekteringen prioriterar större bolag inom bygg, el, stål, fastigheter,
industri och närliggande branscher. Filter för storlek och bransch ska visas
vid ny eller ändrad konfiguration och godkännas innan Apollo-krediter används.
Steg för steg
1. Lista — 20 nya prospekt ur Apollo
python3 scripts/apollo_pull.py # läser config.json, default 20
python3 scripts/apollo_pull.py --n $ARGUMENTS # om skillen anropades med ett antalOutput: <output_dir>/YYYY-MM-DD-prospekt.csv — namn, titel, bolag, domän,
e-post, LinkedIn. Skriptet hämtar bara personer med upplåst e-post; varje rad
drar en Apollo-kredit. Svarar API:t "insufficient credits": stanna och
rapportera — hitta ALDRIG på adresser och fall aldrig tillbaka till rollkonton
(info@/kontakt@) utan Mikaels explicita OK per rad.
AI väljer större relevanta bolag enligt references/operating-model.md och
redovisar de faktiska Apollo-filtren före pull. Använd inte den gamla
uppdelningen där Mikael, Sebastian och Stina hade varsin fasta kategori.
2. Mät — punkt för punkt på mottagarens egen sajt
AI bygger en kluster-fil av dagens domäner ur -prospekt.csv, grupperade per
nisch. Klustret är bara en gruppering för körningen, inte en konkurrentjämförelse.
Format och filnamn:
<output_dir>/YYYY-MM-DD-kluster.json → {"nisch": ["domän.se", ...], ...}Domäner som redan i klassificeringen visar sig ligga utanför kategorin (fel
produkttyp, tom katalog) lämnas utanför klustret och räknas som strukna.
Gaten (beslut Mikael 2026-08-19): en rad är skickbar när domänen saknar
minst tre av de mätta punkterna. Färre än tre saknade punkter → raden stryks.
Det finns ingen alternativ stomme och ingen nödmall.
Konkurrenter behövs inte längre och nämns aldrig i copyn. benchmark.py räknar
fortfarande ut losses (saknad punkt som majoriteten i klustret har) som intern
kontext — den siffran får aldrig gå till ett mejl. Det som styr copyn ärmissing.
python3 scripts/benchmark.py --input <klusterfil.json> --profile <profil> --outdir <output_dir>Profiler: shopify-dtc (e-postfångst, kundomdömen på PDP, Product-schema,
prenumeration på PDP, minst tre produktbilder, produkttext över 300 tecken) ochleadgen (kontakt-/offertformulär, synliga omdömen, schema-markup, H1). Output:-benchmark-raw.csv + -benchmark-summary.json med missing och n_missing
per domän. Skriptet skriver ut hur många av dagens domäner som klarar gaten.
Mätdisciplin (hård gräns, ändras aldrig):
- Ett fynd = domänen saknar punkten. Punkt.
- Inga påhittade fynd. Varje fynd i copy ska gå att peka på i summary-filen.
- Påstå aldrig frånvaro av sådant som inte kan mätas i statisk HTML.
- Ser ett resultat orimligt ut (t.ex. stor aktör som "saknar" något):
verifiera manuellt på fler undersidor innan det används i copy. reviews-punkten får aldrig gå till copy utan kontroll i renderad DOM.
Statisk HTML skiljer inte på tre olika lägen: omdömen som faktiskt visas,
en installerad omdömesmodul som står tom ("0 recensioner", "Inga recensioner
ännu"), och rena platshållare i temat. Alla tre mäts som True eller False på
fel grunder. Kontrollprov 2026-08-19 i smyckesklustret:mulicollection.com
mättes True men visar ingenting alls i webbläsaren,susannafalken.comochcarolinesvedbom.commättes True men står på noll omdömen. Öppna produktsidan
i Playwright och läs sidan innan punkten används som fynd — åt båda hållen,
både hos mottagaren och hos konkurrenterna som påstås ha den.- Rader med
pdp_ok=Falseär mätfel, inte fynd — stryk dem först av allt.
Går/products.jsoninte att hämta fallerreviews,product_schema,images_3plusochdesc_300plusalla ut som False utan att ha mätts, och
domänen ser ut som dagens starkaste prospekt medn_missing=5. Tre orsaker:
lösenordsskyddad butik (/password, HTTP 401), headless Shopify
(Hydrogen/Oxygen —products.jsonfinns inte), botblockering. Kontrollprov
2026-08-22:silvercut.commättes till fem saknade punkter men hade i
renderad DOM 172 omdömen, Product-schema och 16 produktbilder. Filtrera påpdp_oki-benchmark-raw.csvINNAN gaten räknas. Vill du behålla domänen
måste den mätas om i renderad DOM. - En punkt som inte är en säljhävstång i mottagarens kategori är inget fynd.
sub_pdp(prenumeration på produktsidan) saknades hos 20 av 20 domäner i
smyckeskategorin 2026-08-22 — ingen prenumererar på ett halsband. Kanon kräver
att varje fynd rimligen kan kopplas till att sälja mer, så punkten räknas inte
i smycken, klockor, glasögon eller parfym. Räkna omn_missingutan den innan
gaten tillämpas, och notera i rapporten hur många rader det kostade.
3. Copy — enligt kanon
Läs references/copy-template.md och skriv en rad per mottagare. AI skriver
resultatet till <output_dir>/YYYY-MM-DD-berikad.csv: alla kolumner ur-prospekt.csv (Bolag, Mottagare, E-post, Alt-adress …) plus Nisch, Fynd
(exakt tre, semikolonseparerade, samma ordning som i mejlet), Ämnesrad ochCopy (hela mejltexten inklusive signatur). Strukna rader tas inte med alls.
Den filen är input till steg 4.
- Skicka eller stryk: inga andra utfall får gå till review-sheeten.
n_missing≥ 3 → skicka. Välj de tre fynd som är enklast att visa på skärm
och tydligast kopplade till att sälja mer, och skriv in dem iFynd.n_missing< 3 → stryk raden. Skriv aldrig "två saker".- Den aktuella AI-ledda partnerprocessen använder Mikael-varianten och
operator: mikael. Sebastian- och Stina-varianterna är historiska profiler,
inte tillstånd att tilldela dem daglig outreach. - Segmentlås: Mikaels mening om cáene, True Religion, Primus Equipment och
Silva Sweden används endast närcopy_segment: ecommerce. Bygg, el, stål,
fastigheter, industri och annan traditionell B2B använder alltidcopy_segment: traditional-b2boch det separata introt i copymallen. - Mallen använder
Vi har tittat närmare på ert bolag och hittat tre saker som vi gärna vill visa er,sälja meroch 10–15 minuters skärmdelning.
Följ den valda mallen ordagrant; fyll bara dess faktaslots. - Konkurrenter nämns aldrig i mejlet. Ingen bransch-N, inga
X av era Y. - Komma, inte tankstreck. Facktermer förklaras vid första användning.
- CTA-tider enligt rullande regeln i mallen: utskicksdag +3 dagar kl 10:00,
+2 vardagar kl 14:00 (helg rullar fram till vardag). Samma par för hela dagen. - Avsluta med endast tidsfrågan i mallen. Lägg inte till kalenderinbjudan eller
möteslänk i första mejlet. - Ämnesraden finns i den bindande mallen.
4. Review-sheet
python3 scripts/build_sheet.py --csv <berikad.csv> --title "Outreach <operatör> YYYY-MM-DD"Kolumner: Nr, Bolag, Mottagare, E-post, Alt-adress, Nisch, Fynd,
Ämnesrad (förslag), Copy, Godkänd, Skickat, Utfall. Fynd innehåller exakt
tre semikolonseparerade fynd — samma tre som mejlet lovar. build_sheet.py
stoppar hela bygget om en rad saknar nisch, har annat än tre fynd, bryter det
bindande copykontraktet, korsar operatörsprofil eller nämner konkurrenter. Den
valda profilen läses från operator och copy_segment i config.json. Med gws skapas sheeten
direkt; utan gws behålls CSV:n för manuell import.
Godkänd är en förkryssad kryssruta (beslut Mikael 2026-08-18). Skriptet sätter
en riktig BOOLEAN-validering på hela kolumnen och kryssar i varje rad. Granskningen
blir därmed en utkryssning: Mikael läser raderna och kryssar UR det som inte
ska gå, i stället för att skriva JA på varje rad som ska gå. En urkryssad rad gråas
ut och stryks över i hela sin bredd, så bortfallet syns direkt.
Två fällor, båda hanterade i build_sheet.py — bryts de blir rutan tom eller
oklickbar: skrivningen måste ske med valueInputOption: USER_ENTERED (annars landar
"TRUE" som text), och valideringen måste sättas i ett batchUpdate EFTER att värdena
skrivits.
5. Granska → godkänn → skicka
AI går igenom varje rad, faktaverifierar de tre fynden och bifogar underlaget.
Mikael granskar mottagare, ämnesrad och exakt copy utan att göra om researchen,
och kryssar UR rutan i Godkänd på de rader som inte ska skickas. Allt som
fortfarande är ikryssat
går ut. Textläget lever kvar parallellt för sheets byggda före 2026-08-18 och för
den som hellre skriver: JA / OK / X godkänner, NEJ avslår.
Rollkonton kan inte godkännas med en kryssruta. Adresser som info@, order@,hej@ m.fl. stoppas alltid av send_approved.py, även när rutan är ikryssad, och
listas separat i dry-run. För att ändå skicka till dem krävs ett aktivt beslut:JA ROLLKONTO i textläget, eller flaggan --allow-role på körningen. Standardlinjen
är att inte skicka till dem alls (beslut Mikael 2026-08-17: "kommer ge noll").
När Mikael säger "skicka" kör AI:
python3 scripts/send_approved.py --sheet <spreadsheetId> # dry-run, visa planen
python3 scripts/send_approved.py --sheet <spreadsheetId> --send # avfyraBara rader med Godkänd + tom Skickat väljs; Skickat stämplas i sheeten efter
varje utskick. Två vägar, styrs av send_path i config.json:
gws(default): skriptet skickar självt från operatörens Gmail viagws gmail +send. Avsändar-gate: gws-kontot måste matchasender_email,
annars vägrar skriptet — fel konto kan aldrig avfyra en batch.connector: avsändarkontot nås via claude.ai:s Gmail-connector i
stället för gws (t.ex. Mikael: gws = caene, ecomconsulting = connector).
Claude skickar då raderna själv via connectorn — dry-run-listan är
arbetslistan, ämnesrad ur Ämnesrad-kolumnen, body = Copy + signature ur
config — och stämplar Skickat direkt efter varje skickat mejl:python3 scripts/send_approved.py --sheet <id> --stamp <radnr,...>.--sendär avstängt i detta läge. Förutsätter att connectorn är kopplad
till samma adress somsender_email— verifiera vid installationen genom
ett testmejl till operatörens egen adress och läs avsändaren i tråden.
Kör alltid dry-run först och visa Mikael utfallet innan något skickas.
6. Positivt svar → partneröverlämning → möte
Vid positivt svar: logga utfallet och koppla först då in Sebastian och Stina för
fördjupad analys eller mötesmedverkan vid behov. Driv mot bokat möte med två
konkreta tider. När mötet bokats görs de
tre fynd som mejlet lovar visningsbara på skärm — de står redan i sheetensFynd-kolumn och får inte bytas ut mot generiska råd. Hittas fler punkter under
förberedelsen är de en bonus i mötet, inte en ersättning. För in Utfall i
sheeten löpande.
Guardrails
- Inget utskick utan Godkänd + "skicka" —
send_approved.py --sendkörs
först när Mikael har gått igenom sheeten OCH sagt "skicka". Förkryssade
rutor är ingen genväg förbi granskningen: de sparar skrivandet, inte läsandet.
Har Mikael inte sagt "skicka" är ikryssade rader bara ett utkast.
Ändra aldrig copy i en rad efter att den godkänts. - Rollkonton (info@/kontakt@/order@) skickas inte till utan explicit OK per rad.
- Apollo-krediter kostar pengar — hämta aldrig fler än dagens N utan att fråga.
- Ingen partnerarbetsbörda före positivt svar: Sebastian och Stina ska inte
göra research, copygranskning, presentation eller offert innan mottagaren har
visat intresse. - Split utan påhitt: en ny kund från processen omfattas av en procentuell
split, men procentsats, beräkningsgrund och tidsperiod måste beslutas
skriftligt innan avräkning eller utbetalning. - Ingen nödmall: hittas färre än tre fynd stryks prospektet. Skriv aldrig
copy om varför researchen misslyckades och aldrig ett annat antal än tre. - API-nyckeln ligger i
.env/miljön, aldrig i filer som delas eller committas. - Misslyckas ett steg: diagnostisera och rapportera orsaken, hoppa aldrig tyst
över mätningen eller fyll i gissade värden.
Filer i denna skill
scripts/apollo_pull.py— steg 1 (lista + adresser)scripts/benchmark.py— steg 2 (mätning, två profiler,missingstyr copyn)scripts/build_sheet.py— steg 4 (review-sheet)scripts/send_approved.py— steg 5 (utskick av godkända rader via gws)references/copy-template.md— copy-kanon (steg 3), bindandereferences/operating-model.md— roller, överlämning och splitprincip, bindandeconfig.example.json— mall för operatörens configtests/test_outreach_contract.py— kontraktstester; kör efter varje ändring
i skript eller kanon:python3 -m unittest discover -s tests -v(stdlib,
ingen pytest behövs)
Uppdatering hos Sebastian och Stina
- Klona repot rakt in i skill-mappen:
(Eller till projektetsgit clone https://github.com/EcomConsulting/outreach-daily.git ~/.claude/skills/outreach-daily.claude/skills/om du hellre kör per projekt.) - Kör
git pull --ff-onlyoch läsreferences/operating-model.md. - Sebastian och Stina behöver inte konfigurera Apollo, Gmail eller en daglig
kategori. De kopplas in av Mikael först efter ett positivt svar. - En ny session eller omstart kan krävas för att skillkatalogen ska läsa den
uppdaterade beskrivningen.
Uppdateringar
Repot är enda källan. Mikael publicerar ändringar därifrån; alla andra hämtar
dem med git pull i skill-mappen (steg 0 gör det automatiskt vid varje
körning). Zip-utskick är avvecklat — det var så copy-kanon hann driva isär
mellan operatörerna en gång.