> ## Documentation Index
> Fetch the complete documentation index at: https://docs.context.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a personalized sales demo

> Use a prospect's domain to prepare a branded demo with sourced account facts, editable content, and a reviewable sharing step.

export const AgentSetupPrompt = ({children, variant = "setup"}) => {
  const isRecipe = variant === "recipe";
  const promptLabel = isRecipe ? "recipe prompt" : "setup prompt";
  const [copyState, setCopyState] = useState("idle");
  const contentRef = useRef(null);
  const detailsRef = useRef(null);
  const timerRef = useRef(null);
  const mountedRef = useRef(true);
  const pendingRef = useRef(false);
  useEffect(() => {
    mountedRef.current = true;
    return () => {
      mountedRef.current = false;
      if (timerRef.current !== null) clearTimeout(timerRef.current);
    };
  }, []);
  const copyPrompt = useCallback(async () => {
    if (pendingRef.current) return;
    pendingRef.current = true;
    if (timerRef.current !== null) {
      clearTimeout(timerRef.current);
      timerRef.current = null;
    }
    setCopyState("copying");
    try {
      const code = contentRef.current?.querySelector("pre code") ?? contentRef.current?.querySelector("pre");
      const prompt = code?.textContent;
      if (typeof prompt !== "string" || !prompt.trim()) {
        throw new Error("Prompt is not available.");
      }
      if (typeof navigator === "undefined" || !navigator.clipboard?.writeText) {
        throw new Error("Clipboard access is not available.");
      }
      await navigator.clipboard.writeText(prompt);
      if (!mountedRef.current) return;
      setCopyState("copied");
      timerRef.current = setTimeout(() => {
        if (mountedRef.current) setCopyState("idle");
        timerRef.current = null;
      }, 4000);
    } catch {
      if (!mountedRef.current) return;
      if (detailsRef.current) detailsRef.current.open = true;
      setCopyState("error");
    } finally {
      pendingRef.current = false;
    }
  }, []);
  const feedback = copyState === "copied" ? `${isRecipe ? "Recipe" : "Setup"} prompt copied. Paste it into your coding agent.` : copyState === "error" ? "Couldn't copy automatically. The prompt is open below. Select and copy the text manually." : "";
  return <section className="agent-setup-prompt not-prose" aria-label={`Agent ${promptLabel}`}>
      <style>{`
        .agent-setup-prompt {
          --asp-surface: oklch(0.985 0.005 255);
          --asp-border: oklch(0.9 0.012 255);
          --asp-title: oklch(0.25 0.025 255);
          --asp-text: oklch(0.45 0.021 255);
          --asp-accent: oklch(0.49 0.2 264);
          --asp-accent-hover: oklch(0.44 0.19 264);
          --asp-on-accent: oklch(0.985 0.005 255);
          --asp-focus: oklch(0.6 0.19 255);
          margin: 1.5rem 0;
          border: 1px solid var(--asp-border);
          border-radius: 0.875rem;
          background: var(--asp-surface);
          color: var(--asp-title);
          font-family: inherit;
          min-width: 0;
        }
        .dark .agent-setup-prompt {
          --asp-surface: oklch(0.2 0.014 255);
          --asp-border: oklch(0.33 0.016 255);
          --asp-title: oklch(0.96 0.006 255);
          --asp-text: oklch(0.78 0.013 255);
          --asp-accent: oklch(0.73 0.15 255);
          --asp-accent-hover: oklch(0.8 0.11 255);
          --asp-on-accent: oklch(0.19 0.025 255);
          --asp-focus: oklch(0.78 0.14 255);
        }
        .agent-setup-prompt__header { padding: 1.125rem 1.25rem; }
        .agent-setup-prompt__row {
          display: flex;
          align-items: flex-start;
          justify-content: space-between;
          gap: 1.25rem;
        }
        .agent-setup-prompt__intro { min-width: 0; }
        .agent-setup-prompt__title {
          margin: 0;
          font-size: 1.125rem;
          font-weight: 650;
          line-height: 1.4;
          color: var(--asp-title);
        }
        .agent-setup-prompt__subtitle {
          max-width: 65ch;
          margin: 0.375rem 0 0;
          font-size: 0.875rem;
          line-height: 1.6;
          color: var(--asp-text);
        }
        .agent-setup-prompt__copy {
          display: inline-flex;
          flex-shrink: 0;
          align-items: center;
          justify-content: center;
          gap: 0.5rem;
          min-height: 2.75rem;
          padding: 0.625rem 0.875rem;
          border: 1px solid transparent;
          border-radius: 0.5rem;
          background: var(--asp-accent);
          color: var(--asp-on-accent);
          font: inherit;
          font-size: 0.8125rem;
          font-weight: 600;
          line-height: 1.4;
          cursor: pointer;
        }
        .agent-setup-prompt__copy:hover:not(:disabled) {
          background: var(--asp-accent-hover);
        }
        .agent-setup-prompt__copy:disabled { cursor: wait; }
        .agent-setup-prompt__copy:focus-visible,
        .agent-setup-prompt__summary:focus-visible {
          outline: 2px solid var(--asp-focus);
          outline-offset: 3px;
        }
        .agent-setup-prompt__feedback {
          margin: 0.75rem 0 0;
          font-size: 0.8125rem;
          line-height: 1.5;
          color: var(--asp-text);
        }
        .agent-setup-prompt__feedback:empty { margin: 0; }
        .agent-setup-prompt__details { border-top: 1px solid var(--asp-border); }
        .agent-setup-prompt__summary {
          display: flex;
          align-items: center;
          gap: 0.5rem;
          padding: 0.75rem 1.25rem;
          border-radius: 0 0 0.875rem 0.875rem;
          font-size: 0.8125rem;
          font-weight: 500;
          line-height: 1.5;
          color: var(--asp-text);
          list-style: none;
          cursor: pointer;
        }
        .agent-setup-prompt__summary::-webkit-details-marker { display: none; }
        .agent-setup-prompt__summary:hover { color: var(--asp-title); }
        .agent-setup-prompt__details[open] .agent-setup-prompt__chevron {
          transform: rotate(90deg);
        }
        .agent-setup-prompt__content { min-width: 0; padding: 0 1.25rem 1rem; }
        .agent-setup-prompt__content > :first-child { margin-top: 0; }
        .agent-setup-prompt__content > :last-child { margin-bottom: 0; }
        .agent-setup-prompt__content pre {
          max-height: 32rem;
          overflow: auto;
          white-space: pre-wrap;
          overflow-wrap: anywhere;
        }
        @media (max-width: 640px) {
          .agent-setup-prompt__header { padding: 1rem; }
          .agent-setup-prompt__row { flex-direction: column; gap: 0.875rem; }
          .agent-setup-prompt__copy { width: 100%; }
          .agent-setup-prompt__summary { padding: 0.75rem 1rem; }
          .agent-setup-prompt__content { padding: 0 1rem 1rem; }
        }
      `}</style>

      <div className="agent-setup-prompt__header">
        <div className="agent-setup-prompt__row">
          <div className="agent-setup-prompt__intro">
            <h2 className="agent-setup-prompt__title">
              {isRecipe ? "Have your agent implement this recipe" : "Set up Context.dev"}
            </h2>
            <p className="agent-setup-prompt__subtitle">
              {isRecipe ? "Copy this prompt into your coding agent to build the recipe in your project." : "Paste into your coding agent. It will ask about your goal and project before making changes."}
            </p>
          </div>
          <button className="agent-setup-prompt__copy" type="button" onClick={copyPrompt} disabled={copyState === "copying"} aria-busy={copyState === "copying"}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" aria-hidden="true" focusable="false">
              {copyState === "copied" ? <path d="m5 12 4 4L19 6" strokeLinecap="round" strokeLinejoin="round" /> : <>
                  <rect x="8" y="8" width="12" height="12" rx="2" />
                  <path d="M16 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3" />
                </>}
            </svg>
            {copyState === "copied" ? "Copied" : copyState === "copying" ? "Copying…" : `Copy ${promptLabel}`}
          </button>
        </div>
        <p className="agent-setup-prompt__feedback" role="status" aria-live="polite" aria-atomic="true">
          {feedback}
        </p>
      </div>

      <details className="agent-setup-prompt__details" ref={detailsRef}>
        <summary className="agent-setup-prompt__summary">
          <svg className="agent-setup-prompt__chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" aria-hidden="true" focusable="false">
            <path d="m9 5 7 7-7 7" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
          {`View ${promptLabel}`}
        </summary>
        <div className="agent-setup-prompt__content" ref={contentRef}>
          {children}
        </div>
      </details>
    </section>;
};

