There are three ways to automatically have your plugin called when data changes.

  • Preprocessors: called before the data is stored, with the given input. Gives you a chance to set defaults or validate values.
  • Triggers: called while the data is being stored, with the previous value (null if this is a create) and the new value (null if this is a delete).
  • Event handlers: called after the data is stored.

To make your plugin be a preprocessor, trigger, or event handler, define AdPreprocessorType, AdTriggerType, or AdEventType, respectively, to be the name (or list of names) of the types for which you want to be notified.


A single plugin can be a preprocessor, trigger and event handler, if desired.

Preprocessors are limited to processing the given records, and they do not need to include all attributes (existing ones will be merged automatically). This means that in practice, the choice comes down to writing your plugin as a trigger or event handler.

  Triggers Event handlers
Advantages Can modify or cancel changes Given changes as a batch; does not stall other writes
Disadvantages Performance-sensitive; given one record at a time Cannot alter or cancel the modification; not guaranteed to run

Triggers block storage of other records of that type, so they must be written to be fast. In particular, they should not contact outside services or run any code that may block. On the other hand, event handlers run after the fact, asynchronously (on a background thread).


In some cases, it is best to make a plugin do both: the trigger methods are called for the in-line updates, and the event handler is called afterwards to do any cleanup or downstream work.

For instance, to define a trigger for Package.Type1, in a plugin called foo.plugin, the foo/plugin.cfg file would consist of the following:

AdTriggerType = Package.Type1

To be a trigger for two types, your config file would be:

AdTriggerType := { "Package.Type1", "Package.Type2" }


Note that this uses the extended syntax to define a list: the value starts with =, and uses the alternate : separator. It is written as := simply for convenience. It could also be written like this: AdTriggerType = ={ "Package.Type1", "Package.Type2" }

When a notification plugin is loaded, the type itself is automatically updated with a list of plugins that are monitoring it, in the CurrentTypePreprocessors, CurrentTypeTriggers, and CurrentTypeEvents attributes.

That would result in the following attribute on Package.Type1, for the example above:

CurrentTypeTriggers = { "foo.plugin" }

In some cases, you may not be able to enumerate the type name(s) in advance. In that case, you can specify an expression that matches the types rather than the list of type names. You can define this expression on one of AdPreprocessorTypesMatching, AdTriggerTypesMatching, or AdEventTypesMatching as appropriate. This expression is evaluated on each type ad, and if it evaluates to true, your plugin will get notified for that type.

As an example, if you want to be notified when there are any changes made to records whose type has Scheduled set to true, you would put this in your plugin configuration:

AdEventTypesMatching := Scheduled == true

In this case, any type that has Scheduled defined to be true will have this plugin called as an event handler. Remember: the expression is evaluated on the type not the records of that type.


This example is actually used in CycleServer to create, update or delete a matching timer for each record.