Skip to Content
FeaturesLifecycle Hooks

Lifecycle Hooks

Lifecycle hooks trigger operations automatically when content changes. When a record is created, updated, deleted, published, or unpublished, Foir can execute one or more operations in response.

Overview

Hooks connect content lifecycle events to operations. When an event fires on a model, Foir executes the configured operations with the full record context.

Page published -> Execute "notify-slack" operation (async) -> Execute "sync-cdn" operation (async) -> Execute "update-search-index" operation (sync)

Lifecycle Events

Record events — fired when content changes:

EventWhen It Fires
RECORD_CREATEDA new record is created
RECORD_UPDATEDA record is updated
RECORD_DELETEDA record is deleted
RECORD_PUBLISHEDA version is published (goes live)
RECORD_UNPUBLISHEDA published version is unpublished

Operation events — fired when an operation execution reaches a terminal state. Useful for chaining follow-up work (notify Slack when a long-running export finishes, retry a related job, reconcile external state, etc.):

EventWhen It Fires
OPERATION_COMPLETEDOperation finished successfully; payload carries executionId, operationKey, status, and the typed result.
OPERATION_FAILEDOperation finished with an error; payload includes the error {code, message, retryable}.
OPERATION_CANCELLEDOperation was cancelled (by admin or by the extension itself) before completing.
OPERATION_TIMED_OUTAn async-callback operation didn’t call back within callbackTtlSeconds and the watchdog reaped it.

Hook Configuration

Each hook specifies:

FieldDescription
operationKeyThe operation to execute
asynctrue to dispatch the hook’s operation without blocking the originating event, false to wait for the operation to complete before the event call returns. Orthogonal to the operation’s own execution mode — you can set async: true on a hook that triggers a sync operation (fire the op, don’t wait) or async: false on a hook that triggers an async-callback operation (wait for the callback before returning).
conditionOptional rule expression to conditionally execute
inputOptional static input data passed to the operation

In the Admin

Configuring Hooks

  1. Go to Settings > Models
  2. Select a model (e.g., “Blog Post”)
  3. Click Lifecycle Hooks
  4. Add triggers for the desired events:
    • Select the event (e.g., RECORD_PUBLISHED)
    • Choose the operation to execute
    • Set async mode
    • Optionally add a condition
  5. Save and publish the model

Creating an Operation for Hooks

Before adding a hook, you need an operation to target:

  1. Go to Automation > Operations
  2. Click Create Operation
  3. Configure the key, endpoint URL, and touch points (select “Record” for lifecycle hooks)
  4. Save

See Operations for the full guide.

Execution Modes

ModeBehaviorUse Case
Async (async: true)Enqueued to a background job queue with automatic retriesNotifications, external syncs, non-critical tasks
Sync (async: false)Executed inline, result awaited before the operation completesValidation, critical transformations

Async operations retry automatically if the target endpoint is temporarily unavailable.

Via the CLI

# List all hooks foir hooks list # Get a hook by key or ID foir hooks get <keyOrId> # Create a hook foir hooks create --data '{ "key": "notify-on-publish", "name": "Notify on Publish", "event": "RECORD_PUBLISHED", "modelKey": "blog-post", "operationKey": "slack-notify-publish", "async": true }' # Update a hook foir hooks update <id> --data '{"async": false}' # Delete a hook foir hooks delete <id> # List recent deliveries for a hook (filter by status) foir hooks deliveries <hookId> --status failed --limit 20 # Retry a failed delivery foir hooks retry-delivery <deliveryId> # Send a synthetic delivery to verify the receiver foir hooks test <hookId> --data '{"recordId": "rec_abc123"}'

See foir hooks for the full subcommand and option reference.

Via the API

Operations triggered by hooks receive a standardized payload containing:

FieldDescription
operationKeyWhich operation is being called
trigger.typelifecycle
record.idThe record ID
record.modelKeyThe model key
record.contentThe record’s current content
context.projectIdProject ID
context.userIdUser who triggered the event
context.timestampWhen the event occurred

See Operations for the full HTTP contract and response format.

Config System

Hooks can also be defined in foir.config.ts using defineHook. See the Configuration reference for details.

Common Patterns

Slack Notification on Publish

Create an operation that POSTs to a Slack webhook, then add it as a hook on the RECORD_PUBLISHED event:

{ "event": "RECORD_PUBLISHED", "operationKey": "slack-notify-publish", "async": true }

External System Sync

Keep an external system in sync by triggering on create, update, and delete. Create separate hooks for each event, all pointing to the same sync operation:

[ { "event": "RECORD_CREATED", "operationKey": "sync-to-external", "async": true }, { "event": "RECORD_UPDATED", "operationKey": "sync-to-external", "async": true }, { "event": "RECORD_DELETED", "operationKey": "delete-from-external", "async": true } ]

Conditional Webhook

Only notify when content meets specific criteria. Use a condition to filter which records trigger the hook:

{ "event": "RECORD_PUBLISHED", "operationKey": "notify-uk-team", "async": true, "condition": { "type": "condition", "left": { "type": "field", "path": "market" }, "operator": "equals", "right": { "type": "literal", "value": "uk" } } }

Best Practices

  • Use async for external calls — do not slow down content operations with network latency.
  • Add retry policies on operations — configure retries for resilience against transient failures.
  • Use conditions sparingly — simple hooks are easier to debug.
  • Monitor deliveries — check the hook deliveries list for failures.
  • Test with draft content — use foir hooks test to verify hooks work before going live.
  • Document hook purpose — use operation descriptions to explain why each hook exists.
Last updated on