Rex Automaton
All posts
AI Voice & Chat AgentsAugust 29, 20268 min read

AI Post-Purchase Coach Demo for ZTH Training

How we built a post-purchase AI coach demo for ZTH Training that qualifies buyers and books upgrade calls. A fast demo de-risks scoping and accelerates buy-in.

By Jacky Lei

We built a post-purchase AI coach demo for ZTH Training that qualifies buyers in chat, routes them to the right path, and hands off to booking for the $4k program. It showed the experience in under a week, let Haris test voice and flows safely, and removed scope risk before a production build.

AI post-purchase coaching is an on-brand chat experience that engages new buyers after purchase: qualify, coach lightly, and escalate to a call or nurture without forcing a login or app.

The problem it solves

Teams stall when scoping an AI assistant into a funnel: unclear persona, unknown booking glue, worries about cost and voice fit. Meanwhile low-ticket buyers go cold before anyone can qualify them for an upsell.

Manual processDemo-led AI coach
Hand-written DMs and manual reviews after purchasesInstant chat engagement after purchase confirmation
Founder has to qualify every buyer personallyAI asks targeted questions, classifies persona, proposes next step
Booking handoff varies by rep and time of dayConsistent booking CTA with pre-qualification data
No safe way to test voice or toneDemo sandbox with strict JSON turn-state and mock booking
Weeks to spec, longer to build3 to 7 days to a clickable, brand-voiced demo
Scope creep during discoveryWorking flows lock scope before production

How the automation works

A lightweight web app runs a strict turn-by-turn dialog that keeps persona, goals, and next actions in a single source of truth. It never guesses prices or overpromises. It branches by persona and pushes a booking CTA only when the buyer qualifies.

  • Frontend: Next.js app: branded UI that hosts the chat, displays the current plan path, and shows a mocked pipeline panel.
  • Turn-state engine: one strict JSON object per turn stores persona, qualification flags, next action, and safe CTA text.
  • Model layer: a fast, cost-efficient LLM produces coach responses under a constrained prompt. It cannot invent prices or offers.
  • Persona routing: three paths we agreed with Haris: academy hopeful -> book, casual player -> nurture, hesitant -> objection handling then book.
  • Booking handoff: a mocked booking step in the demo with ready hooks to wire into a real calendar later.

ZTH Training post-purchase AI coach demo workflow: purchase triggers the coach, the AI coach session branches by persona and produces a booking or nurture handoff, and the founder reviews transcripts

Step-by-step: how to build it

1. Define personas and qualification fields

Start with the decision tree you would use on a call. We wrote the minimum fields to route correctly and avoided anything the model could not reliably infer in a short chat.

// personas.ts
export type Persona = "academy_hopeful" | "casual_player" | "hesitant";
 
export interface TurnState {
  turn: number;
  persona: Persona | null;
  goals: string[];           // buyer stated goals
  blockers: string[];        // time, money, injury, confidence
  qualified: boolean;        // ready to book a call
  next_action: "ask" | "coach" | "book" | "nurture";
  cta_text: string;          // safe, brand-voiced CTA
}
 
export const INITIAL: TurnState = {
  turn: 0,
  persona: null,
  goals: [],
  blockers: [],
  qualified: false,
  next_action: "ask",
  cta_text: ""
};

Key gotcha: keep the state small and explicit. Anything you do not track will drift across turns.

2. Constrain output to strict JSON per turn

We required the model to return only TurnState JSON plus a display message. A light validator caught drift early.

// validate.ts
import { INITIAL, TurnState } from "./personas";
 
export function validateState(input: any): { state: TurnState; message: string } {
  if (!input || typeof input !== "object") return { state: INITIAL, message: "" };
  const s = input.state ?? {};
  const state: TurnState = {
    turn: Number(s.turn ?? 0),
    persona: ["academy_hopeful","casual_player","hesitant"].includes(s.persona) ? s.persona : null,
    goals: Array.isArray(s.goals) ? s.goals.slice(0, 5).map(String) : [],
    blockers: Array.isArray(s.blockers) ? s.blockers.slice(0, 5).map(String) : [],
    qualified: Boolean(s.qualified),
    next_action: ["ask","coach","book","nurture"].includes(s.next_action) ? s.next_action : "ask",
    cta_text: String(s.cta_text ?? "")
  };
  const message = typeof input.message === "string" ? input.message : "";
  return { state, message };
}

Gotcha: never trust raw model output. Validate and clamp every field.

3. Implement a reducer that protects business rules

Even with valid JSON, the assistant cannot break rules: no pricing from the model, no booking CTA unless qualified is true.

// reducer.ts
import { TurnState } from "./personas";
 
export function enforcePolicy(prev: TurnState, next: TurnState): TurnState {
  const out = { ...next };
  // Never let the model set price language in CTA
  if (/\$|dollar|price|discount/i.test(out.cta_text)) out.cta_text = "Let's find a time to talk through your plan.";
  // Gate booking on qualification
  if (!out.qualified && out.next_action === "book") {
    out.next_action = "ask";
    out.cta_text = "Quick question: what is the single biggest change you want in the next 4 weeks?";
  }
  // Keep turn monotonic
  out.turn = Math.max(prev.turn + 1, out.turn);
  return out;
}

