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

# Tutorial: Environments

> PolyAcademy Level 1 – Understand Draft, Published, and Environment stages to safely promote changes to production.

export const LessonMeta = ({level, difficulty, time}) => {
  const levelConfig = {
    1: {
      badge: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
      label: 'Level 1'
    },
    2: {
      badge: 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200',
      label: 'Level 2'
    },
    3: {
      badge: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
      label: 'Level 3'
    }
  };
  const difficultyConfig = {
    Beginner: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
    Intermediate: 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200',
    Advanced: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'
  };
  const lvl = levelConfig[level] || levelConfig[1];
  const diffColor = difficultyConfig[difficulty] || difficultyConfig['Beginner'];
  return <div className="flex flex-wrap items-center gap-2 my-4 not-prose">
      <span className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold ${lvl.badge}`}>
        {lvl.label}
      </span>
      <span className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold ${diffColor}`}>
        {difficulty}
      </span>
      {time && <span className="inline-flex items-center gap-1 text-xs text-gray-500 dark:text-gray-400">
          <svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
          </svg>
          {time}
        </span>}
    </div>;
};

export const Challenge = ({scenario, hints = [], solution}) => {
  const [revealed, setRevealed] = useState(0);
  const [showSolution, setShowSolution] = useState(false);
  return <div className="my-6 rounded-xl border-2 border-violet-200 bg-violet-50 dark:border-violet-800 dark:bg-violet-950 overflow-hidden">
      <div className="flex items-center gap-2 px-5 pt-4 pb-0">
        <svg className="w-4 h-4 shrink-0 text-violet-500 dark:text-violet-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
          <path strokeLinecap="round" strokeLinejoin="round" d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z" />
        </svg>
        <span className="text-xs font-bold uppercase tracking-widest text-violet-500 dark:text-violet-400">
          Challenge
        </span>
      </div>

      <p className="px-5 pt-3 pb-4 mt-0 text-sm leading-relaxed text-violet-900 dark:text-violet-100">
        {scenario}
      </p>

      {hints.length > 0 && <div className="border-t border-violet-200 dark:border-violet-800 px-5 py-3">
          {revealed > 0 && <div className="mb-3 space-y-2">
              {hints.slice(0, revealed).map((h, i) => <div key={i} className="flex gap-2 text-sm">
                  <span className="font-semibold shrink-0 text-violet-400 dark:text-violet-500">
                    Hint {i + 1}:
                  </span>
                  <span className="text-violet-800 dark:text-violet-200">{h}</span>
                </div>)}
            </div>}
          {revealed < hints.length && <button type="button" onClick={() => setRevealed(r => r + 1)} className="text-xs font-medium text-violet-600 dark:text-violet-400 hover:text-violet-800 dark:hover:text-violet-200 underline underline-offset-2 cursor-pointer transition-colors duration-150">
              {revealed === 0 ? 'Show a hint' : 'Show next hint'}
            </button>}
        </div>}

      {solution && <div className="border-t border-violet-200 dark:border-violet-800 px-5 py-3">
          {!showSolution ? <button type="button" onClick={() => setShowSolution(true)} className="text-xs text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 underline underline-offset-2 cursor-pointer transition-colors duration-150">
              Show solution
            </button> : <div>
              <div className="flex justify-between items-center mb-2">
                <span className="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
                  Solution
                </span>
                <button type="button" onClick={() => setShowSolution(false)} className="text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 underline underline-offset-2 cursor-pointer transition-colors duration-150">
                  Hide
                </button>
              </div>
              <p className="mt-0 text-sm leading-relaxed text-gray-800 dark:text-gray-200">
                {solution}
              </p>
            </div>}
        </div>}
    </div>;
};

