How to add guides and surveys to your app with Codex CLI

Use Codex CLI to anchor a guide to the report export button, trigger a rating survey after an export, and check the saved response. Review the generated changes against a complete browser example and verify the result in Amplitude.

  • Codex CLI
  • Vite
  • Unified SDK 1.1.43
  • 30 min

Chris Tam · Last updated 3 November 2026

A coding agent can label the elements in your app while you decide what guidance belongs there. Amplitude guides and surveys can explain the report export action and ask for feedback after someone uses it.

In this tutorial, you'll give Codex CLI an implementation prompt, review its changes, run a report example, and check the result in your development project.

A browser report app that lets you anchor a guide to the report export button, trigger a rating survey after an export, and check the saved response. The agent's code is reviewable against the complete files below.

Prerequisites

  • Codex CLI installed and signed in. This tutorial uses its interactive terminal workflow, not an unattended run.
  • Node.js 22.12 or later and Corepack with pnpm 10.33.2. The example pins Vite 8.3.4 and @amplitude/unified 1.1.43; it doesn't require another Build tutorial.
  • A development Amplitude project and its public client API key from Settings > Projects > your project > General. Confirm its US or EU data center. Check that the project has access to the product and capacity for your test.

Sign up for Amplitude if you need an account, and compare plans for your project's allowances. Use synthetic data throughout this walkthrough.

1. Open the project and connect context

I use a small Vite app to make the generated changes easy to inspect. For a new example, create an empty repository. If you're extending an existing app, open its repository and preserve its setup instead.

Terminal
mkdir amplitude-report-guides-surveys
cd amplitude-report-guides-surveys
git init

Optionally connect Amplitude's MCP server so the agent can inspect your project's existing event taxonomy. Run these commands from the project directory before starting the interactive session. Use https://mcp.eu.amplitude.com/mcp instead for EU data residency.

Terminal
codex mcp add amplitude --url https://mcp.amplitude.com/mcp
codex mcp login amplitude
codex mcp list

Complete the browser sign-in opened by codex mcp login amplitude. Read the client's MCP instructions if your organization manages these settings. Review the requested access. Connecting context doesn't authorize the agent to publish experiences or change targeting.

2. Give the agent a precise implementation prompt

Run codex in the project directory after configuring MCP, then paste this prompt into the interactive session. It names the event, the SDK boundary, and the checks the agent must preserve.

Implementation prompt
Work in this repository and first inspect its app entry point, existing Amplitude initialization, consent flow, authentication, package manager and AGENTS.md. Use current Amplitude docs and report the source URLs. Before changing code, describe the files you will touch and any conflicting existing setup.

Read the complete reference app at https://github.com/amplitude/ampli-examples/tree/1c8efa7b11d3d91033198e8ba3ead0579170e5e7/browser/coding-tools/guides-surveys. Implement that standalone Vite report example, or merge the same behavior into the existing browser app. If you cannot read the reference, ask me to paste its files before implementing it. For the isolated development demo only: Use @amplitude/unified 1.1.43 with one browser-only initAll call after Start demo and top-level serverZone. Keep the engagement integration enabled, request replay sampleRate 0, and disable analytics autocapture for this manual-event sample. Set synthetic user build-web-guides-demo after initialization. Do not call standalone engagement.init, boot or forwardEvent in addition to the Unified integration. Keep a stable #export-report DOM target. Track Report Exported with report_period 7d once per export. Do not claim plugin setup or a preview proves delivery or a saved answer. Preserve auth, consent, existing analytics and routes when merging.

In an existing app, use its existing analytics client, confirmed user identity, consent lifecycle, and enabled products. Do not copy the demo synthetic IDs or its sample/skip choices into that client. If the existing installation is not Unified, propose a documented compatible integration before changing initialization; never add a parallel client. Preserve routes, authentication, replay privacy, consent and other product setup. Do not replace whole shared files or reset identity while authentication is anonymous or loading. Read only public client configuration through VITE_ variables; never use server or admin credentials. Keep .env.local ignored. Preserve the lockfile and frozen-install workflow. Report what was built, what was tested locally, and which service-side outcomes remain unverified. Stop the affected claim if current docs disagree; record both sources rather than guessing.

