Platform event functions

Write a function that runs asynchronously after something happens in OmniLab, and subscribe it to the event types it should react to.

4 min read

A platform event function runs after the fact. Something happens in OmniLab — a touchpoint is completed, a reward is won, a contact is created — and your function is invoked with that event. It cannot change what already happened, which makes it the safe kind to start with.

When to use it

Choose a platform event function when you want to react to activity: enrich a contact after they play, push a record to your CRM, notify an internal channel when a prize is claimed. If instead you need to influence an operation while it is happening — reject a sign-up, rewrite a submitted email — you need a hook function.

The handler

Your file exports onPlatformEvent. It receives one event and returns nothing.

user.ts — hello world
import type { PlatformEventHandler } from "./omnilab";

export const onPlatformEvent: PlatformEventHandler = (event) => {
  console.log("Hello world! Received " + event.type + " (" + event.id + ")");
};

Build this, subscribe it to an event type, and every matching event shows up in the execution log with your line in the Logs panel. That is the whole loop, and it is worth confirming before you write anything more involved.

The event you receive always has the same five fields:

What your handler receives
{
  type: "touchpoint.completed.v1",
  id: "evt-...",              // unique per event
  time: "2026-05-28T14:48:31Z",
  source: "…",                // where the event came from
  data: { /* the event payload */ }
}

Doing something useful

Because the return value is discarded, a platform event function has an effect only by calling out through the SDK. This one tags the contact with the last touchpoint they finished:

user.ts — write a contact custom field
import { OmniLab } from "./omnilab";
import type { PlatformEventHandler } from "./omnilab";

type TouchpointCompleted = {
  contact?: { contact_id?: string };
  touchpoint_title?: string;
};

export const onPlatformEvent: PlatformEventHandler = async (event) => {
  const data = event.data as TouchpointCompleted;
  const contactId = data.contact?.contact_id;

  if (!contactId) {
    console.log("No contact on " + event.id + ", nothing to do");
    return;
  }

  await OmniLab.Contact.setCustomField(
    contactId,
    "last_touchpoint",
    data.touchpoint_title ?? "unknown",
  );
};

event.data is the raw event payload, so cast it to the shape you expect and read defensively — fields differ between event types. The event reference lists what each type carries. For a complete sample of any type the picker lists, open the Test tab and use Load example.

The same event can reach your handler twice, so deduplicate on event.id before any external side effect.

Subscribe it to events

A built function runs only once you subscribe it to event types, and only for events that carry an identified contact.

In the function's editor:

  1. Select the Subscriptions tab.

    Touchpoint completion logger with the Subscriptions tab highlighted

  2. Select Add subscription.

    Subscriptions tab with the Add subscription button highlighted

  3. Enter the Display name, here Loyalty award trigger.

    Subscription form with the Display name Loyalty award trigger, highlighted

  4. Under Event types, choose Completed in the Touchpoint group.

    Subscription form with Event types set to Completed, highlighted

    Leave Event types empty to match every event.

  5. Select Create.

    Subscription form with the Create button highlighted

The subscription starts Active and appears in the list with its event types and filters. The picker groups events as Touchpoint, Reward, Contact and Other, and you can search it. Leave out Expired in the Reward group: the platform never sends it. The event reference shows which types reach a function, listed or not.

A function can carry several subscriptions. The tab warns you when an event type reaches its subscription cap.

Add an event type the picker doesn't list

In the subscription form:

  1. Enter the type in the custom event field, below Event types.

    Subscription form with my.custom.event.v1 in the custom event field, highlighted

  2. Select Add custom.

    Subscription form with the Add custom button highlighted

Booking, ticket, check-in and receipt submission events reach a function this way: copy their names from the event reference. Any other type saves too, even one the platform never sends, so a misspelt name never fires. Only touchpoint.page_visit.v1 is refused, when you select Create.

Narrow a subscription with filters

Filters are combined with AND and must match exactly. With no filters, every event of the selected types triggers the function.

Some booking and ticket events belong to no organisation: their group_context is empty. They reach only subscriptions with no Group filter.

In the subscription form:

  1. Select Add filter.

    Subscription form with the Add filter button highlighted

  2. Choose the key, here Interaction. The others are Tenant, Group and Contact.

    Filter row with the key Interaction, highlighted

  3. Enter the exact id to match, here the campaign's interaction.interaction_id from the payload.

    Filter row with an interaction id as the value, highlighted

  4. Select Create, or Save when you edit a subscription.

    Subscription form with the Create button highlighted

A subscription's status can't be changed

The Status field appears when you edit a subscription, but saving a change to it has no effect, and the form says so. To stop a subscription, delete it, and create it again when you need it back.

Failure and retries

Your function's failures do not affect the activity that produced the event — the operation has already completed by the time you are invoked.

What happenedWhat OmniLab does
Your handler throwsThe failure is recorded as an execution with a non-zero exit code, and is not retried — the same code would throw again
A transient problem on OmniLab's sideRetried up to 5 times after the first attempt, waiting longer each time
A permanent problem, such as the function having been deletedStops being retried, because retrying cannot help

If something's blocked

  • The Subscriptions tab is missing: the tab only appears on platform event functions. Check the kind badge at the top of the editor.
  • A subscription exists but the function never runs: confirm the function status is Ready — a Draft function is skipped entirely — and that the subscription is Active. Then check that your filters are not excluding everything, and that the event carries an identified contact.
  • A custom event type never fires: the platform doesn't send that type. A misspelt name saves without complaint, so check it against the event reference.
  • The subscription is refused when you select Create: it names touchpoint.page_visit.v1, which functions can never subscribe to.
  • It ran, but nothing changed: remember the return value is discarded. Check the Logs and the SDK call results in the execution detail — an EGRESS_BLOCKED, PERMISSION_DENIED or NOT_FOUND error there is the usual reason a handler runs cleanly but has no effect.

Next steps

On this page