whilesmart/eloquent-outreach

Host-agnostic user outreach for Laravel: compose messages with a call to action, target audiences, and track per-recipient deliveries across pluggable channels.

Maintainers

Package info

github.com/whilesmartphp/eloquent-outreach

pkg:composer/whilesmart/eloquent-outreach

Transparency log

Statistics

Installs: 43

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-dev 2026-06-27 17:09 UTC

This package is auto-updated.

Last update: 2026-06-28 10:00:27 UTC


README

Host-agnostic user outreach for Laravel. Compose a message with a call to action, target an audience, and track a per-recipient delivery, all over channels your app implements. The package owns the workflow; your app owns how recipients are found, addressed, personalized, and sent.

Installation

composer require whilesmart/eloquent-outreach
php artisan vendor:publish --tag=outreach-migrations
php artisan migrate

Concepts

  • Outreach — one composed message: subject, body, CTA (cta_label + cta_url), channel, audience criteria, status, optional scheduled_at, and rolled-up stats. Optional polymorphic owner (sender) and campaign (a campaign "has many" outreaches).
  • Delivery — one row per recipient, with status and timestamps. This is what later per-recipient actions (resend, view, mark) hang off.

What the host implements

use Whilesmart\Outreach\Contracts\AudienceResolver;  // criteria -> recipient models
use Whilesmart\Outreach\Contracts\MessageRenderer;   // outreach + recipient -> RenderedMessage
use Whilesmart\Outreach\Contracts\OutreachChannel;   // deliver a RenderedMessage

Bind your implementations in a service provider:

$this->app->bind(AudienceResolver::class, MyAudienceResolver::class);
$this->app->bind(OutreachChannel::class, MyEmailChannel::class);
// MessageRenderer defaults to attribute token replacement ({{first_name}});
// bind your own for richer templating.

Safe defaults ship out of the box: the audience resolves to nobody and the channel sends nowhere until you bind real ones.

Sending

use Whilesmart\Outreach\Models\Outreach;
use Whilesmart\Outreach\Services\OutreachDispatcher;

$outreach = Outreach::create([
    'channel' => 'email',
    'subject' => 'Hi {{first_name}}',
    'body' => 'We added something you will like.',
    'cta_label' => 'Open Trakli',
    'cta_url' => 'https://trakli.app/dashboard',
    'audience' => ['type' => 'inactive', 'days' => 30],
]);

app(OutreachDispatcher::class)->dispatch($outreach);

The dispatcher resolves the audience, creates a Delivery per recipient, renders and sends each through the bound channel, rolls the results onto the outreach, and emits OutreachDispatched, DeliverySucceeded, and DeliveryFailed for the host to bridge.

Campaigns

An outreach can belong to a campaign via the optional campaign morph, so a campaign (in your app or another package) can own many outreaches without this package depending on it.

License

MIT. WhileSmart LTD.