MCP. Ask the connected agent to inspect the project's existing event names before adding Report Exported. If your taxonomy already defines this action, use its established name consistently in the app and product configuration.

3. Review the proposed changes

Inspect the diff before accepting the implementation. I check the initialization and privacy paths before the visible UI, because a working button doesn't prove the integration is configured correctly.

Terminal
git diff -- package.json pnpm-workspace.yaml index.html main.js .gitignore AGENTS.md
git status --short

Confirm the agent uses initAll, sets serverZone at the top level, initializes once per page load, and preserves the existing client if the app already has one. Check that .env.local is ignored. Vite embeds VITE_ values in the browser bundle; these values may contain client ingestion or client deployment keys, never server, admin, or secret credentials.

The complete example uses a fixed synthetic identity. In an existing app, connect your confirmed application identity and consent lifecycle. Don't overwrite your login handler, navigation, or another product's instrumentation with this demo.

4. Check the complete reference implementation

These are complete files for the new standalone example. Compare them with the agent's result. In an existing app, merge the relevant imports, handlers, configuration, and DOM targets without replacing unrelated files.

The Unified SDK installation guide supplies the SDK setup. This example pins the version documented in the 1.1.43 API reference. Its initialization returns a plain promise, so the code awaits initAll directly.

package.json
{
  "name": "amplitude-build-guides-surveys",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "packageManager": "pnpm@10.33.2+sha512.a90faf6feeab71ad6c6e57f94e0fe1a12f5dcc22cd754db40ae9593eb6a3e0b6b12e3540218bb37ae083404b1f2ce6db2a4121e979829b4aff94b99f49da1cf8",
  "engines": {
    "node": ">=22.12.0",
    "pnpm": ">=10.21.0"
  },
  "scripts": {
    "dev": "vite --host 127.0.0.1",
    "build": "vite build",
    "preview": "vite preview --host 127.0.0.1"
  },
  "dependencies": {
    "@amplitude/unified": "1.1.43"
  },
  "devDependencies": {
    "vite": "8.3.4"
  }
}
pnpm-workspace.yaml
minimumReleaseAge: 10080
trustPolicy: no-downgrade
.gitignore
node_modules/
dist/
.env.local
.env.qa
.env.example
VITE_AMPLITUDE_API_KEY=
VITE_AMPLITUDE_SERVER_ZONE=US
index.html
<!doctype html>
<html lang="en">
  <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Usage report demo</title></head>
  <body>
    <main>
      <h1>Usage report</h1>
      <p>84 actions in the past 7 days.</p>
      <button id="start">Start demo</button>
      <button id="export-report">Export report</button>
      <p>Local exports: <span id="exports">0</span></p>
      <p>Use #export-report as the guide target.</p>
      <p id="status" role="status">Demo stopped. Use synthetic data only.</p>
    </main>
    <script type="module" src="/main.js"></script>
  </body>
</html>
main.js
import * as amplitude from "@amplitude/unified";

const apiKey = (import.meta.env.VITE_AMPLITUDE_API_KEY || "").trim();
const zone = (import.meta.env.VITE_AMPLITUDE_SERVER_ZONE || "US").trim();
const status = document.querySelector("#status");
const startButton = document.querySelector("#start");
const exportButton = document.querySelector("#export-report");
let started = false;
let starting;
let exports = 0;

exportButton.addEventListener("click", () => {
  exports += 1;
  document.querySelector("#exports").textContent = String(exports);
  if (started) amplitude.track("Report Exported", { report_period: "7d" });
});