export const FillBlank = ({prompt, answer, hint, explanation}) => {
  const [value, setValue] = useState('');
  const [submitted, setSubmitted] = useState(false);
  const normalize = s => s.trim().toLowerCase().replace(/[^a-z0-9_]/g, '');
  const answers = Array.isArray(answer) ? answer : [answer];
  const isCorrect = answers.some(a => normalize(value) === normalize(a));
  const handleSubmit = e => {
    e.preventDefault();
    if (value.trim()) setSubmitted(true);
  };
  const handleReset = () => {
    setValue('');
    setSubmitted(false);
  };
  return <div className="my-6">
      <p className="mt-0 mb-3 text-sm font-semibold leading-relaxed text-gray-900 dark:text-gray-100">
        {prompt}
      </p>
      <form onSubmit={handleSubmit} className="flex flex-col gap-2.5">
        <div className="flex gap-2">
          <input type="text" value={value} onChange={e => {
    setValue(e.target.value);
    setSubmitted(false);
  }} placeholder={hint || "Type your answer…"} className="flex-1 rounded-xl border py-2.5 px-4 text-sm font-mono border-gray-200 bg-white text-gray-900 placeholder-gray-400 outline-none focus:border-gray-400 focus:ring-2 focus:ring-gray-200 transition-all duration-150 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-600 dark:focus:border-gray-500 dark:focus:ring-gray-700" />
          <button type="submit" className="rounded-xl border py-2.5 px-5 text-sm font-medium transition-all duration-150 border-gray-800 bg-gray-800 text-white hover:bg-gray-700 hover:border-gray-700 dark:border-gray-200 dark:bg-gray-200 dark:text-gray-900 dark:hover:bg-white">
            Check
          </button>
        </div>
        {submitted ? <div className={`py-3 pl-4 pr-3.5 rounded-r-xl text-sm leading-relaxed border-l-4 ${isCorrect ? 'border-green-500 bg-green-50 dark:bg-green-900 dark:border-green-500' : 'border-red-500 bg-red-50 dark:bg-red-900 dark:border-red-500'}`}>
            {isCorrect ? <>
                <span className={`font-semibold !text-green-800 dark:!text-green-200`}>Correct.</span>{' '}
                <span className="!text-gray-700 dark:!text-gray-300">{explanation}</span>
              </> : <>
                <span className="font-semibold !text-red-800 dark:!text-red-200">Not quite.</span>{' '}
                <span className="!text-gray-700 dark:!text-gray-300">The answer is <code className="!text-gray-800 dark:!text-gray-200">{answers[0]}</code>. {explanation}</span>
              </>}
          </div> : null}
      </form>
      {submitted ? <button type="button" onClick={handleReset} className="mt-2 text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 underline underline-offset-2 cursor-pointer transition-colors duration-150">
          Try again
        </button> : null}
    </div>;
};

