---
title: "Action templates"
description: "Build vertical assistants with action configs, reusable cards, forms, chips, template manifests, and a safe server-verified execution model."
---

Action templates turn the widget from a text-only assistant into a guided,
interactive surface: buttons, cards, forms, confirmations, status trackers, and
vertical presets such as docs, lead capture, services booking, restaurant,
ecommerce, and travel.

This is a foundation for assistants that can **do useful work** without each app
forking the widget or inventing one-off UI. The core rule is simple: build
verticals from generic primitives, then wire the actual business action on the
server side.

> **Note**
>
> Client-side action templates are supported today. Hosted execution of arbitrary
> customer webhooks (tenant isolation, SSRF guards, idempotency, secrets, audit
> logs, rate limits, confirmation policies) is not yet available — implement
> server-side execution yourself in the meantime.

## What ships in the widget

Import React primitives from the main package entrypoint, and import server-safe
manifest data/types from the `./actions` subpath when you are in server code,
dashboard code, docs tooling, or any place that should not pull in the client
component entry.

```tsx
import {
  ActionButton,
  ActionChips,
  ActionForm,
  ConfirmationCard,
  EntityCard,
  EntityCarousel,
  StatusTracker,
  SummaryCard,
} from '@mordn/chat-widget';
import {
  leadCaptureTemplate,
  type MordnActionDispatcher,
} from '@mordn/chat-widget/actions';
```

The contract is intentionally small:

```ts
type MordnActionHandler = 'client' | 'server' | 'hosted';
type MordnActionLoadingBehavior = 'auto' | 'none' | 'self' | 'container';
type MordnActionConfirmationPolicy = 'none' | 'recommended' | 'required';
type MordnActionRiskLevel = 'ui' | 'read' | 'capture' | 'mutation' | 'regulated';

interface MordnActionConfig<TPayload = unknown> {
  type: string;                 // e.g. lead.capture, ecommerce.cart.add
  payload?: TPayload;           // untrusted until the server validates it
  handler?: MordnActionHandler;
  loadingBehavior?: MordnActionLoadingBehavior;
  confirmation?: MordnActionConfirmationPolicy;
  idempotencyKey?: string;
  label?: string;
  risk?: MordnActionRiskLevel;
  analyticsName?: string;
  schema?: MordnActionSchema;
}

type MordnActionDispatcher = (event: MordnActionEvent) => Promise<MordnActionResult | void> | void;
```

## Example: lead capture

Use an action form to collect values, then hand the event to your host code. The
host still validates and persists server-side.

```tsx
import { ActionForm } from '@mordn/chat-widget';
import { leadCaptureTemplate, type MordnActionDispatcher } from '@mordn/chat-widget/actions';

const onAction: MordnActionDispatcher = async ({ action, values }) => {
  if (action.type !== 'lead.capture') return;

  await fetch('/api/leads', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(values),
  });
};

export function LeadCaptureDemo() {
  return (
    <ActionForm
      title="Book a demo"
      description="Tell us where to follow up."
      action={leadCaptureTemplate.actions[0]}
      onAction={onAction}
      fields={[
        { name: 'email', label: 'Work email', type: 'email', required: true },
        { name: 'company', label: 'Company' },
        { name: 'message', label: 'What are you trying to do?', type: 'textarea', required: true },
      ]}
    />
  );
}
```

> **Warning**
>
> Client-side examples are only interaction wiring. Never treat browser-submitted
> payloads or browser-supplied user ids as authoritative. Server actions must use
> [`getUserId`](/docs/guides/authentication) / `getChatUserId` verified identity.

## Generic primitives

### ActionButton and ActionChips

Use for guided next steps, quick replies, local UI actions, or server actions.

- `ActionButton` emits one `MordnActionConfig`.
- `ActionChips` renders a small set of next actions.
- Use client handlers for local UI work such as opening a modal or checkout.
- Use server or hosted handlers for reads, capture, and mutations.

### ActionForm

Use for lightweight structured input:

- lead capture
- reservation requests
- callback requests
- service intake
- quote requests
- support ticket details

For complex validation, payments, or sensitive data, open a host-owned page or
modal and send only the final safe result back to the widget.

### EntityCard and EntityCarousel

Use for vertical cards:

- menu items
- products
- services
- hotel options
- destinations
- plans
- events
- support resources

Each card supports image, title, subtitle, description, attributes, price or
badge, and action buttons or safe external links.

### SummaryCard and ConfirmationCard

Use a summary before consequential actions. A confirmation should show what will
happen, where data will go, and how the user can cancel.

### StatusTracker

Use for order status, booking state, setup checklists, support ticket progress,
or itinerary steps.

## Built-in starter manifests

The package ships starter manifests for six verticals:

| Template | What it covers |
| --- | --- |
| `docs-assistant` | citations, setup guidance, open-page actions, unanswered question capture |
| `lead-capture` | qualification, form capture, summary, CRM or webhook handoff |
| `services-booking` | service selection, intake, request-to-book, status updates |
| `restaurant-assistant` | menu cards, dietary badges, recommendations, reservation request |
| `ecommerce-concierge` | product cards, variant selection, cart/checkout handoff |
| `travel-planner` | destination or hotel cards, itinerary timeline, quote request |

The restaurant, ecommerce, and travel templates deliberately use request or
handoff language by default. Don't let them imply confirmed inventory, purchase,
payment, or reservation unless a configured action result actually confirms it.

## Safety model

Action templates have five risk levels:

| Level | Examples | Default posture |
| --- | --- | --- |
| `ui` | open modal, navigate, open checkout link | client handler is fine |
| `read` | search menu, lookup order, check public status | server validation and timeout |
| `capture` | lead form, booking request, support ticket | confirmation or clear submit, audit redaction |
| `mutation` | create CRM record, add to cart server-side, reserve slot | required confirmation and idempotency |
| `regulated` | payment card, medical/legal/financial commitment | hand off outside chat |

Server-side or hosted execution should enforce:

- verified user identity
- schema validation
- authorization and tenant scope
- idempotency
- rate limits
- confirmation policy
- timeouts
- audit logs
- redaction for sensitive fields
- SSRF-safe webhook calls

## Template strategy

Don't build one-off restaurant, ecommerce, or travel widgets. Build reusable
primitives and declarative manifests, then layer verticals on top.

The docs assistant, lead capture, and services-booking templates need no live
inventory, cart, or payment integration, so they're the fastest to stand up.
Ecommerce and travel templates are worth reaching for once you have a stable
action-execution and confirmation flow in place, since they carry more
transactional risk.

## Hosted execution and dashboard tooling

The primitives above cover client-side templates. Hosted, per-tenant action
execution — with a dashboard template gallery, action editor, and analytics —
is not yet available; server-side execution is currently something you wire up
yourself, as described in the safety model above.

See [AI UX extras](/docs/guides/ai-ux) for related follow-up chips,
action-result cards, and tool approval patterns.
