amjadiqbal/filament-audience-sync
A Filament plugin for syncing audience and subscriber records with external marketing platforms. It adds admin screens to configure field mappings, trigger syncs, and monitor status from your Laravel app, with queued jobs for reliable background processing.
This is a Filament panel plugin, not a general-purpose Laravel mail package. The value is concentrated in the resource actions and the status column, so start there rather than with the widgets.
Before installing, check the version matrix. It requires PHP 8.2–8.5, Laravel 12/13, and Filament 4/5. On Laravel 11 or Filament 3 this will not install; that constraint will drive the decision more than any feature.
Minimal path:
composer require amjadiqbal/filament-audience-sync
php artisan vendor:publish --tag=filament-audience-sync-migrations
php artisan migrate
Then register the plugin on the panel:
->plugin(AudienceSyncPlugin::make())
Where to look first: the README's "model and resource setup" section. The release notes deliberately defer to it, and that is where the pieces the notes only name — the Subscribable contract/trait, provider audience/list IDs, driver credentials, and the config file — are actually specified. Confirm whether a config file is publishable; only the migrations tag is documented here.
First use case: pick one existing resource whose model is a person (a Customer, User, or Subscriber), add the trait, and put the Subscribe record action on the table row plus the status column. Do this against a sandbox/test audience with two or three of your own addresses before touching a bulk action or a production list. That single resource validates credentials, field mapping, and the local status table in one pass.
Record actions as the primary interface. Subscribe / Sync details / Edit tags and Unsubscribe map onto distinct support workflows: onboarding a new signup, fixing a drifted record, and honouring a request. Put Subscribe and Unsubscribe behind confirmation modals on the row; keep Sync details as a plain action for staff.
Bulk actions belong behind a queue worker. The queued bulk actions chunk and retry, but only do useful work if a worker is running and your queue is sized for it. Pattern: restrict the bulk action to a filtered table (a "needs sync" filter) rather than "select all", and run large backfills in off-peak windows.
Treat sync as reconciliation, not fire-and-forget. The status column reads from a local table, so it is a cache of provider truth. A reasonable pattern is a scheduled job that calls the same sync path for records touched recently, so panel state converges after changes made in Mailchimp/Brevo's own UI.
Driver extension is a mapping exercise, not a class-plugging one. AudienceSync::extend() registers a provider, but Mailchimp merge fields, Brevo attributes, and lists are structurally different. Plan an explicit map from your model attributes to each provider's field names, and register the extension from a service provider so it is present before the panel boots.
Consent-first shapes your domain model. Because anyone unsubscribed, bounced, or archived at the provider can never be re-subscribed from the panel, model "subscribed" as an intent that the provider may override. Never delete a local record to "reset" someone — that is precisely the state the package guards against.
Widgets are for stakeholders, not operators. Members, unsubscribes, open/click rates, and recent campaigns are cached. Wire them onto a dashboard for reporting; do not use them to verify that an action you just took worked. Use the status column and Sync details for that.
Test your mapping with Http::fake(). The package's own fixtures give you documented request/response shapes for both providers. Reuse those shapes to assert what your model actually sends — especially custom fields, which the package cannot know exist in your account.
Pending confirmation state with no obvious exit. Nothing in the notes says the package polls for confirmation. Plan how a pending record becomes Subscribed — a scheduled sync, a manual Sync details, or provider-side confirmation only.retry_after against the longest backoff and keep tries modest so a flapping provider does not produce repeated tag/attribute writes. Email-keyed subscribe operations are largely idempotent; tag edits are less so..env are fine, but your own Log::error($e) with request context, or Filament's debug output, can still surface them. Audit your exception reporting around these actions..env versus config versus the database before you edit a migration in place.How can I help you explore Laravel packages today?