Conversation workflows
These are browser SDK conversation flows. They are not durable background jobs or server automation delays. Developer SDK access is beta; check availability.
Guide AI conversations through structured, multi-step flows with full analytics.
Quick Start
Level 1: Workflow ID Only (3 lines)
Masheev.init({
inboxId: "your-inbox-id",
workflow: { id: "onboarding" },
});
This is enough to start tracking workflow analytics. The AI agent runs freely; you get started/completed/abandoned events in the dashboard.
Level 2: Add Steps
Masheev.init({
inboxId: "your-inbox-id",
workflow: {
id: "onboarding",
name: "New User Onboarding",
steps: [
{ id: "welcome", name: "Welcome", instructions: "Greet the user by name." },
{
id: "collect_info",
name: "Collect Info",
instructions: "Ask for their business name and use case.",
},
{
id: "setup",
name: "Setup",
instructions: "Create their workspace using the available tools.",
},
],
},
});
Steps give you a funnel visualization in the analytics dashboard, per-step instructions for the AI, and turn-count tracking.
Level 3: Full Configuration
import { defineWorkflow, useMasheev, useWorkflow } from "@masheev/embed-sdk/react";
const ONBOARDING = defineWorkflow({
id: "onboarding",
name: "New User Onboarding",
variant: "v2-short",
steps: [
{
id: "welcome",
name: "Welcome",
instructions: "Greet {{context.userName}}. Ask about their business.",
},
{
id: "collect_info",
name: "Collect Business Info",
instructions: "Collect business name and use case (support, sales, or both).",
},
{
id: "create_org",
name: "Create Organization",
instructions: "Use create_organization tool with collected info.",
tools: ["create_organization"],
requiredTools: ["create_organization"],
},
],
});
function OnboardingChat() {
const masheev = useMasheev({
inboxId: "your-inbox-id",
workflow: {
...ONBOARDING,
context: { userName: user.name },
},
});
useWorkflow(masheev, {
onStepComplete: ({ stepId, data }) => {
analytics.track("onboarding_step", { stepId });
},
onComplete: ({ outcome }) => {
if (outcome === "success") navigate("/dashboard");
},
});
return <div ref={masheev.containerRef} />;
}
API Reference
WorkflowConfig
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique ID for analytics grouping. Alphanumeric + underscore/hyphen, max 64 chars. |
name | string | No | Human-readable name (max 100 chars). |
steps | WorkflowStepConfig[] | No | Ordered step definitions (max 20). |
resumeAtStep | string | No | Step ID to resume at (for returning users). Defaults to first step. |
context | Record<string, string | number | boolean> | No | Key-value pairs available via {{context.KEY}} in step instructions. Max 20 keys, 500 chars per value. |
variant | string | No | A/B testing tag (e.g., "control", "variant-a"). |
WorkflowStepConfig
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique step ID. Alphanumeric + underscore/hyphen, max 64 chars. |
name | string | Yes | Human-readable step name (max 100 chars). |
instructions | string | No | Step-specific AI instructions (max 1,000 chars). Supports {{context.KEY}}. |
allowSkip | boolean | No | Whether the AI can skip this step (default: false). |
tools | string[] | No | Restrict visible tools to this list during the step. Workflow tools are always available. |
requiredTools | string[] | No | Tools the AI must call before completing this step. Injected as a hard constraint into the prompt. |
suggestions | string[] | No | Quick reply buttons auto-shown when this step activates. Max 10, 200 chars each. |
defineWorkflow(config)
Type-inference helper for defining workflows as module-level constants. Returns the same config object (identity function for TypeScript inference).
import { defineWorkflow } from "@masheev/embed-sdk/react";
const MY_WORKFLOW = defineWorkflow({
id: "my_workflow",
steps: [{ id: "step1", name: "Step 1" }],
});
useWorkflow(masheev, handlers)
React hook that wires workflow event listeners with automatic cleanup.
useWorkflow(masheev, {
onStepComplete: (payload) => {
/* { workflowId, stepId, data?, nextStepId?, suggestions? } */
},
onComplete: (payload) => {
/* { workflowId, outcome, data? } */
},
});
updateWorkflow(updates)
Update workflow context or name mid-session. Available on the useMasheev return object.
const { updateWorkflow } = useMasheev({ ... });
// Add/update context values
updateWorkflow({ context: { cartTotal: 99.99 } });
workflowState (reactive)
Reactive workflow state returned by useMasheev. Populated from server state on init (survives page refresh), updates automatically on step/workflow completion.
const { workflowState } = useMasheev({ ... });
| Field | Type | Description |
|---|---|---|
status | "idle" | "running" | "completed" | "failed" | Current workflow status |
workflowId | string | The workflow ID |
currentStepId | string | null | Active step ID (null when completed) |
completedStepIds | string[] | IDs of completed steps |
context | Record<string, unknown> | Accumulated context from step completions |
Returns null when no workflow is configured.
// Progress bar
const progress = workflowState ? workflowState.completedStepIds.length / steps.length : 0;
// Step tracker
steps.map((step) => ({
...step,
status: workflowState?.completedStepIds.includes(step.id)
? "done"
: workflowState?.currentStepId === step.id
? "active"
: "pending",
}));
// Redirect on completion
if (workflowState?.status === "completed") {
navigate("/dashboard");
}
Vanilla JS — use getWorkflowState() for current state and on("workflowState", callback) for updates:
import { on, getWorkflowState } from "@masheev/embed-sdk/js";
on("workflowState", (state) => {
document.getElementById("progress").style.width =
`${(state.completedStepIds.length / totalSteps) * 100}%`;
});
Session Mode
When using workflows, set sessionMode: "workflow" on the SDK config. This ensures each workflow gets its own isolated session (keyed by inboxId + workflowId) and enables workflow-specific behavior like AI-first turns and automatic session reset on workflow completion.
const masheev = useMasheev({
inboxId: "your-inbox-id",
sessionMode: "workflow",
workflow: { ... },
});
Without sessionMode: "workflow", the widget uses a shared visitor session across all conversations on the same inbox.
Lifecycle
Workflows automatically handle session persistence, resumption, and restart.
New visitor
- Widget fetches config — no existing conversation found
- Session is created on first interaction
- AI triggers its first turn using step 1 instructions (no generic greeting)
- Workflow progresses through steps as the AI calls
workflow_step_complete
Returning visitor (in-progress workflow)
- Widget fetches config — existing conversation found
- Session is resumed, chat history is loaded
- AI continues from the last completed step (
resumeAtStepis set automatically)
Returning visitor (completed workflow)
- Widget fetches config — server reports
workflowCompleted: true - Client clears the stored visitor ID from localStorage
- Widget re-fetches config as a new visitor — fresh workflow starts automatically
- The previous conversation and workflow run are preserved in the database for analytics
This means completed workflows always restart cleanly. Users never see a stale "already done" state.
Returning visitor (failed workflow run)
If the workflow run has a "completed" status on the resumed session, the client calls resetConversation() which clears state and starts fresh (same as the completed flow above).
Manual restart
The host app can trigger a restart by calling resetConversation() from the widget context. This clears the visitor ID, resets all client state, and re-fetches config to start a new workflow session.
// From useWorkflow onComplete handler
useWorkflow(masheev, {
onComplete: ({ outcome }) => {
if (outcome === "success") {
// Widget auto-resets on next mount; navigate away
navigate("/dashboard");
}
},
});
Context Interpolation
Step instructions support {{context.KEY}} placeholders that resolve server-side using the workflow's context object.
workflow: {
id: "support",
context: { plan: "pro", region: "EU" },
steps: [{
id: "greet",
name: "Greeting",
instructions: "The user is on the {{context.plan}} plan in {{context.region}}.",
}],
}
Context values are XML-escaped on the server to prevent prompt injection.
Cross-Step Data Propagation
When a step completes with data (via the workflow_step_complete tool), that data is automatically merged into the workflow context. Subsequent steps can access it via {{context.KEY}}:
// Step "create_business" completes with data: { name: "Talia's", country: "US" }
// Next step can use:
{
id: "setup_menu",
instructions: "Set up the menu for {{context.name}} in {{context.country}}.",
}
// Also available namespaced: {{context.create_business_name}}
No client-side updateWorkflow() call needed — tool result data flows into context automatically.
Per-Step Tool Filtering
When a step defines tools, only those tools (plus the built-in workflow management tools) are visible to the AI during that step.
steps: [
{
id: "browse",
name: "Browse Products",
instructions: "Help the user find products.",
tools: ["search_products", "get_product_details"],
},
{
id: "checkout",
name: "Checkout",
instructions: "Process the order.",
tools: ["create_order", "apply_coupon"],
},
];
Events
Workflows emit lifecycle events that you can listen to client-side:
| Event | Payload | When |
|---|---|---|
workflow:stepComplete | { workflowId, stepId, data?, nextStepId?, suggestions? } | AI calls workflow_step_complete tool |
workflow:complete | { workflowId, outcome, data? } | AI calls workflow_complete tool |
The suggestions field in the step complete event contains the next step's configured suggestions (if any). The widget auto-renders them — no client-side setQuestions() wiring needed.
Third-Party Relay
Forward events to your analytics provider:
// Segment
useWorkflow(masheev, {
onStepComplete: ({ stepId, data }) =>
analytics.track("Workflow Step Completed", { stepId, ...data }),
onComplete: ({ outcome }) => analytics.track("Workflow Completed", { outcome }),
});
// GA4
useWorkflow(masheev, {
onStepComplete: ({ stepId }) => gtag("event", "workflow_step", { step_id: stepId }),
onComplete: ({ outcome }) => gtag("event", "workflow_complete", { outcome }),
});
Analytics Dashboard
Workflow analytics are available at Analytics > Workflows in the Masheev dashboard.
- Overview: Total started, completed, abandoned counts per workflow with completion rates
- Funnel: Per-step completion counts and average turn counts (color-coded: green >85%, amber 50-85%, red <50%)
- Sentiment: Average CSAT and sentiment scores for completed workflows
- Conversation drilldown: Step-by-step timeline for individual conversations
Security
Prompt Injection Defense
- Step instructions are placed within XML structural boundaries (
<workflow-context>,<current-step-instructions>) - Context values are XML-escaped via
escapeXml()before interpolation - Workflow data is PII-scrubbed before storage (keys containing email, phone, ssn, password, etc. are stripped)
- Instructions follow the prompt hierarchy: guardrails > identity > workflow instructions > knowledge
Rate Limits
| Operation | Limit |
|---|---|
workflow_step_complete tool | 4 calls per AI turn (idempotent — duplicate step completions are ignored via unique constraint) |
workflow_complete tool | 1 per conversation (idempotent) |
Trigger ({ trigger: true }) | 3 per minute per session |
| Step order validation | Steps must follow the defined order (no skipping unless allowSkip: true) |
Cardinality Guards
- Max 500 distinct workflow IDs per organization
- Max 50 variants per workflow
- Events exceeding these limits are silently dropped
Limitations
- Max 20 steps per workflow
- Max 20 context keys, 500 chars per value
- Total step instructions capped at 5,000 chars across all steps
- Workflow and step IDs must match
/^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/ - Invisible Unicode characters in IDs and instructions are stripped server-side