export const Quiz = ({questions = []}) => {
  const [selected, setSelected] = useState({});
  const [resetCount, setResetCount] = useState(0);
  const letters = ['A', 'B', 'C', 'D'];
  const handleSelect = (qIdx, optIdx) => {
    if (selected[qIdx] !== undefined) return;
    setSelected(prev => ({
      ...prev,
      [qIdx]: optIdx
    }));
  };
  const handleReset = () => {
    setSelected({});
    setResetCount(c => c + 1);
  };
  if (!questions?.length) return null;
  const getOptionClasses = ({hasAnswered, isThisCorrect, isThisSelected}) => {
    if (!hasAnswered) {
      return {
        btn: 'flex w-full items-center gap-3 py-2.5 px-4 rounded-xl text-sm leading-normal transition-all duration-150 text-left border cursor-pointer border-gray-200 bg-white text-gray-700 hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200 dark:hover:border-gray-500 dark:hover:bg-gray-700',
        badge: 'w-6 h-6 rounded-full text-xs font-bold flex items-center justify-center shrink-0 leading-none transition-all duration-150 bg-gray-100 text-gray-500 dark:bg-gray-700 dark:text-gray-300',
        icon: null
      };
    }
    if (isThisCorrect) {
      return {
        btn: 'flex w-full items-center gap-3 py-2.5 px-4 rounded-xl text-sm leading-normal transition-all duration-150 text-left border cursor-default border-green-400 bg-green-50 text-green-900 font-medium dark:border-green-500 dark:bg-green-950 dark:text-green-100',
        badge: 'w-6 h-6 rounded-full text-xs font-bold flex items-center justify-center shrink-0 leading-none transition-all duration-150 bg-green-500 text-white dark:bg-green-500',
        icon: <svg className="shrink-0 w-4 h-4 text-green-500 dark:text-green-400 ml-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
          </svg>
      };
    }
    if (isThisSelected) {
      return {
        btn: 'flex w-full items-center gap-3 py-2.5 px-4 rounded-xl text-sm leading-normal transition-all duration-150 text-left border cursor-default border-red-400 bg-red-50 text-red-900 dark:border-red-500 dark:bg-red-950 dark:text-red-100',
        badge: 'w-6 h-6 rounded-full text-xs font-bold flex items-center justify-center shrink-0 leading-none transition-all duration-150 bg-red-500 text-white dark:bg-red-500',
        icon: <svg className="shrink-0 w-4 h-4 text-red-400 dark:text-red-400 ml-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}>
            <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
          </svg>
      };
    }
    return {
      btn: 'flex w-full items-center gap-3 py-2.5 px-4 rounded-xl text-sm leading-normal transition-all duration-150 text-left border cursor-default border-gray-100 bg-white text-gray-400 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-500',
      badge: 'w-6 h-6 rounded-full text-xs font-bold flex items-center justify-center shrink-0 leading-none transition-all duration-150 bg-gray-100 text-gray-500 dark:bg-gray-700 dark:text-gray-500',
      icon: null
    };
  };
  return <div key={resetCount} className="my-6">
      {questions.map((q, qIdx) => {
    const answer = selected[qIdx];
    const hasAnswered = answer !== undefined;
    const isCorrect = answer === q.correct;
    return <div key={String(qIdx)} className="mb-8">
            <p className="flex items-start gap-2.5 font-semibold text-sm mb-3 mt-0 leading-relaxed text-gray-900 dark:text-gray-100">
              <span className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-gray-800 dark:bg-gray-200 text-white dark:text-gray-900 text-xs font-bold shrink-0 mt-px leading-none">
                {qIdx + 1}
              </span>
              {q.q}
            </p>

            <div className="flex flex-col gap-2">
              {q.options.map((opt, i) => {
      const isThisCorrect = i === q.correct;
      const isThisSelected = i === answer;
      const {btn, badge, icon} = getOptionClasses({
        hasAnswered,
        isThisCorrect,
        isThisSelected
      });
      return <button key={String(i)} type="button" onClick={() => handleSelect(qIdx, i)} className={btn}>
                    <span className={badge}>{letters[i]}</span>
                    <span className="flex-1">{opt}</span>
                    {icon}
                  </button>;
    })}
            </div>

            {hasAnswered ? <div className={`mt-3 py-3 pl-4 pr-3.5 rounded-r-xl text-sm leading-relaxed border-l-4 ${isCorrect ? 'border-green-500 bg-green-50 dark:bg-green-950 dark:border-green-500' : 'border-red-500 bg-red-50 dark:bg-red-950 dark:border-red-500'}`}>
                <span className={`font-semibold ${isCorrect ? '!text-green-800 dark:!text-green-200' : '!text-red-800 dark:!text-red-200'}`}>
                  {isCorrect ? 'Correct.' : 'Not quite.'}
                </span>{' '}
                <span className="!text-gray-700 dark:!text-gray-300">{q.explanation}</span>
              </div> : null}
          </div>;
  })}

      <button type="button" onClick={handleReset} className="mt-1 text-xs text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 underline underline-offset-2 cursor-pointer transition-colors duration-150">
        Reset quiz
      </button>
    </div>;
};

export const ProgressTracker = ({lessonNum, totalLessons, level}) => {
  const [checked, setChecked] = useState(false);
  return <div onClick={() => setChecked(prev => !prev)} className={checked ? 'flex items-center gap-3 p-4 rounded-lg border-2 border-green-600 bg-green-50 dark:bg-green-950 cursor-pointer select-none transition-all' : 'flex items-center gap-3 p-4 rounded-lg border-2 border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 cursor-pointer select-none transition-all'}>
      <div className={checked ? 'w-5 h-5 rounded border-2 border-green-600 bg-green-600 flex items-center justify-center shrink-0 transition-all' : 'w-5 h-5 rounded border-2 border-gray-400 dark:border-gray-500 bg-white dark:bg-gray-800 flex items-center justify-center shrink-0 transition-all'}>
        {checked ? <svg width="10" height="8" viewBox="0 0 10 8" fill="none">
            <path d="M1 4L3.5 6.5L9 1" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
          </svg> : null}
      </div>
      <div>
        <div className={checked ? 'font-semibold text-sm text-green-700 dark:text-green-300' : 'font-semibold text-sm text-gray-700 dark:text-gray-200'}>
          {checked ? 'Lesson complete' : 'Mark lesson complete'}
        </div>
        {lessonNum && totalLessons ? <div className="text-xs text-gray-500 dark:text-gray-400 mt-0.5">
            {level ? level + ' - ' : ''}Lesson {lessonNum} of {totalLessons}
          </div> : null}
      </div>
    </div>;
};

