outreach-send-coreReusable outreach toolkit for backend projects that need:
It does not include app-specific preview URLs, unsubscribe token generation, branded templates, database schemas, or Local News article creation.
sent, failed, and suppressed outcomes through your repository adapterGoogleFirstSeedProvidercreateOutreachEngine(...)BrevoOutboundMailerBouncerEmailVerifierevaluateBouncerEmailVerification(...)GoogleFirstSeedProviderGooglePlacesSeedProvidernpm install outreach-send-core
The package is functional and ships with a runnable demo that exercises:
createOutreachEngine(...)BrevoOutboundMailerBouncerEmailVerifierevaluateBouncerEmailVerification(...)GoogleFirstSeedProviderGooglePlacesSeedProviderLive demo:
https://andreakrea.github.io/outreach/Run the mock preview locally with:
npm install
npm run demo
That mode uses mocked HTTP responses, so you do not need Brevo, Bouncer, Google Places, or live public-search credentials just to preview the package.
If you want the finder demo to behave like the real app and surface live diagnostics, run:
$env:PUBLIC_SERVICE_USER_AGENT='outreach-demo/1.0 (you@example.com)'
$env:GOOGLE_PLACES_API_KEY='your_key_here'
npm run demo:live -- --cities Torino --business-types restaurant --keywords pizza
For quick local startup, you can also use the launcher scripts:
npm run start:demo:live
npm run start:playground
On Windows, the direct wrappers work too:
scripts\demo-live.cmd
scripts\playground.cmd
The live demo keeps the send-engine example mock-backed, but runs GooglePlacesSeedProvider against live Google Places, OpenStreetMap, and public web results using the legacy multi-source acceptance logic. It prints:
You can tune the live finder with CLI flags or env vars such as:
--cities, --provinces, --business-types, --keywords, --limit--google-places-enabled, --openstreetmap-enabled, --web-search-enabled--search-budget-ms, --crawl-page-limit, --max-enrichment-candidatesPUBLIC_SERVICE_USER_AGENT, PUBLIC_SERVICE_REFERER, GOOGLE_PLACES_API_KEYIf you want a browser UI for testing the package locally, run:
$env:GOOGLE_PLACES_API_KEY='your_key_here'
npm run playground
Then open http://127.0.0.1:4317.
The finder playground uses live provider calls for Google Places, OpenStreetMap, and public web search. It now exposes the live tuning knobs too, including:
If you leave Google Places enabled without a key, the provider will skip Google discovery and note that in diagnostics.
If you want the live site to publish from this repo, enable GitHub Pages for the main branch and /docs folder in the repository settings.
Example output:
Mock-backed demo for outreach-send-core.
No Brevo, Bouncer, Google Places, or live public-search credentials are required for this preview.
Outreach Engine Demo
====================
{
"result": {
"approvedCount": 3,
"attemptedCount": 2,
"sentCount": 1,
"suppressedCount": 1,
"skippedMissingPreviewCount": 1
}
}
Prospect Finder Demo
====================
{
"diagnostics": {
"provider": "google_places",
"strategy": "multi_source_province_acceptance"
},
"firstSeed": {
"companyName": "Cafe Uno",
"businessType": "restaurant",
"primaryEmail": "booking.cafeuno@gmail.com"
}
}
import {
BrevoOutboundMailer,
BouncerEmailVerifier,
createOutreachEngine,
evaluateBouncerEmailVerification,
} from 'outreach-send-core'
const outreach = createOutreachEngine({
repository: myRepository,
mailer: new BrevoOutboundMailer({
apiKey: process.env.BREVO_API_KEY,
fromEmail: process.env.EMAIL_FROM,
fromName: process.env.EMAIL_FROM_NAME,
}),
emailVerifier: new BouncerEmailVerifier({
apiKey: process.env.BOUNCER_API_KEY,
}),
evaluateEmailVerification: evaluateBouncerEmailVerification,
resolveGlobalDailyCap: async () => 50,
resolveReplyTo: async () => 'hello@example.com',
linkBuilder: async ({ campaign, contact, preview }) => ({
previewUrl: preview.publicUrl,
unsubscribeUrl: `https://example.com/unsubscribe?email=${encodeURIComponent(contact.email)}`,
registerUrl: `https://example.com/register?campaign=${campaign.id}`,
}),
templateBuilder: async ({ prospect, links }) => ({
subject: `Preview for ${prospect.companyName}`,
htmlContent: `<a href="${links.previewUrl}">Open preview</a>`,
textContent: `Open preview: ${links.previewUrl}`,
tags: ['outreach'],
}),
})
await outreach.sendApprovedBatch('campaign_123')
import { GoogleFirstSeedProvider } from 'outreach-send-core'
const provider = new GoogleFirstSeedProvider({
apiKey: process.env.GOOGLE_PLACES_API_KEY,
maxExpandedMunicipalities: 12,
businessTypeLabels: {
restaurant: 'restaurant',
salon: 'hair salon',
},
nearbyTypesByBusinessType: {
restaurant: ['restaurant'],
salon: ['beauty_salon', 'hair_care'],
},
})
const result = await provider.search({
country: 'IT',
language: 'it',
cities: ['Torino'],
provinces: [],
businessTypes: ['restaurant'],
keywords: ['pizza'],
limit: 20,
})
The production provider now uses Google Places as the business identity source of truth, applies staged identity/type/website/contact gates, returns accepted prospects in seeds, and holds borderline prospects in reviewCandidates.
Each returned seed can now include:
primaryEmailqualityScoresourceMetadata with source classes, source providers, and a score summaryDiagnostics now also expose:
rawCandidateCountacceptedCandidateCountacceptedCountreviewCountrejectedCountrejectionCountsrejectionCountsByStagereviewCountssourceContributionCountsYour repository must implement:
getCampaigncountSentSincelistApprovedSendCandidatesgetSuppressedEmailSetsuppressEmailrecordSuppressedSendrecordFailedSendrecordSentSendSee src/types.ts for the full interface definitions.
If you are wiring this package with an AI coding agent, start with AGENTS.md.
That file gives the exact integration path:
sendApprovedBatch(...) execution flowGoogleFirstSeedProvider integration pathseeds vs reviewCandidatesnpm run build
npm test
npm pack
npm publish
If you are publishing a public scoped package on npm, use:
npm publish --access public