Platform
Learn
Developer docs User guide Quickstart Blog
Company
Services About Contact Links Get started

Content event triggers

Updated

Content event functions run your run(payload) code after a content item is created or updated, and immediately before one is deleted. They run in the background, so editors and API clients never wait for them. Use them to send notifications, call other services or keep related data in step. This page covers when the events fire, the payload you receive, and what happens when something goes wrong.

A working example

Create a function with the trigger Content item created, mark it active, and save this code. It emails you when a comments item is created.

// Trigger: Content item created. Emails you when a "comments" item is created.
function escapeHtml(s) {
  return String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}

function run(payload) {
  if (payload.ContentType.DeveloperName !== "comments") {
    return;                                   // this trigger fires for every content type
  }
  var comment = payload.PublishedContent;     // raw stored values: strings, numbers, booleans
  var message = EmailMessage.From(
    "New comment",
    "<p>" + escapeHtml(comment.body) + "</p><p>Item id: " + payload.Id + "</p>",
    "[email protected]",
    CurrentOrganization.SmtpDefaultFromAddress,
    CurrentOrganization.SmtpDefaultFromName
  );
  Emailer.SendEmail(message);
}

The function has to check the content type itself. Raytha runs every active function with this trigger for every content item that is created, whatever its type.

When each event fires

TriggerFires afterWhen it runs
content_item_createdA content item is created in the admin, through the REST API, in a batch, or by API_V1.CreateContentItem.After the item is saved.
content_item_updatedAn item's content is saved, its settings (template or route path) are saved, or its template is reassigned in bulk.After the change is saved.
content_item_deletedAn item is moved to the trash, singly or in bulk.Immediately before the delete is saved, so the item and its template still exist.

No event fires for unpublish, discard draft, set as home page, restore from the trash, or revert. Saving as a draft still counts as an update.

Payload

payload is the content item as a plain JavaScript object. Its keys are PascalCase, unlike the REST API, and ids are strings. This is an abridged example:

{
  "Id": "1vVCcIYeeE-dMQjbYGq_ng",
  "IsPublished": true,
  "IsDraft": false,
  "ContentTypeId": "2nWDdJZffF-eNRkcHhr_oh",
  "ContentType": { "DeveloperName": "comments", "LabelSingular": "Comment", "LabelPlural": "Comments" },
  "WebTemplateId": "3oXEeKaggG-fOSldIis_pi",
  "RouteId": "4pYFfLbhhH-gPTmeJjt_qj",
  "RoutePath": "comments/1vvccciyeeedmqjbygqng",
  "PrimaryField": "",
  "PublishedContent": { "title": "Great post", "body": "Thanks for <writing> this" },
  "DraftContent": { "title": "Great post", "body": "Thanks for <writing> this" },
  "CreatorUser": { "Id": "5qZGgMciiI-hQUnfKku_rk", "FirstName": "Ada", "LastName": "Lovelace", "EmailAddress": "[email protected]", "FullName": "Ada Lovelace" },
  "CreationTime": "2026-10-02T16:58:50.394254",
  "LastModifierUser": null,
  "LastModificationTime": null
}
KeyMeaning
Id, ContentTypeId, WebTemplateId, RouteIdIds, as strings.
ContentTypeThe content type: DeveloperName, LabelSingular, LabelPlural, its fields and more.
WebTemplateThe template object, including its source. Large, and rarely needed.
IsPublished, IsDraftPublish state.
RoutePathThe item's public path, without a leading slash.
PublishedContent, DraftContentThe stored field values keyed by field developer name.
CreatorUser, LastModifierUserWho created and last changed the item, or null.
CreationTime, LastModificationTimeUTC timestamps.
PrimaryFieldThe event path does not fill it in, so expect an empty string. Read the title from PublishedContent instead.

PublishedContent holds the raw stored values, such as {"title": "Hello", "rank": 5}. This differs from API_V1 and the REST API, which wrap each value as {value, text, hasValue}. Dates are ISO strings, multiple select values are arrays, and a relationship is the related item's id.

Background execution

  • Raytha stores a background task for each matching function. A worker picks it up within a second or so. The number of workers is NUM_BACKGROUND_WORKERS, 4 by default.
  • The function runs run(payload). Its return value is ignored.
  • The function shares the RAYTHA_FUNCTIONS_MAX_ACTIVE pool with HTTP functions. It waits up to RAYTHA_FUNCTIONS_QUEUE_TIMEOUT for a slot and is stopped after RAYTHA_FUNCTIONS_TIMEOUT.
  • A failure does not affect the save that caused it. Raytha records Error running Raytha Function and the error on the background task, and does not retry.
  • There is no web request. CurrentUser is not authenticated, and you cannot return anything to anyone. Use CurrentOrganization, Emailer and HttpClient as normal.

If you need confirmation that a handler ran, write a result somewhere you can read: send an email, call a service, or create a content item.

Loops

Raytha has no recursion guard. A function on content_item_updated that edits an item through API_V1.EditContentItem raises content_item_updated again, which runs the function again. Two functions can also trigger each other through different content types. If you write back to content, compare the new value with the stored one first, and skip the write when nothing would change. Prefer writes that go to a different content type than the one that triggered you, and check the content type at the top of the function.

Gotchas

  • Filter by content type. The trigger fires for every type. Start the function with if (payload.ContentType.DeveloperName !== "...") return;.
  • Calls that look up an item by id may fail here. GetContentItemById, EditContentItem, EditContentItemSettings, UnpublishContentItem, DeleteContentItem and GetContentItems with a view id check the item against the current web request's route. In a background task there is no request, and from reading the source these calls are expected to throw a null reference error. That has not been run against a live site. CreateContentItem and list calls without a view id do not use the check. Test the call you need before you build on it.
  • Raw values. Do not expect .Value or .Text on PublishedContent fields in the payload.
  • Deleted is raised before the delete is saved, but your function runs later, when the item is already in the trash. Use the payload, not a lookup.
  • Bulk operations queue one task per item. Deleting 500 items queues 500 runs. Keep the function cheap.
  • Imports. Whether a CSV import raises created events was not verified. Do not depend on it.

Next steps