XeniaXenia Docs

Webhooks and actions

PreviousNext

Fire a journey from your site, read its reply, and let the reply render banners, popups and content swaps on the page.

A journey that starts with a Webhook trigger and ends in a Webhook Response node is a request and a reply. Your site calls it, the journey decides, and the reply comes back in the same request. The trigger id to call is shown on the Webhook trigger node in the builder.

Calling a journey

import { useWebhook } from "@xenia-martech/sdk-js/client/react";
 
function OfferButton() {
  const { send, response, isLoading, error } = useWebhook("<trigger id>");
  return <button onClick={() => send({ page: "pdp" })}>Show my offer</button>;
}

The Webhook Response node returns whatever JSON you design. The SDK keeps the last reply per trigger, readable with getResponse(triggerId) or the useXeniaResponse() hook.

Actions

To change the page, the reply includes an xeniaActions array. The browser SDK applies them automatically.

{
  "xeniaActions": [
    { "type": "swap", "target": "#hero", "html": "<b>Welcome back!</b>" },
    { "type": "hide", "target": ".guest-banner" },
    {
      "type": "banner",
      "message": "20% off today",
      "cta": { "label": "Shop", "url": "/sale" },
      "dismissAfterMs": 8000,
      "position": "bottom"
    }
  ]
}
ActionFieldsEffect
swap / setContenttarget, html or textReplaces an element's content
hidetargetHides an element
showtargetShows a hidden element again
banner / popup / modaltitle, message, imageUrl, html, cta, dismissAfterMs, positionShows a message over the page
inlinetarget, plus the message fieldsShows a message inside the page
redirecturlNavigates the browser
  • target is a CSS selector, or the id in a data-xenia-slot="…" attribute.
  • cta is { label, url?, event? }. Clicking a CTA with an event tracks it.
  • position is a corner for popup (top-left, top-right, bottom-left, bottom-right, default bottom-right) or an edge for banner (top, bottom, default bottom). Modals are always centered.

Messages render in an isolated shadow root, so your site's styles never leak into them, and theirs never leak out.

Your own design

The built-in message card can be restyled, replaced per message, or replaced everywhere.

Restyle with CSS variables

Every visual value on the built-in card is a CSS custom property. Set them in your own stylesheet and they reach inside the shadow root:

:root {
  --xenia-card-bg: #111827;
  --xenia-card-color: #f9fafb;
  --xenia-cta-bg: #6366f1;
  --xenia-card-radius: 16px;
}

Replace the markup

Set html on a single action to use that markup instead of the card. To replace the card everywhere, register a template once:

createXeniaClient({
  // ...
  messageTemplate: ({ action, variant, escapeHtml }) =>
    `<div class="my-${variant}">${escapeHtml(action.message ?? "")}</div>`,
  messageStyles: `.my-banner { background: #111; color: #fff; }`,
});

Inside any custom markup, data-xenia-close dismisses the message and data-xenia-cta fires the action's CTA.

Use your own component

messageComponent mounts a real component instead of an HTML string. React has a ready-made adapter:

import {
  XeniaProvider,
  reactMessageComponent,
  type XeniaMessageComponentProps,
} from "@xenia-martech/sdk-js/client/react";
 
function OfferCard({ action, close, fireCta }: XeniaMessageComponentProps) {
  return (
    <div className="offer-card">
      <p>{action.message}</p>
      {action.cta && <button onClick={fireCta}>{action.cta.label}</button>}
      <button onClick={close} aria-label="Close">×</button>
    </div>
  );
}
 
<XeniaProvider
  tenantId="<workspace id>"
  environmentKey="<environment>"
  messageComponent={reactMessageComponent(OfferCard)}
>
  <App />
</XeniaProvider>;

For other frameworks, pass messageComponent: (container, ctx) => { … } that mounts into container and returns an unmount function. Precedence is: an action's own html, then messageComponent, then messageTemplate, then the built-in card.

Spin-wheel widgets

An action with widgetType: "spin-wheel" renders a prize wheel. The prize weights and codes stay on the server, and the outcome is always decided there. The browser never guesses a result.

The spinner, win and loss screens are your own HTML, so your markup is the widget. The SDK only needs a few attributes inside them:

AttributeGoes inEffect
data-xenia-wheel-slotSpinner screenWhere the wheel is drawn
data-xenia-spinSpinner screenStarts the spin and asks the server for the outcome
data-xenia-wheel-prizeWin or loss screenFilled with the winning segment's label
data-xenia-wheel-ctaWin or loss screenFires the action's CTA, then closes
data-xenia-spin-againWin or loss screenBack to the spinner. Hidden once the visitor has no plays left
data-xenia-closeAny screenCloses the widget

A visitor who already played sees their earlier outcome again rather than a new spin.

Capturing form fields

Any widget can capture input. Mark inputs with data-xenia-field="<name>" and the submit control with data-xenia-submit. The SDK shows a spinner while the request is in flight, reveals an optional <div data-xenia-success hidden> block on success, and shows a retry message in data-xenia-error on failure.