Operations
Automation without Flows
Externa will not ship a visual Flow / no-code automation canvas. Operators automate with Laravel (listeners, jobs, schedule) and outbound webhooks. This page is the Directus-migrant map.
Choose a tool
| Need | Prefer | Why |
|---|---|---|
| React inside the same app (side effects, sync another table) | Eloquent model hook or Event::listen | Same request / same deploy; full model access |
| Slow / unreliable / external HTTP | Queued Job | Retries, Horizon, isolation from the admin request |
| Cron / recurring | routes/console.php Schedule | Already runs collections:process-schedules, cleanups, sync |
| Agency / Make / n8n / static rebuild | Outbound webhook | Signed HTTP; no custom PHP on the CMS host |
| “Publish at 09:00” / unpublish | Built-in scheduled publish | publish_at / unpublish_at + collections:process-schedules — not a Flow |
Non-goal: a drag-and-drop Flow builder. Fork or wrap Externa if you need one.
Out of the box
- Outbound webhooks —
item.*,collection.*,file.*,pingviaOutboundWebhookDispatcher/DeliverOutboundWebhookJob(catalog). - Scheduled publish — versioned items; admin UI + every-minute schedule.
- AI / import jobs — e.g.
ImportCollectionJobon the queue.
For “when item updates → Slack / rebuild / CRM”, start with webhooks before writing PHP.
Skeleton: model saved listener
Core emits webhooks from writers (CollectionItemValuesWriter, CollectionItemPublisher), not a dedicated ItemUpdated domain event. For in-process automation, observe the model (or listen after you define your own event):
// app/Providers/AppServiceProvider.php (boot)
use App\Models\CollectionItem;
CollectionItem::saved(function (CollectionItem $item): void {
if ($item->wasRecentlyCreated) {
return; // or handle create separately
}
// Fast, safe work only — queue anything slow:
\App\Jobs\SyncItemToLegacyCrm::dispatch($item->id);
});
Or a class-based listener on a custom event you fire from an observer — same pattern as Laravel docs. Keep CMS upgrades clean: put custom code in app/ (or a package), not in vendor.
Skeleton: queued job
<?php
namespace App\Jobs;
use App\Models\CollectionItem;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
class SyncItemToLegacyCrm implements ShouldQueue
{
use Queueable;
public function __construct(public int $itemId) {}
public function handle(): void
{
$item = CollectionItem::query()->find($this->itemId);
if ($item === null) {
return;
}
// HTTP / SDK call…
}
}
Require a worker (queue:listen / Horizon) — see Operations. Outbound webhook delivery already uses DeliverOutboundWebhookJob the same way.
Skeleton: schedule
routes/console.php already schedules Externa maintenance. Add your own command next to it:
use Illuminate\Support\Facades\Schedule;
Schedule::command('collections:process-schedules')->everyMinute();
// Your nightly digest, etc.
Schedule::command('app:send-editorial-digest')->dailyAt('07:30');
Production: cron → php artisan schedule:run every minute (or Laravel’s scheduler systemd unit).
Webhooks instead of Flows
Point Project settings → Webhook URL at Make / n8n / Zapier / your API. Filter on type (item.updated, item.published, …). Verify X-Externa-Signature. Recipes: Webhooks · Rebuild / purge.
vs Directus Flows (cheat sheet)
| Directus Flow idea | Externa |
|---|---|
| Filter → webhook | Outbound webhook + Make/n8n filter on type |
| Filter → run script | Job / model hook in app/ |
| Schedule trigger | Schedule:: in routes/console.php |
| Publish later | Built-in publish_at / unpublish_at |
| Nested Flow UI | Not provided — code or external automation |