This page is a deep technical guide to the Event Bus and Event Handlers in the Liteed Platform. For a UI walkthrough, see the Platform User Manual: Events.
Understanding events and event handlers
An Event is a recorded occurrence emitted by a product or service. It has a name, a business payload that carries data, and a processing status. Event Handlers react to events with the same name. A handler can notify people, call external systems, or both, depending on its type and configuration.
Event lifecycle
- Emit → a service records the event with name, payload, and optional meta.
- Select → the dispatcher finds enabled handlers for that name, then applies the selector.
- Execute → matching handlers run in priority order according to mode.
- Record → outcomes, retries, and exports are persisted for observability.
Meta envelope
Events may include a stable meta envelope that is purpose-built for routing. It decouples matching logic from the free-form business payload.
meta.versionnumber for the envelope (start at 1).meta.traitscanonical attributes likeappKey,origin,language.meta.tagsfree-form labels for segmentation like["vip","pilot"].meta.aliasesoptional map to derive traits from payload without schema changes.
Selectors
A selector decides when a handler should run. Selectors use a small DSL with a version and a list of predicates combined with AND.
{ "version": 1, "all": [ { "path": "meta.traits.appKey", "operator": "equals", "value": "store-bot" } ] }Operators
equals,notEqualsinList(value in array)matchesRegex(ECMAScript syntax)exists(path presence and not null)lessThan,greaterThan(numeric or lexicographic where applicable)
Paths can reference meta.traits.*, meta.tags, or payload.*.
Handler types
- notify → sends notifications.
- httpCall → executes a step pipeline to call external systems.
Execution modes and priority
- mode:
fire-and-continueorstop-on-match. - priority: lower number runs earlier. Default is 100.
Reference
Event
namestring, for examplechatbot.widget.requestCallbackpayloadobjectmeta?object with:versionnumber, start at 1traits?object (stable attributes)tags?array of stringsaliases?map of alias name to JSONPath-like string into payload
statusone of queued, processing, done, dead
Event Handler
eventNamestringtypeone ofnotify,httpCalldescription?short human text at top levelenabledbooleanmode?fire-and-continueorstop-on-matchpriority?number, lower runs firstselectorobject:versionnumber, start at 1all?array of predicates- predicate:
{ path, operator, value? }
configobject:- notify:
{ notify: { email?: string[], internal?: string[] } } - httpCall:
authone of none, staticBearer, basic, apiKey, webhookKey, oauth2defaultHeaders?map of header → value or SourceRefstepsarray of typed stepsexports?array of{ name, from: SourceRef }
- notify:
httpCall steps
httpRequest:{ kind: "httpRequest", id, baseUrl, path, method, headers?, query?, body?, expectJson?, saveAs?, onStatus? }assign:{ kind: "assign", id, saveAs, value: SourceRef }
SourceRef can read from event, a const literal, or a previous step result, for example: { from: "event", path: "payload.customer.email" } or { from: "step", stepId: "createLead", path: "body.id" }.
Examples
Notification handler (minimal)
{
"type": "notify",
"description": "Notify sales on callback",
"selector": {
"version": 1,
"all": [
{
"path": "meta.traits.appKey",
"operator": "equals",
"value": "store-bot"
}
]
},
"config": {
"notify": {
"email": [
"sales@example.com"
]
}
}
}Selector: match any event for a name
{
"version": 1
}Selector: only a specific chatbot (by appKey)
{
"version": 1,
"all": [
{
"path": "meta.traits.appKey",
"operator": "equals",
"value": "store-bot"
}
]
}Selector: tags and segmentation
Use inList to target events labeled with specific tags like vip or pilot.
{
"version": 1,
"all": [
{
"path": "meta.tags",
"operator": "inList",
"value": [
"vip",
"pilot"
]
}
]
}httpCall: single step CRM intake
{
"type": "httpCall",
"description": "Forward lead to CRM",
"selector": {
"version": 1,
"all": [
{
"path": "meta.traits.appKey",
"operator": "equals",
"value": "store-bot"
},
{
"path": "payload.customer.phone",
"operator": "exists"
}
]
},
"config": {
"auth": {
"type": "staticBearer",
"token": "{{secret:crmApiKey}}"
},
"defaultHeaders": {
"Content-Type": "application/json",
"X-Source": "liteed-platform",
"X-App": "{{event.meta.traits.appKey}}"
},
"steps": [
{
"kind": "httpRequest",
"id": "createLead",
"baseUrl": "https://api.example.com",
"method": "POST",
"path": "/intake",
"headers": {
"Authorization": "Bearer {{secret:crmApiKey}}"
},
"body": {
"name": "{{event.payload.customer.name}}",
"email": "{{event.payload.customer.email}}",
"phone": "{{event.payload.customer.phone}}",
"source": "{{event.meta.traits.appKey}}"
},
"expectJson": true,
"saveAs": "leadResp",
"onStatus": {
"retryOn": [
429,
500,
502,
503
],
"maxRetries": 3,
"backoffMs": 1200
}
}
],
"exports": [
{
"name": "externalId",
"from": {
"from": "step",
"stepId": "leadResp",
"path": "body.id"
}
}
]
}
}httpCall: multi-step with OAuth2 and chaining
{
"type": "httpCall",
"description": "Create contact then attach note",
"selector": {
"version": 1,
"all": [
{
"path": "meta.traits.appKey",
"operator": "equals",
"value": "support-bot"
}
]
},
"config": {
"auth": {
"type": "oauth2",
"grantType": "client_credentials",
"tokenUrl": "https://auth.example.com/oauth/token",
"clientId": "{{secret:clientId}}",
"clientSecret": "{{secret:clientSecret}}",
"scopes": [
"contacts.write"
]
},
"steps": [
{
"kind": "httpRequest",
"id": "createContact",
"method": "POST",
"baseUrl": "https://api.example.com",
"path": "/contacts",
"body": {
"email": "{{event.payload.customer.email}}",
"name": "{{event.payload.customer.name}}"
},
"expectJson": true,
"saveAs": "contact"
},
{
"kind": "assign",
"id": "noteText",
"saveAs": "note",
"value": {
"from": "const",
"value": "Requested a live agent via chatbot."
}
},
{
"kind": "httpRequest",
"id": "attachNote",
"method": "POST",
"baseUrl": "https://api.example.com",
"path": "/contacts/{{steps.createContact.body.id}}/notes",
"body": {
"text": "{{steps.note}}"
},
"expectJson": true
}
],
"exports": [
{
"name": "contactId",
"from": {
"from": "step",
"stepId": "createContact",
"path": "body.id"
}
}
]
}
}Best practices
- Route on
meta.traitsormeta.tagsrather than on raw payload whenever possible. - Keep handlers small and focused; use multiple handlers with
fire-and-continuefor fan-out. - Use Secrets Vault placeholders; values are injected server side from the database.
- Configure retries only for transient errors like 429 and 5xx.
User manual section
For hands-on instructions on creating, editing, enabling, and deleting handlers in the UI, see Platform User Manual: Events. This technical page focuses on definitions and configuration. The user manual page links back here when you need exact config details.
Supported event names
chatbot.widget.provideContactschatbot.widget.requestCallbackchatbot.widget.customFormSubmitchatbot.widget.requestLiveAgent