startButton.addEventListener("click", () => {
  if (starting) return;
  if (!apiKey || !["US", "EU"].includes(zone)) {
    status.textContent = "Add the development configuration and restart Vite. Local export still works.";
    return;
  }
  startButton.disabled = true;
  starting = start().catch(() => {
    status.textContent = "Setup failed. Check configuration and connectivity, then reload.";
  });
});

async function start() {
  await amplitude.initAll(apiKey, {
    serverZone: zone,
    analytics: { autocapture: false },
    sessionReplay: { sampleRate: 0 },
    engagement: {},
  });
  amplitude.setUserId("build-web-guides-demo");
  started = true;
  status.textContent = `SDK initialized; device ${amplitude.getDeviceId()}. Verify identity and visible guide separately.`;
}

Install from the checked-in manifest. Commit the generated lockfile, and use a frozen install in repeatable builds.

Terminal
corepack enable
corepack prepare pnpm@10.33.2 --activate
pnpm install
cp .env.example .env.local

Fill in .env.local with the development configuration. The client key is public and limited to ingestion, but keeping it in local configuration avoids tying shared source to one project. See Vite's environment-variable reference for rebuild and browser-exposure behavior.

5. Configure the development project

Enable guides and surveys for the development project. Start the configured app and export once so Report Exported is available when you choose its event trigger. Then create a guide and a separate survey in the builder.

Implementation prompt
Guide: Report export help
Form factor: Pin
Target CSS selector: #export-report
Heading: Export your usage report
Body: Export this seven-day report to review the usage total.
Action: Got it, complete the guide
Where: the exact development report URL
Trigger: Immediately

Survey: Report feedback
Form factor: Modal
Question: How useful was this report?
Answer: Rating, five stars
Action: submit the answer and complete the survey
Where: the exact development report URL
Trigger: On event tracked, Report Exported
Test user: build-web-guides-demo

Use each experience's preview controls to inspect it on the report page. Verify the preview identity and URL, then check the visible pin or survey. Close or complete the guide before exporting so it doesn't interfere with the survey check.

For a separate delivery check, add the synthetic user under Test users and set each experience to Testing. Testing confines delivery to the specified users and ignores global throttles, but per-user completion and dismissal limits still apply. Leave production publication for your rollout decision.

The integration handles engagement initialization and event forwarding. Adding standalone boot or forwardEvent calls would create a second setup path. The sample requests a zero replay sample rate; check the project's remote replay settings and preserve consent and privacy controls when merging.

6. Run the report

Start Vite and open the local URL it prints. Export once before starting the SDK to confirm the report remains usable when analytics is unconfigured. Then tap Start demo once and perform the product check below.

Terminal
pnpm dev

After the development check, stop the server and verify the production build. Restart Vite after changing environment configuration.

Terminal
pnpm install --frozen-lockfile
pnpm build

Verify it worked

  1. With the key blank, export locally and tap Start demo. Confirm the warning and no SDK initialization requests.
  2. Fill in the development configuration, restart Vite, and start once. Confirm that the guides SDK loads. The initialized status only establishes SDK setup.
  3. Preview the guide on the exact report URL. Confirm its pin points to Export report, and check the expected synthetic identity in the debugger before testing its action.
  4. Close preview and check separate Testing-status delivery for the specified user. Complete or dismiss the guide, export once, and confirm the rating survey appears after Report Exported.
  5. Submit a synthetic five-star answer. In Responses, choose the test's date range and turn off Ignore test/preview data. Confirm the rating, user ID, and timestamp.
  6. In User Lookup, confirm the report event belongs to the same tester. Keep browser rendering, event forwarding, visible survey delivery, and the saved response as separate checks. Don't broaden the audience until the relevant checks pass.

Troubleshooting

Start demo asks for configuration. Fill in .env.local and restart Vite. The guard deliberately leaves the SDK stopped when the required client configuration is missing or the region isn't US or EU.

