Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Audience Sync Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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.

Implementation Patterns

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.

Gotchas and Tips

  • Provider behaviour is unverified against live APIs. The release is explicit that tests are fixture-based and the UI pass used a stateful fake. Treat v1.0.0 as needing a staging smoke test on a real Mailchimp and/or Brevo account with a throwaway list before any production bulk action.
  • "Never subscribed again" will generate support tickets. The consent guard is absolute and correct for GDPR, but staff will ask why a contact cannot be resubscribed. Decide the escalation path (contact the person directly; the provider console is the only route) before rollout.
  • The status column drifts. No API call per row is the right call for table performance, but changes made in the provider UI will not appear until a sync runs. When a status looks wrong, run Sync details before debugging anything else.
  • Double opt-in produces a 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.
  • Queue retry can stack with package backoff. The package handles 429/5xx with exponential backoff; your queue also retries. Check 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.
  • Key redaction covers package-thrown errors only. Keys in .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.
  • Bulk actions burn provider quota even when they succeed. Retry-After handling smooths bursts; it does not raise daily caps. Size chunks against the provider's plan limits, not against the package defaults.
  • Check the failed-sync reason field first. Design intent is that failed syncs carry the reason locally. That field is your only offline diagnostic and the fastest way to distinguish a mapping error from an auth or rate-limit problem.
  • Read the published migration before it holds data. Only the migrations tag is documented; if you later discover audience/list configuration is also publishable, know which values live in .env versus config versus the database before you edit a migration in place.
  • Widgets are cached — find the TTL. Expect stale numbers in tests and immediately after an import. Locate the cache keys so you can bust them in your own test setup rather than assuming the dashboard reflects fresh state.
  • The GDPR notes are a starting point. Ensure your privacy policy, retention, and deletion flow match what the package actually writes locally — an unsubscribe is not a data-erasure operation.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk