About functions
What OmniLab functions are, how they run, when to use one instead of a webhook, and the limits your code works within.
A function is a small piece of TypeScript that runs on OmniLab's own infrastructure. You write it in Studio and OmniLab compiles it. From then on the platform runs it for you: when something happens in a campaign, or at a decision point such as a contact signing up.
You need no server, no endpoint, and no deployment of your own.
Functions are configured under Enterprise Settings, which needs an Admin role with the Global organisation selected. Confirm you have that access before planning work: the tab is hidden without it.
How it works
You store one TypeScript file with one exported handler. When you build it, OmniLab compiles that file to a WebAssembly module and keeps the compiled version. Every invocation from then on loads it into a fresh sandbox and calls your export.
import type { PlatformEventHandler } from "./omnilab";
export const onPlatformEvent: PlatformEventHandler = (event) => {
console.log("Something happened: " + event.type);
};Your handler reaches the outside world through the calls OmniLab provides: read a config value, read a secret, make an HTTP request to a host you have allow-listed, read a contact, and write contact custom fields. The set is deliberately small, which is what makes your code safe to run on the platform. It is enough for most integration work.
Each invocation is independent. Nothing you set on one call carries over to the next. Keep what you need in contact custom fields, or in your own system over HTTP.
Functions or webhooks?
Both let you react to what happens in OmniLab. The difference is where your code runs and whether it can change the outcome.
| Function | Webhook | |
|---|---|---|
| Runs on | OmniLab's infrastructure | Yours |
| You need to host | Nothing | An HTTPS endpoint |
| Can change what OmniLab does | Yes, for one kind | No |
| Best for | Logic that must run inside an OmniLab operation, or small integrations you would rather not host | Feeding a system you already run |
A webhook tells your systems that something happened, after OmniLab has already decided it. A function runs inside OmniLab. One of the two kinds runs inside the decision itself, so it can reject a sign-up, normalise an email before it is stored, or send a verification message through your own provider.
Choose a kind
Every function is one of two kinds, and the kind decides when it runs and what it can affect. It is fixed when you create the function, so make this choice before you start writing.
| Platform event function | Hook function | |
|---|---|---|
| Runs | After something happened | During an operation, which waits for it |
| Can it change the outcome? | No | Yes — it continues or refuses |
| Your export | onPlatformEvent | on + the hook point, for example onContactCreatePre |
| Wired up by | A subscription to event types | A binding to a hook point |
| Your return value | Discarded | The decision OmniLab acts on |
| If it fails | The activity is unaffected | The operation is refused: see what a hook guarantees |
Start with a platform event function when either would work. It cannot break anything a customer is doing. Getting it wrong costs a missing side effect, not a failed sign-up.
Use a platform event function to react to activity: enrich a contact, push to your CRM, notify a channel. Use a hook function to vet a sign-up, normalise data before it is stored, or send verification mail.
Changing the kind means starting again
The kind determines the export your code must provide, which tabs appear in the editor, and the payload the test runner sends. There is no way to convert one kind into another: you create a new function and delete the old one.
From draft to running
Saving and building are separate on purpose. Saving stores your code and type-checks it; it never changes what is running. Only a build produces a new compiled version.
The status in the function list is one of Draft, Building, Ready, Build failed, or Deleted. Only a Ready function runs.
The two paths out of Building are worth knowing. A function that has never built successfully goes to Build failed. A live function that fails a rebuild returns to Ready and keeps serving its previous version. A broken rebuild never takes a working function offline.
What your code works within
Your code is one TypeScript file, user.ts. A function with an empty allow-list makes no outbound calls. Each invocation has a fixed time, memory and SDK call budget, listed under Limits.
Two rules catch people out. Secrets never go in your source — you store them in a bundle, bind the bundle to the function, and read them by name at runtime. And contact custom field keys beginning system. or feature. are reserved, so writes to them are rejected.