The status says initialized, but nothing appears. Check project enablement, identity, exact URL, trigger, selector, and per-user limits. Initialization doesn't establish targeting or display.

The pin points to the wrong place. Inspect the rendered #export-report target. Keep its identifier unique and stable; don't target generated styling classes that change on rebuild.

The export reaches analytics but no survey appears. Confirm the event trigger names Report Exported, the same browser integration forwards it, the guide is closed, and the test user's limits permit delivery.

A survey is visible but Responses is empty. Submit the answer and check project, date range, and the test/preview filter. A preview or a locally forwarded event doesn't establish a saved response.

The agent added a second SDK or broke an existing route. Review the repository's original entry point and merge into its existing client. Keep one Amplitude initialization for this app; don't combine this example with a second script loader or independently initialized browser package.

FAQ

Does Codex CLI need my Amplitude secret key?

No. The browser app uses a public project API key. An MCP connection authenticates separately. Keep server deployment keys, admin tokens, and secret keys out of browser configuration and committed source.

Can I use this in an existing React or Next.js app?

Yes. Have the agent merge the behavior into the existing browser entry point and client. Preserve authentication, consent, and routing. The standalone reference uses Vite; a server-rendered app needs its framework's browser-only lifecycle rather than a direct copy of the Vite entry file.

Does a successful build prove the product works?

No. A build checks that the browser app can be bundled. The Verify section separately checks the rendered experience, processed activity, and the product result. Record which checks you performed before broadening the audience.

Will the agent make the same changes every time?

Treat its output as a change to review. Keep the explicit prompt, pinned reference, and repository rules, then compare its diff with the required behavior. An agent can propose a different implementation without establishing that its SDK calls or product settings are correct.

Full code reference

Step 4 contains every application and configuration file. Download the complete example with its lockfile and setup README. Keep .env.local ignored and commit the package lock when you customize the example.

Keep the rules in your repository

Append this guidance to the repository's existing AGENTS.md. Preserve its current instructions. Codex CLI supports repository guidance; restart the interactive session after editing it when necessary to load the updated instructions.

AGENTS.md
## Amplitude report instrumentation

Use the existing Amplitude client and current official SDK documentation.
Initialize once, after the application's consent requirements are met.
Keep serverZone at the Unified options' top level.
Keep private and server credentials out of browser configuration and source.
Preserve authentication, routes, other product setup, and the lockfile.
For the isolated development demo only:
Use @amplitude/unified 1.1.43 with one browser-only initAll call after Start demo and top-level serverZone. Keep the engagement integration enabled, request replay sampleRate 0, and disable analytics autocapture for this manual-event sample. Set synthetic user build-web-guides-demo after initialization. Do not call standalone engagement.init, boot or forwardEvent in addition to the Unified integration. Keep a stable #export-report DOM target. Track Report Exported with report_period 7d once per export. Do not claim plugin setup or a preview proves delivery or a saved answer. Preserve auth, consent, existing analytics and routes when merging.
In an existing app, use its existing analytics client, confirmed user identity, consent lifecycle, and enabled products. Do not copy the demo synthetic IDs or its sample/skip choices into that client. If the existing installation is not Unified, propose a documented compatible integration before changing initialization; never add a parallel client.
Separate build/local checks from processed service results. If sources conflict,
record both and pause the affected claim before publishing the implementation.

What this unlocks

Product analytics shows Report Exported for the same identity used by this example. The guides and surveys integration adds context around that report action. Keep the event name and report-period property consistent as you extend the app.

Next steps

Amplitude is the leading all-in-one analytics platform for building better products. Product and engineering teams use Amplitude to connect product analytics and web analytics with session replay, heatmaps, experimentation, feature flags, and guides and surveys in one unified workflow, with AI agents working across all of it. Learn how Amplitude is building self-improving products with Wave, helping teams turn product data into PRs.

Build the report, then check the result

Connect a development project and verify the report action before you roll out the integration.