outreach-send-core
v0.2.16
Published
Reusable outreach toolkit with a headless send engine, Brevo mailer, Bouncer verifier, and multi-source prospect search.
Maintainers
Readme
outreach-send-core
Reusable outreach toolkit for backend projects that need:
- a headless send engine
- Brevo transactional email sending
- Bouncer email verification
- Google-first production prospect search
- accepted-only legacy multi-source prospect search
- a mock-backed preview and a live-tuning demo for prospect search
It does not include app-specific preview URLs, unsubscribe token generation, branded templates, database schemas, or Local News article creation.
What It Handles
- Daily send caps
- Candidate filtering for missing contacts or previews
- Suppression checks
- Optional email verification
- Template rendering
- Delivery handoff to your mailer
- Recording
sent,failed, andsuppressedoutcomes through your repository adapter - Production prospect discovery through
GoogleFirstSeedProvider - Legacy prospect discovery across Google Places, OpenStreetMap, public web results, and optional LLM hints
Included Adapters
createOutreachEngine(...)BrevoOutboundMailerBouncerEmailVerifierevaluateBouncerEmailVerification(...)GoogleFirstSeedProviderGooglePlacesSeedProvider
Install
npm install outreach-send-coreDemo
The package is functional and ships with a runnable demo that exercises:
createOutreachEngine(...)BrevoOutboundMailerBouncerEmailVerifierevaluateBouncerEmailVerification(...)GoogleFirstSeedProviderGooglePlacesSeedProvider
Live demo:
https://andreakrea.github.io/outreach/
Run the mock preview locally with:
npm install
npm run demoThat 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 ([email protected])'
$env:GOOGLE_PLACES_API_KEY='your_key_here'
npm run demo:live -- --cities Torino --business-types restaurant --keywords pizzaFor quick local startup, you can also use the launcher scripts:
npm run start:demo:live
npm run start:playgroundOn Windows, the direct wrappers work too:
scripts\demo-live.cmd
scripts\playground.cmdThe 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:
- resolved criteria and provider toggles
- search diagnostics and rejection counts
- accepted seeds
- fetch logs for provider calls and page crawls
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_KEY
If you want a browser UI for testing the package locally, run:
$env:GOOGLE_PLACES_API_KEY='your_key_here'
npm run playgroundThen 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:
- search budget
- crawl page limit
- max enrichment candidates
- timeout
- public-service user agent and referer overrides
- request-log toggling
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": "[email protected]"
}
}Production Quick Start
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 () => '[email protected]',
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')Prospect Search Example
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:
primaryEmailqualityScoresourceMetadatawith source classes, source providers, and a score summary
Diagnostics now also expose:
rawCandidateCountacceptedCandidateCountacceptedCountreviewCountrejectedCountrejectionCountsrejectionCountsByStagereviewCountssourceContributionCounts
Required Repository Contract
Your repository must implement:
getCampaigncountSentSincelistApprovedSendCandidatesgetSuppressedEmailSetsuppressEmailrecordSuppressedSendrecordFailedSendrecordSentSend
See src/types.ts for the full interface definitions.
AI Agent Implementation Guide
If you are wiring this package with an AI coding agent, start with AGENTS.md.
That file gives the exact integration path:
- the repository contract
- required data invariants
- the canonical
sendApprovedBatch(...)execution flow - the production
GoogleFirstSeedProviderintegration path - the meaning of
seedsvsreviewCandidates - the meaning of each result counter
- a minimal adapter skeleton
- an acceptance checklist for validation
Publish
npm run build
npm test
npm pack
npm publishIf you are publishing a public scoped package on npm, use:
npm publish --access public