Skip to content
Plugins

Hooks

Actions tell you something happened; filters let you change a value on its way through. Every hook Lettern fires is listed here.

On this page
PHP
$hooks = $this->cms()->hooks();

// Action: react.
$hooks->on('entry.published', function (Entry $entry, ?User $user) {
    Http::post('https://hooks.example.com/published', ['url' => $entry->uri]);
});

// Filter: return the (possibly changed) first argument.
$hooks->addFilter('entry.uri', fn (string $uri, Entry $entry) => $entry->collection === 'jobs' ? '/careers'.$uri : $uri);

Both take an optional priority (lower runs first, default 10). Prefer abilities for things people or agents should be able to do; use hooks to react and adjust.

#Actions

ActionArgumentsFired when
entry.saved$entry, $user, $actionAn entry is created, saved, published or unpublished ($action says which).
entry.published$entry, $userAn entry goes live — also when a scheduled one comes due.
entry.unpublished$entry, $userAn entry is taken offline.
entry.deleting, entry.deleted$entry, $userAround deletion.
entry.uri-changed$entry, $old, $newA live entry's URL changed.
entry.structure-changed$entry, $userAn entry moves or its position changes. Its editorial draft stays unpublished; use this hook to invalidate structural output.
entries.reordered$collection, $ids, $userSiblings were reordered.
collection.saved, collection.deleted$collection / $handleA collection definition changed.
globals.saved$setA global set's fields or values changed.
navigation.saved, form.saved$handleA menu or form changed. (Templates and components are entries: use entry.saved and entry.published.)
redirects.saved—The redirect list changed.
media.uploaded$item, $userA file was added.
media.updated, media.deleted$itemA file changed or was removed.
form.submitted$form, $data, $submissionA valid submission arrived.
comment.created$comment, $entry, $authorSomeone commented.
users.fields.saved—The user profile fields changed.
settings.saved$namespace, $valuesA settings namespace was saved (site is config/site.yaml).
design.saved—The design system or custom code changed.
site.page.built$uri, $entry, $file, $pageThe static build wrote a page.
site.page.removed$uri, $fileThe static build removed a page.
site.built$manifestA build finished.
render.begin$contextA page starts rendering.
render.head, render.foot$key, $htmlAdd markup to the page's head or end of body (deduplicated by key).
render.style$key, $css, $orderAdd CSS to the page (blocks use this).
render.depend$keyRecord that the page depends on something (entry:<id>, collection:<handle>), so the static build rebuilds it when that changes.
render.structured$key, $dataAdd JSON-LD to the page's graph.
ability.executing$ability, $input, $contextBefore any ability runs.
ability.executed$ability, $input, $result, $contextAfter it ran (letterncms/audit records changes here).
user.login$userSomeone signed in to the admin.
account.*Public accounts: signed up, verified, signed in, deleted and more. See Using accounts.
oauth.authorized$user, $clientAn agent was granted access.

#Filters

FilterValue, then argumentsLets you change
entry.creating$input, $collection, $userInput for a new entry.
entry.saving$entry, $collectionAn entry right before it is stored.
entry.uri$uri, $entry, $collectionAn entry's URL (the languages plugin adds prefixes here).
collections$collectionsThe list of collections (add code-defined ones).
collections.defaults$definitionThe initial definition returned by collections/options. The blocks plugin contributes an explicit blocks body; without it, the core starts with no body. Creators apply explicit values afterward.
collections.settings$fields, $collectionThe settings a collection offers in Collections (the site plugin adds its template picker).
ability.input$input, $ability, $contextValidated input before the handler.
ability.result$result, $ability, $input, $contextWhat an ability returns.
users.fields$fieldsThe fields of user profiles (add your own). A field with public: true can be edited on the public profile page.
gate.allows$allowed, $user, $entry, $ruleWhether someone may read a gated entry.
settings.fields$fields, $namespaceA settings namespace's fields — add to another's, or take one over (the languages plugin manages the site's language).
settings.saving$values, $namespace, $storedSettings right before they are validated and stored.
admin.manifest$manifest, $userWhat the admin receives at start-up (modules add collections, globals, the home screen and config.<namespace> here).
admin.nav$itemsSidebar items that come and go (letterncms/content adds one per collection, letterncms/settings one per settings page).
revisions.keep$countHow many revisions each entry keeps (letterncms/content makes it a setting).
agent.guide$guideThe instructions every AI agent gets.
site.head$tags, $contextTags in a page's <head>.
site.foot$tags, $contextScripts at the end of a page's <body>.
site.scope$scope, $contextThe data templates and blocks can bind to (site, entry, globals, page, pagination…). $context->mode says who the page is for: only add a signed-in person's data in request, as a build goes to a public file.
site.html$html, $contextA rendered page.
site.build.extras$extrasExtra files the static build writes (sitemap, feeds…).
site.search.record$record, $entryA page's search index record (null leaves it out).
seo.alternates$alternates, $entryhreflang alternates.
seo.sitemap$urlsAddresses your plugin answers itself, for the sitemap: ['uri' => …, 'updated' => …].
site.links.answered$answered, $pathWhether an address your plugin answers itself is real, so cms links:check accepts it.
blocks.render.data$data, $typeWhat a block's render function receives.
blocks.data-sources$sourcesLists the Repeat block can loop over — see Plugins.
blocks.scope$scopeData for blocks rendered outside a page (Markdown twins, search).
blocks.svg$svg, $sourceSanitize the custom SVG of an Icon block.
fields.options.<name>$optionsOptions of select fields with dynamic: <name> (templates, collections, timezones).
fields.definition.<name>$field, $contextResolved definition of a dynamic: <name> field. The collection host supplies $context['collection']; use it for presentation such as the inherited template name. Raw collection YAML remains unchanged.
blocks.variants$variantsBlock variants offered to editors.
blocks.folders, blocks.pattern-sources$sourcesFolders with blocks (block.json) or read-only patterns (<handle>.json).
blocks.breakpoints$breakpointsBreakpoints for block styles.
media.url, media.imgnull, $image…How images resolve and render (point them at a CDN).
form.data$data, $form, $requestA submission before it is stored.
navigation.entry, navigation.item—How menu items resolve.
i18n.translatornullThe machine translator (the assistant registers Claude).
mcp.prompts$promptsPrompts offered to MCP clients: the site's skills (cms()->skills()).