**Lesson 5 of 6** – Before your agent goes live, you need to understand environments. This is how you test safely – so changes never reach real users until you're ready.

<LessonMeta level={1} difficulty="Beginner" time="15 min" />

Think of environments as stages: **Sandbox** (your workspace) → **Pre-release** (QA review) → **Live** (real users). Each stage runs a specific published version of your agent.

## How environments work

Think of environments as **checkpoints**, not branches.

<CardGroup cols={3}>
  <Card title="Draft" icon="pencil">
    Your working state. Changes exist only for you.
  </Card>

  <Card title="Published version" icon="camera">
    A snapshot of the agent at a moment in time.
  </Card>

  <Card title="Environment" icon="server">
    A place where a specific version is running and can receive traffic.
  </Card>
</CardGroup>

Multiple environments can point to the same version, but **only one version per environment** is active at any time.

## The full promotion flow

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart TD
    A[Make changes in Draft] --> B[Publish version]
    B --> C[Test in Sandbox]
    C --> D{Tests pass?}
    D -->|Yes| E[Promote to Pre-release]
    D -->|No| F[Fix in Draft]
    F --> B
    E --> G{UAT approved?}
    G -->|Yes| H[Promote to Live]
    G -->|No| F
    H --> I[Monitor first calls]
```

## The Environments page

This page shows every published version of your agent and where it is currently deployed.

<AccordionGroup>
  <Accordion title="What you'll see" icon="list">
    * Version name or description
    * Author and timestamp
    * Which environments it's active in (Sandbox / Pre-release / Live)
    * An overflow menu for actions
  </Accordion>

  <Accordion title="Key actions" icon="ellipsis-vertical">
    * **Preview version** – load the agent exactly as that version behaves
    * **Compare versions** – open a side-by-side diff
    * **Rollback to this version** – immediately restore a previous state
  </Accordion>
</AccordionGroup>

## Check your understanding

<Quiz
  questions={[
{
q: "Version 14 is active in Sandbox. You publish version 15 and promote it to Sandbox. What happens to version 14?",
options: [
  "It keeps running alongside version 15 until manually removed",
  "It is replaced – version 15 becomes the only active version in Sandbox",
  "Both versions run simultaneously for a short overlap period",
  "Version 14 is automatically promoted to Pre-release",
],
correct: 1,
explanation: "Only one version per environment is active at any time. Promoting version 15 to Sandbox replaces version 14 immediately. Multiple environments can each point to the same version, but each environment runs exactly one.",
}
]}
/>

<FillBlank prompt="Clicking Publish creates a _____ of the agent at that point in time. It does not push to any environment automatically." answer={["version", "snapshot", "Version", "Snapshot"]} hint="Think of it like taking a photograph of the current state — immutable, dated, reviewable." explanation="Publish creates a version (a snapshot) of the agent. You still need to promote that version to Sandbox, Pre-release, or Live separately. This separation means publishing is always safe — it never touches a live environment until you explicitly promote." />

## Environment types

<Tabs>
  <Tab title="Sandbox" icon="flask">
    Sandbox is your **development and testing environment**.

    **Use Sandbox for:**

    * Writing or editing KB topics
    * Adjusting rules and behavior
    * Changing voice settings
    * Wiring or modifying actions, SMS, or handoffs

    <AccordionGroup>
      <Accordion title="Best practices" icon="circle-check">
        - Confirm you are in **Sandbox** before editing
        - Publish frequently with short, descriptive notes
        - Test after every meaningful change

        **Example publish note:**
        "Clarified late checkout KB and added SMS consent language"
      </Accordion>

      <Accordion title="Verification" icon="magnifying-glass">
        * Chat testing reflects your latest changes
        * Test calls hit Sandbox numbers, not Live numbers
        * Conversation Review shows the Sandbox environment tag
      </Accordion>
    </AccordionGroup>
  </Tab>

  <Tab title="Pre-release" icon="vial">
    Pre-release is your **staging / UAT environment**.

    It should be treated as:

    * Stable
    * Reviewable
    * Close to production

    **Use Pre-release for:**

    * Final review and approval
    * QA review
    * Final voice and pacing checks
    * Regression testing

    <AccordionGroup>
      <Accordion title="Best practices" icon="circle-check">
        - Promote to Pre-release only after Sandbox tests pass
        - Re-run your smoke test for what you've built so far:
          * Simple FAQ (Chat and Call)
          * Voice and greeting
          * Out-of-scope refusal
          * Any topics or actions you've added
        - Review Conversation Review data again

        **Example UAT checklist:**

        * Do your FAQ topics still trigger correctly?
        * Does voice sound identical to Sandbox?
        * Are there any unexpected regressions?
      </Accordion>

      <Accordion title="Verification" icon="magnifying-glass">
        * No unexpected KB topics are triggered
        * No new hallucinations appear
        * Behavior matches expectations across channels
      </Accordion>
    </AccordionGroup>
  </Tab>

  <Tab title="Live" icon="signal-stream">
    Live is **production**. Real users are here.

    **Rules for Live:**

    * Never edit directly
    * Never "just test something quickly"
    * Always know exactly what version is live

    <AccordionGroup>
      <Accordion title="Best practices" icon="circle-check">
        - Promote to Live only from Pre-release
        - Confirm promotion explicitly in the dialog
        - Monitor first calls after release

        **Example safe release habit:**
        Promote → make one test call → open Conversation Review → confirm behavior → continue monitoring
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

## Publishing and promotion flow

<Steps>
  <Step title="Make changes">
    Work in Draft mode
  </Step>

  <Step title="Publish">
    Click **Publish** and add a clear description
  </Step>

  <Step title="Version appears in Sandbox">
    Test thoroughly in Sandbox environment
  </Step>

  <Step title="Promote to Pre-release">
    Move to staging for QA review
  </Step>

  <Step title="Test and review">
    Complete full regression testing
  </Step>

  <Step title="Promote to Live">
    Deploy to production
  </Step>
</Steps>

Nothing skips steps. If it does, something is wrong.

## Comparing versions

The **Compare versions** view is your primary safety tool.

<CardGroup cols={2}>
  <Card title="What it shows" icon="code-compare">
    * Added KB topics
    * Modified content
    * Removed actions
    * Function wiring changes
    * Sample question edits
  </Card>

  <Card title="When to use it" icon="shield-check">
    Before every promotion to verify all changes are intentional
  </Card>
</CardGroup>

**Example comparison insight:**

* Late checkout KB text changed
* New SMS action added
* No rule or voice changes

If you can't explain every change in the diff, don't promote.

## Rollbacks

Rollbacks are instant and safe.

<AccordionGroup>
  <Accordion title="When to use rollback" icon="clock-rotate-left">
    * A KB topic misfires
    * Voice sounds wrong
    * Handoffs spike unexpectedly
    * A release introduces confusion
  </Accordion>

  <Accordion title="How to rollback" icon="list-ol">
    1. Select a known-good version
    2. Click rollback
    3. Confirm rollback
    4. Verify with a live test call

    Rollback restores behavior immediately without deleting newer versions.
  </Accordion>
</AccordionGroup>

## Phone numbers and environments

Each environment can have its own phone number.

<CardGroup cols={3}>
  <Card title="Sandbox number" icon="flask">
    Internal testing only
  </Card>

  <Card title="Pre-release number" icon="vial">
    QA and acceptance testing
  </Card>

  <Card title="Live number" icon="phone">
    Public traffic
  </Card>
</CardGroup>

This prevents accidental testing on production users.

## Common mistakes

<Warning>
  **Avoid these pitfalls:**

  * Editing while in Live without noticing
  * Forgetting to publish before testing
  * Assuming KB edits propagate automatically
  * Skipping version comparison
  * Using vague publish notes like "updates"
</Warning>

## Check your understanding

<Quiz
  questions={[
{
q: "What does clicking 'Publish' do?",
options: [
  "Pushes the agent live immediately",
  "Sends the agent to Pre-release for QA",
  "Creates a snapshot (version) of the agent",
  "Notifies your team of pending changes",
],
correct: 2,
explanation: "Publish creates a snapshot (version) of the agent at that moment. It does not push to Live – you still need to promote the version to an environment separately.",
}
]}
/>

## Verification checklist

<Check>
  You understand environments when:

  * You can say which version is live without checking
  * You know how to roll back in under a minute
  * You can explain what changed between two versions
  * You never feel tempted to "just tweak production"

  If environments feel slow, that usually means they're doing their job.
</Check>

<Challenge
  scenario="Your agent is live. The helpdesk reports that since last night's release, the agent incorrectly tells callers that refunds take 3 days — the real policy is 5 business days. Version 22 is currently live. Version 21 was working correctly. What do you do first, and why?"
  hints={[
"You have a way to restore correct behavior immediately, without building or testing a new version.",
"The Environments page shows all published versions and their current deployment status.",
"Restoring speed matters more than perfection right now — you can fix the root cause in Draft afterward."
]}
  solution="Roll back to version 21 immediately. Open the Environments page, locate version 21, click Rollback, and confirm. Make one test call to verify the correct refund policy is stated. Then fix the content error in Draft, publish version 23, test in Sandbox, and promote through Pre-release to Live as normal. Rollbacks are safe and instant — always prefer them over deploying a rushed fix under pressure."
/>

## Try it yourself

<Steps>
  <Step title="Challenge: Pre-release checklist">
    You've made three changes in Sandbox: updated the pet policy topic, added a new SMS offer for late checkout, and changed the agent voice stability from 0.6 to 0.8.

    List **at least 4 specific tests** you would run before promoting to Pre-release.

    <Accordion title="Hint">
      Think about each change you made and what could have broken. Also consider: what tests should always run, regardless of what changed?
    </Accordion>

    <Accordion title="Example solution">
      1. **Test pet policy topic** – ask "are dogs allowed?" in both Chat and Call; confirm the correct topic triggers.
      2. **Test SMS consent flow** – ask "can you text me the late checkout info?" and confirm the agent asks for consent before sending.
      3. **Make a test call** – listen for the voice stability change; confirm the agent sounds more consistent and no audio artifacts were introduced.
      4. **Run a full smoke test** – test a simple FAQ, out-of-scope refusal, and a handoff to confirm nothing regressed from your other changes.
      5. **Compare versions** – open the diff and confirm the three changes are the only ones present.
    </Accordion>
  </Step>
</Steps>

## Check your understanding

<Quiz
  questions={[
{
q: "Which environment should receive real user traffic?",
options: [
  "Sandbox – it's the most stable because it's been through the most testing",
  "Pre-release – it's the last step before Live, so it's safe for real users",
  "Live",
  "Either Pre-release or Live – the difference is just a naming convention",
],
correct: 2,
explanation: "Only Live should receive real user traffic. Sandbox is for development and initial testing; Pre-release is for QA and UAT with test users only. Exposing real callers to earlier environments risks hitting untested behavior.",
}
]}
/>

## Go deeper

The Environments reference covers version diffs, project history, and advanced workflows:

<CardGroup cols={3}>
  <Card title="Environments reference" icon="server" href="/environments-and-versions/introduction">
    Full reference for environments and version management
  </Card>

  <Card title="Version diffs" icon="code-compare" href="/environments-and-versions/diffs">
    How to compare versions side-by-side before promoting
  </Card>

  <Card title="Version management" icon="clock-rotate-left" href="/learn/maintain/version-management">
    Best practices for maintaining versions in a live project
  </Card>
</CardGroup>

***

<CardGroup cols={2}>
  <Card title="← Previous: Edit voice settings" icon="arrow-left" href="/learn/guides/get-started/edit-voice-settings">
    Lesson 4 of 6
  </Card>

  <Card title="Next: Conversation reviews →" icon="arrow-right" href="/learn/guides/get-started/conv-review">
    Lesson 6 – see what your agent actually did
  </Card>
</CardGroup>

<ProgressTracker lessonKey="l1-5-environments" lessonNum={5} totalLessons={6} level="Level 1" />
