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

NeedPreferWhy
React inside the same app (side effects, sync another table)Eloquent model hook or Event::listenSame request / same deploy; full model access
Slow / unreliable / external HTTPQueued JobRetries, Horizon, isolation from the admin request
Cron / recurringroutes/console.php ScheduleAlready runs collections:process-schedules, cleanups, sync
Agency / Make / n8n / static rebuildOutbound webhookSigned HTTP; no custom PHP on the CMS host
“Publish at 09:00” / unpublishBuilt-in scheduled publishpublish_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.*, ping via OutboundWebhookDispatcher / DeliverOutboundWebhookJob (catalog).
  • Scheduled publish — versioned items; admin UI + every-minute schedule.
  • AI / import jobs — e.g. ImportCollectionJob on 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 ideaExterna
Filter → webhookOutbound webhook + Make/n8n filter on type
Filter → run scriptJob / model hook in app/
Schedule triggerSchedule:: in routes/console.php
Publish laterBuilt-in publish_at / unpublish_at
Nested Flow UINot provided — code or external automation
Previous
Queues & scheduler