Masheev Developers

Open Markdown · Agent index

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

FieldTypeRequiredDescription
idstringYesUnique ID for analytics grouping. Alphanumeric + underscore/hyphen, max 64 chars.
namestringNoHuman-readable name (max 100 chars).
stepsWorkflowStepConfig[]NoOrdered step definitions (max 20).
resumeAtStepstringNoStep ID to resume at (for returning users). Defaults to first step.
contextRecord<string, string | number | boolean>NoKey-value pairs available via {{context.KEY}} in step instructions. Max 20 keys, 500 chars per value.
variantstringNoA/B testing tag (e.g., "control", "variant-a").

WorkflowStepConfig

FieldTypeRequiredDescription
idstringYesUnique step ID. Alphanumeric + underscore/hyphen, max 64 chars.
namestringYesHuman-readable step name (max 100 chars).
instructionsstringNoStep-specific AI instructions (max 1,000 chars). Supports {{context.KEY}}.
allowSkipbooleanNoWhether the AI can skip this step (default: false).
toolsstring[]NoRestrict visible tools to this list during the step. Workflow tools are always available.
requiredToolsstring[]NoTools the AI must call before completing this step. Injected as a hard constraint into the prompt.
suggestionsstring[]NoQuick 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({ ... });
FieldTypeDescription
status"idle" | "running" | "completed" | "failed"Current workflow status
workflowIdstringThe workflow ID
currentStepIdstring | nullActive step ID (null when completed)
completedStepIdsstring[]IDs of completed steps
contextRecord<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

  1. Widget fetches config — no existing conversation found
  2. Session is created on first interaction
  3. AI triggers its first turn using step 1 instructions (no generic greeting)
  4. Workflow progresses through steps as the AI calls workflow_step_complete

Returning visitor (in-progress workflow)

  1. Widget fetches config — existing conversation found
  2. Session is resumed, chat history is loaded
  3. AI continues from the last completed step (resumeAtStep is set automatically)

Returning visitor (completed workflow)

  1. Widget fetches config — server reports workflowCompleted: true
  2. Client clears the stored visitor ID from localStorage
  3. Widget re-fetches config as a new visitor — fresh workflow starts automatically
  4. 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:

EventPayloadWhen
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

OperationLimit
workflow_step_complete tool4 calls per AI turn (idempotent — duplicate step completions are ignored via unique constraint)
workflow_complete tool1 per conversation (idempotent)
Trigger ({ trigger: true })3 per minute per session
Step order validationSteps 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