Skip to content

Writing Handlers

A handler is a PHP file that returns a closure. The closure receives a single argument — an AutomationContext — and runs when any of the automation’s triggers fire.

<?php
return function ($ctx) {
// ... your logic ...
return ['ok' => true];
};

The handler is stored in its own .php file next to the automation object (an external code field), so it’s edited like any code field in the admin but never bloats the collection JSON. It travels with the object through Sync and JumpStart.

$ctx is the only thing a handler receives — pre-wired services plus the trigger payload. No need to reach into a container.

Objects

PropertyWhat it is
$ctx->objectFetcherRead objects — fetchObject($collection, $id)
$ctx->objectSaverCreate objects — saveObject($collection, $data)
$ctx->objectUpdaterUpdate objects — updateObject($collection, $id, $data)
$ctx->objectRemoverDelete objects — deleteObject($collection, $id)
$ctx->objectClonerDuplicate an object — cloneObject($from, $to)
$ctx->propertyIncrementerAtomic counters — incrementProperty($collection, $id, $property, $amount = 1) / decrementProperty(...)

Deck items (cards/repeaters inside an object)

PropertyWhat it is
$ctx->deckItemSaverAdd a deck item — saveDeckItem($collection, $objectId, $property, $itemId, $itemData)
$ctx->deckItemUpdaterUpdate a deck item — updateDeckItem(...)
$ctx->deckItemRemoverRemove a deck item — removeDeckItem($collection, $objectId, $property, $itemId)
$ctx->deckItemFetcherRead deck items — fetchDeckItem(...), fetchAllDeckItems($collection, $objectId, $property)

Querying

PropertyWhat it is
$ctx->indexReaderRead a collection index — fetchIndex($collection) (returns a filterable collection)
$ctx->indexSearcherFull-text-ish search — search($collection, $query), searchByProperty($collection, $property, $query)
$ctx->indexQueryServiceStructured query (filter/sort/paginate) — query($collection, $params)
$ctx->indexBuilderRebuild an index after batch writes — buildIndex($collection)

Collections & schemas

PropertyWhat it is
$ctx->collectionFetcherInspect a collection — fetchCollection($id), collectionExists($id)
$ctx->schemaFetcherInspect a schema — fetchSchemaForCollection($collection), schemaExists($id)

Files & images

PropertyWhat it is
$ctx->fileSaverStore a file into a file field — save(...)
$ctx->imageSaverStore/process an image into an image field — save(...)

Import & sync

PropertyWhat it is
$ctx->csvImporterImport a CSV — import($collection, $file, $updateObject = false)
$ctx->jsonImporterImport JSON — import($collection, $file, $updateObject = false)
$ctx->rssImporterIngest an RSS feed — import($feedUrl, $collection, $options = [])
$ctx->syncServicePush/pull to another T3 install — push(...), pull(...)

Mail, config & run payload

PropertyWhat it is
$ctx->mailerSend a Mailer email — sendEmail($mailerId, $data)
$ctx->configCore configuration
$ctx->loggerPSR-3 logger writing to jobs.log (channel automations)
$ctx->triggerThe trigger row that fired this run (e.g. $ctx->trigger['type'])
$ctx->argsCaller inputs — webhook query + body, or manual Run now args
$ctx->eventEvent payload (collection, id) — event triggers only
$ctx->requestThe PSR-7 request — webhook triggers only

$ctx->request and $ctx->event are null for schedule runs.

<?php
return function ($ctx) {
$posts = $ctx->indexReader->fetchIndex('blog')->objects
->where('draft', false)
->take(5);
$ctx->mailer->sendEmail('weekly-digest', ['posts' => $posts->all()]);
$ctx->logger->info('weekly digest sent', ['count' => $posts->count()]);
return ['sent' => $posts->count()];
};
  • Return value — whatever the closure returns is recorded on the run record and, for a synchronous webhook, becomes the HTTP response body. Return arrays/scalars (JSON-encodable).
  • Throwing marks the run failed. In development the error surfaces loudly; in production it’s contained, optionally emailed via the automation’s error mailer, and counts toward auto-disable.

When you save a handler, the editor scans it for high-risk calls — exec, shell_exec, system, passthru, proc_open, eval, backticks, base64_decode, remote file_get_contents, and similar — and shows a non-blocking advisory listing what it found. It’s a heads-up, not a block: many legitimate handlers use these. Review before relying on a handler that reaches for them.