xavicabot / filament-activecampaign
Filament plugin for ActiveCampaign integration
Package info
github.com/xavicabot/filament-activecampaign
pkg:composer/xavicabot/filament-activecampaign
Requires
- php: ^8.2
- filament/filament: ^4.0|^5.0
- illuminate/support: ^11.28|^12.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^11.0|^12.3
README
Advanced ActiveCampaign integration for Laravel and FilamentPHP 3, focused on developer productivity.
This package allows you to:
- Sync Lists, Tags and Custom Fields from ActiveCampaign
- Create Automations based on custom events (
user.registered,wallet.first_deposit, etc.) - Subscribe contacts to Lists
- Add Tags
- Update Custom Fields
- Update System Fields (
firstName,lastName,phone, etc.) - Use dynamic templating (
{user.*},{ctx.*},{now}, etc.) - Log every automation execution (success, payload, warnings, errors)
- Preview the execution plan before running an automation
- Manage everything visually from FilamentPHP
π§ Installation
Install via Composer:
composer require xavicabot/filament-activecampaign
Publish config and migrations:
php artisan vendor:publish --provider="XaviCabot\FilamentActiveCampaign\ActiveCampaignServiceProvider" --tag=activecampaign-config php artisan vendor:publish --provider="XaviCabot\FilamentActiveCampaign\ActiveCampaignServiceProvider" --tag=activecampaign-migrations php artisan migrate
βοΈ Configuration
Add your ActiveCampaign credentials to .env:
ACTIVECAMPAIGN_BASE_URL="https://YOUR_ACCOUNT.api-us1.com" ACTIVECAMPAIGN_API_KEY="your-api-key"
Published config file:
config/activecampaign.php
π Syncing Metadata (Lists, Tags, Fields)
This package stores ActiveCampaign metadata locally:
activecampaign_listsactivecampaign_tagsactivecampaign_fields
Sync everything
php artisan activecampaign:sync-metadata
Or individually:
php artisan activecampaign:sync-metadata --lists php artisan activecampaign:sync-metadata --tags php artisan activecampaign:sync-metadata --fields
From Filament
The Automations resource includes a "Sync metadata" button to refresh lists/tags/fields without CLI access.
π·οΈ Creating Tags
You can create new tags either from the Filament UI or programmatically from your code.
From Filament UI
- Navigate to ActiveCampaign Tags in Filament
- Click the "Create Tag" button in the header
- Fill in the form:
- Tag Name (required) - The name as it will appear in ActiveCampaign
- Description (optional) - Internal reference note (only used when creating new tags)
- Submit
Smart behavior: If the tag name already exists (locally or in ActiveCampaign), it will be retrieved instead of creating a duplicate. No errors, just works!
From Code
Use the ActiveCampaign facade to create or retrieve tags programmatically:
Recommended: getOrCreateTag() (Safe, no duplicates)
use XaviCabot\FilamentActiveCampaign\Facades\ActiveCampaign; // Get existing tag or create if it doesn't exist $tag = ActiveCampaign::getOrCreateTag('VIP Customer'); // With description (only used if creating) $tag = ActiveCampaign::getOrCreateTag('Premium Member', 'Users with premium subscription'); // The returned $tag is an ActiveCampaignTag model instance echo $tag->ac_id; // ActiveCampaign ID echo $tag->name; // Tag name echo $tag->description; // Optional description
What happens with getOrCreateTag():
- Searches local database first (case-insensitive)
- If not found locally, searches ActiveCampaign API
- If found in AC but not locally, syncs it to local database
- If not found anywhere, creates it in ActiveCampaign and stores locally
- Cache automatically updated for instant use
Benefits:
- β No duplicate errors
- β Idempotent (safe to call multiple times)
- β Handles sync automatically
Alternative: createTag() (Always creates new)
// Only use if you're sure the tag doesn't exist $tag = ActiveCampaign::createTag('New Tag', 'Description');
Warning: This method will fail if the tag already exists in ActiveCampaign.
Note: All tags are created with type contact (the standard type for 99% of use cases).
π§· Managing Contact Tags & Lookup
Use the ActiveCampaign facade to manage tags on a contact and to look up existing contacts.
use XaviCabot\FilamentActiveCampaign\Facades\ActiveCampaign; // Find a contact by email (returns null if it does not exist) $contact = ActiveCampaign::getContactByEmail('jane@example.com'); // Attach one tag ActiveCampaign::addTagToContact($contact['id'], 'vip'); // Attach several tags in one call ActiveCampaign::addTagToContact($contact['id'], ['vip', 'beta-tester']); // List the tag associations for a contact $associations = ActiveCampaign::getContactTags($contact['id']); // Detach a tag by name (no-op if it is not attached) ActiveCampaign::removeTagFromContact($contact['id'], 'beta-tester');
Tag names are resolved to ActiveCampaign IDs using the same cached lookup as addTagToContact β make sure the tag has been synced (php artisan activecampaign:sync-metadata --tags) or created beforehand.
β‘ Single-Call Field Sync
When an automation has multiple fields and/or system_fields, the package now sends them in a single POST /contact/sync request (plus the initial contact resolution call) instead of one HTTP call per field. No code change is required on the consumer side β existing automations benefit automatically.
Behavioural note: a custom field whose template references
{user.*}or{ctx.*}and resolves to an empty value is now omitted from the payload (it used to be sent as an empty string). This mirrors howsystem_fieldsalready behaved and prevents accidental overwrites with blank values.
π¦ Filament Resources Included
| Resource | Purpose |
|---|---|
| Automations | Create automations triggered by custom events |
| Automation Logs | Inspect execution results, payloads, warnings, errors |
| Lists | Read-only view of AC lists |
| Tags | View and create AC tags |
| Fields | Read-only view of AC custom fields |
β‘ Creating Automations
An Automation links:
- a custom event name
- to a set of ActiveCampaign actions
Actions available:
- Subscribe to a list
- Add tags
- Update custom fields
- Update system fields
Example automation:
| Setting | Value |
|---|---|
| Event | user.registered |
| List | βMain Listβ |
| Tags | Customer |
| Custom Fields | LANGUAGE = {user.profile.language} |
| System Fields | firstName = {user.name} |
π§ Triggering Automations from Your Laravel App
Use the facade:
use XaviCabot\FilamentActiveCampaign\Facades\ActiveCampaignAutomations; ActiveCampaignAutomations::trigger('user.registered', $user);
With context:
ActiveCampaignAutomations::trigger('wallet.first_deposit', $user, [ 'amount' => 150, 'currency' => 'EUR', ]);
With another model:
ActiveCampaignAutomations::trigger('post.published', $user, [ 'post' => $post, ]);
Trigger without a registered user (by email)
You can also trigger automations for contacts that are not users in your system by providing an email and optional contact data. The contact will be created/synced in ActiveCampaign if it does not exist.
Basic usage:
use XaviCabot\FilamentActiveCampaign\Facades\ActiveCampaignAutomations; ActiveCampaignAutomations::triggerWithEmail('newsletter.signup', 'john@example.com');
With optional contact data and context:
ActiveCampaignAutomations::triggerWithEmail( 'lead.captured', 'jane@example.com', [ 'firstName' => 'Jane', 'lastName' => 'Doe', 'phone' => '+1 555 123 4567', ], [ 'source' => 'landing-123', 'utm' => [ 'campaign' => 'winter-sale', ], ] );
Notes:
- email is required; other contact fields are optional.
- System fields defined in the automation will be synced using the provided email.
- Template placeholders {ctx.} work as usual; {user.} placeholders will be left as-is if no user is provided.
π Async (Queue) Support
By default, automations run synchronously. You can enable async execution to dispatch triggers to a Laravel queue.
Configuration
In your published config/activecampaign.php:
'async' => false, // Set to true to dispatch all triggers to queue 'queue' => 'default', // Queue name 'async_tries' => 3, // Max retry attempts 'async_backoff' => [10, 60], // Backoff in seconds between retries
Or use environment variables:
ACTIVECAMPAIGN_ASYNC=true ACTIVECAMPAIGN_QUEUE=activecampaign
Usage
Option 1: Global async via config
Set async => true in config. All trigger() and triggerWithEmail() calls will automatically dispatch to the queue:
// These will be queued automatically when async is enabled ActiveCampaignAutomations::trigger('user.registered', $user); ActiveCampaignAutomations::triggerWithEmail('newsletter.signup', 'jane@example.com');
Option 2: Explicit async methods
Use triggerAsync() or triggerWithEmailAsync() to always dispatch to the queue, regardless of the async config value:
// Always queued, even if async config is false ActiveCampaignAutomations::triggerAsync('user.registered', $user, ['plan' => 'pro']); ActiveCampaignAutomations::triggerWithEmailAsync('lead.captured', 'jane@example.com', [ 'firstName' => 'Jane', ], [ 'source' => 'landing-page', ]);
How it works
- The job serializes only primitive data (event name, user ID, email, contact data, context) β no Eloquent models are serialized.
- When a
userIdis provided, the job resolves the user from the database at execution time. If the user no longer exists, the job exits gracefully without errors. - Retries and backoff are configurable via
async_triesandasync_backoff.
Retrocompatibility
With async => false (default), the package behaves exactly as before β all triggers execute synchronously. No changes needed in existing code.
π§© Template Engine
Templates support:
Global
{now}β current datetime{now_date}β current date
User data (supports relations)
{user.email}
{user.name}
{user.profile.phone}
{user.profile.language.name}
Context data
{ctx.amount}
{ctx.currency}
{ctx.post.title}
{ctx.invoice.total}
If a value is missing β placeholder is preserved.
Objects/arrays β JSON encoded.
π Preview Mode (For Testing Automations)
From Filament, you can preview an automation:
- Select a User
- Provide JSON context
- Preview the execution plan without hitting ActiveCampaign
- See:
- list subscription
- tags
- field updates
- system fields
- rendered templates
- warnings
- payload
Perfect for debugging before going live.
π Execution Logs
Every execution is stored in:
activecampaign_automation_logs
Includes:
- automation_id
- event
- user_id
- success
- error_message
- context (JSON)
- payload (JSON)
- warnings (missing tags/fields/etc.)
Filament UI offers:
- warning icons
- JSON pretty blocks
- full detail view
π Examples β Automation on User Registration
1. Create automation in Filament:
- Event:
user.registered - List: βMain Listβ
- Tags: βNew Userβ
- Custom fields:
LANGUAGE = {user.profile.language}
- System fields:
firstName = {user.name}
2. Trigger from your application:
use XaviCabot\FilamentActiveCampaign\Facades\ActiveCampaignAutomations; public function register(Request $request) { $user = User::create([...]); ActiveCampaignAutomations::trigger('user.registered', $user); return $user; }
β€οΈ Contributing
PRs are welcome, especially for:
- new actions
- templates
- automation presets
- improvements to the runner or logging
π License
MIT License.