Gotcha: policy enforcement belongs server-side, not in the prompt.

4. Prompt the coach in a brand voice without leaking authority

We separated voice from authority. The coach speaks like Haris, but only the reducer is allowed to propose booking.

// prompt.ts
export const SYSTEM = `You are a friendly football coach in chat. Short messages. No prices. No medical claims.
Keep one goal per message. If buyer is academy_hopeful and engaged, move toward booking. If casual_player, coach lightly and propose a short check-in later. If hesitant, handle the stated objection with one concrete step.`;
 
export function userTurn(prev: any, buyer: string) {
  return [
    { role: "system", content: SYSTEM },
    { role: "user", content: JSON.stringify({ prev_state: prev, buyer_message: buyer }) }
  ];
}

Gotcha: do not let the model imply guarantees or outcomes. Make that explicit.

5. Mock the booking handoff with a drop-in adapter later

We showed the booking experience without touching a live calendar. The adapter interface keeps the wiring clean for Phase 2.

// booking.ts
export interface BookingPayload {
  email: string;
  name: string;
  notes: string; // goals and blockers
}
 
export interface BookingAdapter {
  createDraft(payload: BookingPayload): Promise<{ previewUrl: string }>;
}
 
export class MockBooking implements BookingAdapter {
  async createDraft(p: BookingPayload) {
    const q = new URLSearchParams({ n: p.name, e: p.email, notes: p.notes }).toString();
    return { previewUrl: `/booking/preview?${q}` };
  }
}

Gotcha: keep adapters pure so you can swap the mock for a real calendar later without refactoring flows.

Where it gets complicated

  • Environment keys and shared accounts: a shared AI key had expired and returned 401. Each demo must use a project-scoped key to avoid cross-project outages.
  • Strict JSON turn-state: models drift into prose unless you clamp outputs. We validated every field and clamped arrays and enums.
  • Voice consistency vs cost: we used a fast model for speed and cost. Voice stayed within spec, but we kept the reducer as the authority on booking and claims to control risk.
  • Booking is mocked by design: a demo handoff should feel real without touching live calendars or CRMs. Adapters make the later wiring trivial.
  • No pricing or guarantees in copy: sales claims live with humans. The assistant focuses on goals, objections, and next steps only.

What this actually changes

For a fitness coaching ladder like ZTH Training, the post-purchase coach created a reliable, on-brand touch the moment someone bought a low-ticket product. That turned scattered manual DMs into a consistent qualifier that nudged qualified buyers to a booked call and routed others to a nurture path. It also made scope concrete: Haris could see and feel the experience before we wrote any integrations.

Retention-focused work pays back. Harvard Business Review notes that acquiring a new customer can cost five to twenty five times more than retaining an existing one: https://hbr.org/2014/10/the-value-of-keeping-the-right-customers. Post-purchase coaching targets people who already raised a hand, so the economics favor building this first.

Frequently asked questions

Is this a live deployment or a demo?

This was a case-study demo we built for ZTH Training. It is a production-grade architecture running in a sandbox with a mocked booking handoff. The same flows and adapters drop into a real calendar and CRM in Phase 2 with minimal changes.

How long does a demo like this take and what do you need from me?

Plan for 3 to 7 days. We need your brand voice guardrails, your persona definitions, objection patterns, and the exact next step you want when a buyer qualifies. We do not need API access or calendars for the demo phase.

Can this connect to my booking tool and CRM later?

Yes. The demo uses a booking adapter interface. In Phase 2 we swap the mock for your real calendar and add a CRM adapter for transcript storage and contact creation without changing the dialog logic.

How do you prevent the assistant from making promises or wrong prices?

We keep offers and prices out of the model. The server reducer enforces policy and strips price language. The assistant can ask, coach, or suggest booking, but only the reducer authorizes booking and composes safe CTA text.

What does it cost to run monthly once live?

The demo run cost was low because we used a fast model and short messages. In production the primary costs are model usage and any scheduling or CRM platform fees. We tune prompts and turn length to keep per-conversation cost low.

Can a non-technical founder operate or test this?

Yes. The demo is a clickable site. You can try the personas, test objections, and review transcripts without logging into any dev tools. We keep integrations behind adapters so you do not need to touch code.

If you want an on-brand post-purchase coach to qualify buyers and book more upgrade calls, we already built the pattern. See how we approach branded retention flows in our coffee subscription demo next: /blog/hanoi-drip-coffee-retention-engine-demo. Or explore how we wire custom systems on our /services#custom-ai-integration page. Ready to scope your version: /book.

Want us to build this for you?

15-minute discovery call. No pitch. We tell you what to automate first.

Book a Discovery Call

Related reading