survos / outreach-bundle
Lightweight outreach CRM bundle for organizations, contacts, activities, and external system footprints.
Package info
github.com/survos/outreach-bundle
Type:symfony-bundle
pkg:composer/survos/outreach-bundle
Fund package maintenance!
Requires
- php: ^8.5
- api-platform/doctrine-orm: ^4.2 || ^5.0
- doctrine/doctrine-bundle: ^3.0
- doctrine/orm: ^3.3
- survos/import-bundle: ^2.5
- survos/jsonl-bundle: ^2.5
- survos/kit-bundle: ^2.34
- symfony/config: ^8.1
- symfony/console: ^8.1
- symfony/dependency-injection: ^8.1
- symfony/framework-bundle: ^8.1
- symfony/http-foundation: ^8.1
- symfony/http-kernel: ^8.1
- symfony/routing: ^8.1
- symfony/string: ^8.1
- twig/twig: ^3.4
Requires (Dev)
- phpunit/phpunit: ^12.0 || ^13.0
Suggests
- symfony/serializer: Useful when consuming the ApiPlatform resources from JSON clients.
Provides
None
Conflicts
None
Replaces
None
- dev-main
- 2.34.16
- 2.34.0
- 2.33.0
- 2.31.1
- 2.10.19
- 2.10.18
- 2.10.17
- 2.10.16
- 2.10.15
- 2.10.14
- 2.10.13
- 2.10.12
- 2.10.11
- 2.10.10
- 2.10.9
- 2.10.8
- 2.10.7
- 2.10.6
- 2.10.5
- 2.10.4
- 2.10.3
- 2.10.2
- 2.10.1
- 2.10.0
- 2.9.4
- 2.9.3
- 2.9.2
- 2.9.1
- 2.9.0
- 2.8.4
- 2.8.3
- 2.8.1
- 2.8.0
- 2.7.23
- 2.7.22
- 2.7.21
- 2.7.20
- 2.7.19
- 2.7.18
- 2.7.17
- 2.7.16
- 2.7.15
- 2.7.14
- 2.7.13
- 2.7.12
- 2.7.11
- 2.7.10
- 2.7.9
- 2.7.8
- 2.7.7
- 2.7.6
- 2.7.5
- 2.7.4
- 2.7.3
- 2.7.2
- 2.7.1
- 2.6.0
- 2.5.8
- 2.5.7
- 2.5.6
- 2.5.5
- 2.5.3
- 2.5.2
- 2.5.1
- 2.5.0
- 2.4.4
- 2.4.3
- 2.4.2
- 2.4.1
- 2.4.0
- 2.3.0
- 2.2.5
- 2.2.4
- 2.2.3
- 2.2.2
- 2.2.1
- 2.2.0
- 2.1.2
- 2.1.1
- 2.0.220
- 2.0.219
- 2.0.218
- 2.0.217
- 2.0.216
- 2.0.215
- 2.0.214
- 2.0.213
- 2.0.212
- 2.0.211
- 2.0.210
- 2.0.209
- 2.0.208
- 2.0.207
- 2.0.206
- 2.0.205
- 2.0.204
This package is auto-updated.
Last update: 2026-09-30 01:56:52 UTC
README
Small outreach CRM bundle for conference and prospect management.
It is intentionally narrow:
- organizations
- contacts
- activities
- external system footprints
This is not a general CRM and it does not try to manage tenants, invoicing, or full marketing automation.
Use Cases
- pre-load conference attendee lists
- track one-time outreach permission and follow-up status
- record booth conversations and demos
- track whether an organization already uses PastPerfect, Omeka, or another system
- attach lightweight tags such as
marac-2026orssai-tenant:middleburg-historical-society
Model
The bundle ships four Doctrine entities and exposes them as ApiPlatform resources:
OrganizationContactActivityOrganizationSystem
Personal Email Heuristic
The included ConferenceRegistrantUpserter service can infer an organization key from a CSV row:
- if the email domain is not personal, use the domain as the organization key
- if the email domain looks personal (
gmail.com,yahoo.com, etc.), fall back to the provided organization name
Examples:
snallen@vuu.edu=>domain:vuu.edufall@loc.gov=>domain:loc.govarchival2010@gmail.com+New Brunswick Free Public Library Archive=>org:new-brunswick-free-public-library-archive
This is intentionally heuristic, not canonical truth.
Installation
composer require survos/outreach-bundle:@dev
Then link it in local development using your normal monorepo flow.
Configuration
The bundle is self-registering:
- Doctrine attribute mappings are registered automatically
- ApiPlatform mapping paths are registered automatically
Optional configuration:
survos_outreach: personal_email_domains: - gmail.com - yahoo.com
Services
OrganizationKeyGuesser
Builds stable organization keys from email and/or organization name.
ConferenceRegistrantUpserter
Upserts an Organization and Contact from an attendee row such as:
[
'First Name' => 'Selicia',
'Last Name' => 'Allen',
'Organization' => 'Virginia Union University',
'Email' => 'snallen@vuu.edu',
]
Optional event tag example:
$upserter->upsert($row, ['marac-2026']);
That tag is added to both the organization and the contact.
Workflow
Recommended flow for conference rosters:
bin/console import:convert registrants.csv --tags=outreach,marac-2026 bin/console outreach:import:jsonl var/data/registrants.jsonl --tag=marac-2026
When import:convert sees the outreach tag, the bundle listens to row events and maps fields like:
First Name->first_nameLast Name->last_nameOrganization->organizationEmail->email
The second command imports the normalized JSONL into Organization and Contact, deduplicating organizations by the configured email-domain heuristic.
Consent groundwork (implementation checkpoint)
Announcements are separate audiences, for example survos-announcements and
pressia-updates, both using AudienceKind::ANNOUNCEMENTS. Each requires email
confirmation. Following a site, bookmarking a story, or importing a contact does
not grant this permission. API resources require ROLE_ADMIN.
ConsentManager records requests, confirmation, audience unsubscribe and global
outreach suppression as activities. ConsentLinks generates signed, purpose-bound
links. Confirmation expires after seven days and is invalidated by a fresh opt-in
or an address change. Unsubscribe links do not expire. GET renders a form; POST
changes permission so email link scanners do not change it accidentally.
The bundle now uses kit-bundle conventions. Consent routes are disabled by default while delivery/provider integration remains unfinished. To enable them in a consuming application:
survos_outreach: routes_enabled: true public_base_url: 'https://pressia.org' signing_secret: '%env(OUTREACH_SIGNING_SECRET)%'
Use a stable secret of at least 32 characters. Allow anonymous access to the
configured /outreach prefix in the host security configuration: the signed link
authorizes the action. Keep locale prefixes disabled for these generated URLs.
Existing databases need a migration for Subscription::consentVersion: add a
nullable 32-character column, backfill each existing row with an independently
generated 16-byte random hex string, then make it non-nullable. Generate the
migration in the consuming app using its naming strategy; this bundle does not
automatically alter existing databases.
This checkpoint does not implement confirmation email dispatch, a send worker,
provider webhook synchronization, or campaign delivery. Those must enforce
SendPolicy at send time, including in long-running workers. Provider selection
is still under review. No mail is sent by enabling these services.
Tests from the monorepo root:
vendor/bin/phpunit --no-configuration --bootstrap bu/outreach-bundle/tests/bootstrap.php bu/outreach-bundle/tests