<AgentSetupPrompt variant="recipe">
  ```text Recipe prompt theme={null}
  Implement this recipe in my project:
  https://docs.context.dev/use-cases/personalized-sales-demos

  Read the recipe and linked API guides, inspect this project's stack, and build the complete flow using its existing conventions.

  Turn a verified prospect domain into a saved, editable demo configuration with reviewed branding and sourced account facts. Keep sender, prospect, CRM context, and clearly labeled sample data distinct. Render a preview, preserve manual overrides across regeneration, and version drafts separately from approved shareable demos. Tie claims to evidence and keep each account's data separate.

  Reuse existing Context.dev configuration and keep secret API keys on the server. Add focused tests, run the relevant checks, and document setup and how to try the result.
  ```
</AgentSetupPrompt>

Turn a prospect's domain into a demo that looks familiar and uses relevant, verified account context. Context.dev supplies brand data and website evidence. Your application supplies CRM history, the product demonstration, and delivery.

[MarketBetter uses brand context for sales meeting preparation](https://www.context.dev/blog/marketbetter-gives-every-sdr-on-brand-meeting-prep-with-context-dev), and [Comp AI built a branded sales-deck generator](https://www.context.dev/blog/comp-ai-builds-an-on-brand-sales-deck-generator-with-context-dev). This recipe produces a saved demo configuration that your own renderer can preview.

## Resolve the prospect first

Start with a verified company domain from your CRM or a rep's selection. If the flow begins with a work email, use [Brand by email](/guides/retrieve-brand-by-email) and let the rep confirm the result. Similar company names, subsidiaries, and platform-hosted pages can otherwise produce the wrong branding.

Keep the sender and prospect as distinct identities:

| Identity     | Purpose                                                               |
| ------------ | --------------------------------------------------------------------- |
| Sender       | Who built the demo, owns its claims, and will share it                |
| Prospect     | Whose approved branding and public context personalize the experience |
| Example data | Clearly labeled sample records used to demonstrate the product        |

## Gather branding and selected facts

On your server, use an API key from the [Quickstart](/quickstart) to retrieve the [Brand profile](/guides/retrieve-brand-by-domain). Use [Styleguide](/guides/extract-design-system-from-website) only when the template needs additional design context.

Read selected about or product pages as [Markdown](/guides/scrape-websites-to-markdown), or use a bounded [Extract request](/guides/extract-structured-data-from-websites). For example, the following body asks only for public product context:

```json Extract request body theme={null}
{
  "url": "https://example.com/product",
  "maxPages": 3,
  "maxDepth": 1,
  "followSubdomains": false,
  "factCheck": true,
  "schema": {
    "type": "object",
    "properties": {
      "product_description": {
        "type": ["string", "null"],
        "description": "The product's stated purpose. Return null if unavailable."
      },
      "stated_audience": {
        "type": ["string", "null"],
        "description": "The audience explicitly named on the site. Do not infer the company's buying goals."
      }
    },
    "required": ["product_description", "stated_audience"],
    "additionalProperties": false
  }
}
```

Keep `urls_analyzed` and the retrieval time with the result. Before approving a fact for the demo, inspect the relevant page and retain its supporting URL or excerpt. The source list does not automatically provide field-level citations.

A full-site crawl should not be required to preview a demo. Let an unavailable page remain missing, and keep a neutral theme and editable company name when brand data is partial.

## Keep website evidence and CRM history distinct

Public website text can support a description of a product. Your CRM can support a rep's meeting notes or a stated next step. Neither source should silently overwrite the other.

```typescript demo-model.ts theme={null}
type Fact = {
  id: string;
  text: string;
  source:
    | { kind: "website"; url: string; retrievedAt: string; excerpt: string }
    | { kind: "crm"; recordId: string; updatedAt: string };
};

type DemoTheme = { accent: string; logoUrl: string | null };
type DemoOverrides = {
  headline?: string;
  accent?: string;
  logoUrl?: string | null;
};

type DemoConfig = {
  version: number;
  state: "draft" | "approved";
  preparedBy: string;
  preparedFor: { name: string; domain: string };
  headline: string;
  theme: DemoTheme;
  facts: Fact[];
  sampleDataLabel: string;
  sourceVersion: string;
  overrides: DemoOverrides;
};

export function buildDemo(input: {
  senderName: string;
  prospectName: string;
  prospectDomain: string;
  reviewedTheme: DemoTheme;
  approvedFacts: Fact[];
  sourceVersion: string;
  previous?: DemoConfig;
}): DemoConfig {
  if (input.previous && input.previous.preparedFor.domain !== input.prospectDomain) {
    throw new Error("Start a separate demo for a different account");
  }
  const overrides = input.previous?.overrides ?? {};
  return {
    version: (input.previous?.version ?? 0) + 1,
    state: "draft",
    preparedBy: input.senderName,
    preparedFor: { name: input.prospectName, domain: input.prospectDomain },
    headline: overrides.headline ?? `A product demo for ${input.prospectName}`,
    theme: {
      accent: overrides.accent ?? input.reviewedTheme.accent,
      logoUrl: overrides.logoUrl !== undefined
        ? overrides.logoUrl : input.reviewedTheme.logoUrl,
    },
    facts: input.approvedFacts,
    sampleDataLabel: "Illustrative sample data",
    sourceVersion: input.sourceVersion,
    overrides,
  };
}
```

`approvedFacts` means facts checked against their source for this revision. Keep their IDs tied to content or a source version so a changed claim cannot inherit an old approval. Rebuilding creates a draft while preserving design and headline edits; it does not silently update an already-shared demo.

## Populate the product demonstration

Use the configuration to select and fill components you control. A model can suggest which product features to highlight, but its output should reference approved fact IDs and a fixed list of available demo sections.

Do not infer the prospect's budget, purchase intent, goals, customer relationships, or results from its branding. Keep sample accounts, transactions, metrics, and testimonials visibly illustrative unless the rep supplied approved real data.

In the preview, show the sender and recipient labels, the source links behind account facts, and controls to replace the logo or edit the copy. Keep internal CRM notes out of the shared payload unless the rep intentionally selects content appropriate for the recipient.

## Save and share the reviewed revision

Persist the configuration and source-bundle version with your CRM account ID. A share action should select an approved revision and create the appropriate application link or export. Email sending, CRM updates, access controls, and link expiry belong to your application.

Try one complete prospect profile, one missing logo, a failed product-page read, and an edited headline. Regenerate the demo and verify that edits survive, unsupported claims remain absent, and the shared version stays unchanged until a new revision is approved.

<CardGroup cols={2}>
  <Card title="Lead enrichment" icon="address-card" href="/use-cases/lead-enrichment">
    Save company and person context without replacing rep edits.
  </Card>

  <Card title="Branded documents" icon="file-lines" href="/use-cases/branded-documents">
    Render the approved meeting brief as a report or deck.
  </Card>
</CardGroup>
