Wander AI documentation
Run it, point it at your own accounts, ship it under your own brand. Written against version 1.8.0 of the download you get on CodeCanyon.
- Version v1.8.0
- Updated Sep 2026
- Platform Flutter
- Stack Flutter · AI
- Demo APKwander-ai-v1.8.0 · 77.7 MB
On this page
Introduction
Wander AI is an AI-powered travel itinerary platform. The product is shipped as a monorepo with two independent sub-projects:
- Admin panel (
admin/) — a Next.js dashboard plus the mobile REST API at/api/mobile/*. - Mobile app (
apps/) — a Flutter iOS and Android client that consumes the mobile API.
They share no code and no JS workspace manager. Each one has its own toolchain, dependencies, and .env. This guide covers both halves.
Repository structure
wander-ai/
├── admin/ Next.js admin panel + /api/mobile/* REST endpoints
├── apps/ Flutter mobile client (iOS + Android)
├── docs/ This documentation
├── CLAUDE.md Engineering notes (for Claude Code)
├── README.md Quick-start
└── package.json Root convenience scripts (no dependencies)Root convenience scripts (run from repo root):
npm run dev:admin # Next.js dev server
npm run build:admin # Next.js production build
npm run dev:app # flutter run (in apps/)
npm run analyze:app # flutter analyze
npm run gen:app # build_runner codegen
npm run build:app:android # Android release APK
npm run build:app:ios # iOS release buildAdmin panel
A comprehensive administrative dashboard for managing the Wander AI platform. It provides tools for user management, trip monitoring, promotional content management, and system configuration. Built with Next.js, Shadcn UI, and Drizzle ORM.
Key features
- Dashboard overview: real-time analytics and system status.
- Admin users: manage admin accounts and secure access.
- App user management New: searchable, paginated list of mobile users with per-user detail (recent trips, Concierge Chat history, likes given), one-click Comp Pro toggle, and cascade-safe delete — replaces every “open Drizzle Studio” support workflow.
- Trip management: oversee user-generated trips, AI itineraries, and budgets.
- Promoted destinations: manage featured locations shown in the mobile app.
- Prompt tuner: edit the system prompts that drive both itinerary generation and the new Concierge Chat refinement (loaded from
settings.system_promptandsettings.refine_system_prompt). - API key management: manage OpenAI / Gemini / Google Maps / OpenWeatherMap keys from the UI. Honors a
IS_DEMO=trueenv flag — when set, real key values are never sent to the client and editing is disabled with a teal “Demo mode” banner (server-side mutations are also blocked). - Settings management: configure dynamic app settings directly from the UI.
- Modern UI/UX: clean interface using Shadcn UI components and Tailwind CSS.
Tech stack
Admin setup
Prerequisites
- Node.js 24 — declared in
admin/package.json(engines.node: "24.x"). Vercel reads the same field, so local and deployed builds match. Node 22 still runs the panel, butnpm installprints an engine warning. - npm (a
package-lock.jsonis included) - PostgreSQL database (e.g., NeonDB)
Installation
- 01Open a terminal in the repo root.
- 02
Install admin dependencies:
terminalnpm --prefix admin install
Admin configuration (.env)
Create a file named .env inside the admin/ directory and configure the following variables:
gemini).true on a public demo deployment to hide all API key values, disable the editor, and reject mutation requests server-side. Leave unset for private deployments.DATABASE_URL=
AUTH_SECRET=
BLOB_READ_WRITE_TOKEN=
MOBILE_API_KEY=
AI_PROVIDER=
JWT_SECRET=
OPENWEATHERMAP_API_KEY=
# IS_DEMO=true public demo deployments onlyWeather API
Wander AI provides weather forecasts for trip destinations using the OpenWeatherMap One Call API 3.0.
Get an API key
- 01Create a free account at openweathermap.org.
- 02Navigate to API keys in your dashboard and copy your key.
- 03Subscribe to the One Call API 3.0 plan (free tier includes 1,000 calls/day). A basic OWM key alone will not work.
Configure the key
- Admin panel (recommended): go to
API Key Managementand enter your key in the OpenWeatherMap API Key field. - Environment variable (fallback): set
OPENWEATHERMAP_API_KEYinadmin/.env. Admin panel value takes priority.
How it works
- Mobile app calls
GET /api/mobile/weather?lat=...&lng=...&startDate=...&endDate=.... - Backend proxies the request to OpenWeatherMap, keeping the API key off the device.
- Returns up to 8 days of daily forecast (temperature, conditions, humidity) filtered to the requested range.
- If trip dates are more than 8 days in the future, no forecast data is returned.
Database schema
The admin panel uses Drizzle ORM with a PostgreSQL database. Migrations live in admin/drizzle/. Manage them via npx drizzle-kit push / npx drizzle-kit generate / npx drizzle-kit studio.
Running the admin
Development mode
npm run dev:admin
# or, from inside admin/
npm run devThe app will be available at http://localhost:3000.
Production mode
npm run build:admin
npm --prefix admin startAdmin customization
Changing the app name
- 01Open
admin/package.jsonand update the"name"field. - 02Update the metadata title in
admin/app/layout.tsx.
Changing colors
- 01Open
admin/app/globals.css. - 02Locate the
:rootblock. - 03Update the
--primaryHSL value (default175 33% 31%, teal).
--ring to match the primary color so focus states stay consistent.Mobile app
WanderAI is an intelligent travel-planner mobile app. It leverages Generative AI and the Google Places API to create detailed, visual itineraries.
Key features
affiliate_cta_clicked) fire fire-and-forget from the mobile app to POST /api/mobile/analytics, which forwards to Vercel Analytics. Track conversion by partner, kind, and destination in your Vercel dashboard.Trip details and refinement
The active itinerary screen and any owned saved trip surface a Refine ✦ floating pill (next to or replacing the Save Trip FAB). Tapping it opens the Concierge Chat sheet at 75% height, hydrates prior messages from GET /api/mobile/trips/:id/messages, and lets the user converse with the same OpenAI / Gemini engine that generated the trip. Refinements are saved server-side to the new trip_messages table; previous-snapshot rollback gives an instant Undo without losing the conversation.
Mobile prerequisites
- Flutter 3.44 or newer (Dart 3.12+) — required by GoRouter 18. Built and tested on Flutter 3.47 / Dart 3.13.
- Android Studio / VS Code, with the Android SDK Platform 37 installed
- Xcode and CocoaPods for iOS builds
Platform targets
minSdk 24 (Android 7.0), targetSdk / compileSdk 37apps/android/app/build.gradle.ktsapps/ios/Podfile and IPHONEOS_DEPLOYMENT_TARGET in the Xcode projectminSdk can go down to 23. If you lower the iOS deployment target, run cd apps/ios && pod install afterwards so the pods are rebuilt against it.Mobile setup
Install dependencies
cd apps
flutter pub getConfigure environment (dart-define)
The mobile app uses Flutter’s --dart-define-from-file for build-time configuration — no .env loader, no runtime asset, no async init. Values resolve to compile-time constants via String.fromEnvironment and are centralized in apps/lib/core/config/env.dart.
Copy apps/env/example.json to apps/env/dev.json (gitignored) and fill in your values:
{
"WANDER_API_KEY": "your_api_key_here",
"GOOGLE_CLIENT_ID": "your_google_client_id_here",
"REVENUECAT_API_KEY_IOS": "appl_...",
"REVENUECAT_API_KEY_ANDROID": "goog_...",
"USE_MOCK_IAP": true,
"ENABLE_ADMOB": false,
"BOOKING_AFFILIATE_ID": "",
"SKYSCANNER_AFFILIATE_ID": "",
"GETYOURGUIDE_PARTNER_ID": ""
}Important:
WANDER_API_KEY: must matchMOBILE_API_KEYinadmin/.env— the backend rejects requests without it.GOOGLE_CLIENT_ID: from your Google Cloud Console (used for Google sign-in).BOOKING_AFFILIATE_ID/SKYSCANNER_AFFILIATE_ID/GETYOURGUIDE_PARTNER_ID: optional. Leave empty to ship unattributed search links. When you have approved partner accounts, paste in the IDs and rebuild — no admin or backend change needed.- Create separate files per environment — e.g.
env/dev.json,env/prod.json. Everything exceptexample.jsonis gitignored. - Since values are compiled in, switching environments means a rebuild. Truly dynamic config (AI provider, prompt, OpenAI key) is served from the backend at
/api/mobile/config, so the build-time file only holds bootstrap values.
RevenueCat (in-app purchases)
WanderAI uses RevenueCat for In-App Purchases and Subscriptions. You need a RevenueCat project linked to your App Store and Google Play apps.
- 01Create a RevenueCat account.
- 02Create a new project for your app.
- 03
Critical — configure entitlements, offerings and products
- Create an Entitlement identifier named
pro(lowercase). - Create an Offering (typically named
default) and attach your products to it. - Ensure your Google Play / App Store product IDs match exactly what you enter in RevenueCat. See setup guides for iOS and Android.
- Create an Entitlement identifier named
- 04Get your API keys from RevenueCat (one per platform).
- 05
Add them to
apps/env/dev.json(orprod.json):apps/env/dev.json{ "REVENUECAT_API_KEY_IOS": "appl_YOUR_IOS_KEY", "REVENUECAT_API_KEY_ANDROID": "goog_YOUR_ANDROID_KEY", "USE_MOCK_IAP": false }
Set USE_MOCK_IAP=true to test the UI without real store connections.
PDF export
Pro users open Export as PDF from the “more actions” sheet on the itinerary or trip details screen. The preview screen offers Share and Print through the OS. Everything is rendered on the device, so there is no endpoint to deploy and no key to configure.
What’s in the file
- Cover: trip photo, destination, dates, and travellers / budget chips.
- Bookings grouped by date, then the day-by-day itinerary (time, place, type, description, photo, and a tappable Open in Maps link), then the packing list with checkboxes.
- Page-numbered footers, in the app’s own font and colours. Page size is US Letter for US/CA/MX/PH locales and A4 everywhere else.
Where to customise it
Affiliate booking and analytics
Each trip details screen surfaces two booking CTAs (Hotels via Booking.com, Flights via Skyscanner), pre-filled with the trip’s destination and dates. Every activity bottom sheet adds a Find Tours & Tickets CTA that searches GetYourGuide by place name. The URL builders live in apps/lib/core/services/affiliate_service.dart; swap partners or tweak query params there.
Wiring partner IDs
- 01Sign up for the partner programs you want to monetize — Booking.com Affiliate Partner, Skyscanner Partners, GetYourGuide Partner.
- 02
Once approved, copy your IDs into
apps/env/dev.json(andprod.json):apps/env/prod.json{ "BOOKING_AFFILIATE_ID": "1234567", "SKYSCANNER_AFFILIATE_ID": "your_associate_id", "GETYOURGUIDE_PARTNER_ID": "your_partner_id" } - 03Rebuild with
--dart-define-from-file=env/prod.json. Every CTA tap now carries your attribution.
Analytics and conversion tracking
CTA taps emit an affiliate_cta_clicked event with partner, kind, destination, trip_id, place_name, and activity_type properties. The mobile app POSTs events to the admin’s POST /api/mobile/analytics route, which validates the payload with Zod and forwards it to Vercel Analytics via @vercel/analytics/server (auto-tagging source: "mobile").
Find the data in your Vercel project → Analytics → Events. Filter by event name and slice by partner / kind / destination to compare which booking surfaces drive the most clicks.
To track any other mobile event, call AnalyticsService.track('your_event_name', properties: { ... }) from anywhere in the Flutter code — it’s fire-and-forget and never throws.
Project structure
The mobile app follows a feature-first architecture for scalability.
apps/lib/
├── core/ Core utilities, theme, router, shared services
├── features/ Feature-based modules
│ ├── auth/ Authentication (email, Google, Apple)
│ ├── bookings/ Flights, hotels and reservations per trip
│ ├── community/ Public trip feed
│ ├── expense/ Trip expense tracker
│ ├── explore/ Discovery / home screen
│ ├── onboarding/ Vibe-check onboarding flow
│ ├── packing/ Packing list per trip
│ ├── pdf_export/ Pro: trip PDF (builder, preview, share/print)
│ ├── refine/ Concierge Chat refinement
│ ├── settings/ App settings (theme, etc.)
│ ├── shell/ Main navigation shell
│ ├── subscription/ RevenueCat paywall
│ ├── trip/ Trip generation, itinerary, saved trips
│ └── weather/ Forecast for trip days
└── main.dart Application entry pointUnit tests live in apps/test/, mirroring the same structure. Run them with cd apps && flutter test.
Mobile customization
Changing the app name
Update the name field in apps/pubspec.yaml. For the display label on the home screen, edit apps/android/app/src/main/AndroidManifest.xml and apps/ios/Runner/Info.plist.
Changing the primary color
- 01Open
apps/lib/core/theme/app_theme.dart. - 02Find the
MaterialThemeclass. - 03Update
primaryin thelightSchemeanddarkSchememethods.
// Example in app_theme.dart
static ColorScheme lightScheme() {
return const ColorScheme(
brightness: Brightness.light,
primary: Color(0xff356668), // <-- change this HEX
// ... other colors
);
}Running the mobile app
Pass your environment file with --dart-define-from-file. For convenience, add the flag to your IDE’s launch configuration (VS Code: .vscode/launch.json, Android Studio: Edit Configurations → Additional run args).
Android
cd apps
flutter run --dart-define-from-file=env/dev.jsoniOS
cd apps/ios
pod install
cd ..
flutter run --dart-define-from-file=env/dev.jsoniOS — Swift Package Manager: some native plugins (google_mobile_ads, RevenueCat, sign_in_with_apple) are CocoaPods-only, which conflicts with Flutter’s Swift Package Manager. If pod install or the iOS build fails with an SPM/CocoaPods error, disable SPM once for your Flutter install, then reinstall pods:
flutter config --no-enable-swift-package-manager
cd apps/ios && rm -rf Pods Podfile.lock && pod install && cd ..The remaining “plugins do not support Swift Package Manager” message is just an informational warning — the build still works through CocoaPods.
google_mobile_ads below 9.1.0: pubspec.yaml pins ">=9.0.0 <9.1.0" on purpose. Version 9.1.0 imports a private AdMob header, so a CocoaPods build fails with “Include of non-modular header inside framework module”. If you raise it and hit that error, pin it back and run pod update Google-Mobile-Ads-SDK.Production builds
flutter build apk --dart-define-from-file=env/prod.json
flutter build appbundle --dart-define-from-file=env/prod.json
flutter build ios --dart-define-from-file=env/prod.jsonChangelog
Every version published so far. Updates are free for the life of the item.
- Mobile — PDF Itinerary Export (Pro): new Export as PDF action in the "more actions" sheet on both the itinerary and trip details screens. Opens a preview with built-in Share and Print. The PDF includes a cover (photo, destination, dates, travelers, budget), bookings grouped by date, a day-by-day itinerary (time, place, type, rating, description, thumbnail, "Open in Maps" link), the packing list with checkboxes, and page-numbered footers.
- Mobile — On-brand, lightweight output: uses the app's Plus Jakarta Sans font and Warm Wanderer colors. Activity thumbnails are resampled to print resolution, so a typical trip exports in a few hundred KB. Noto fallback fonts cover accented, Cyrillic, Japanese, Korean, Chinese and Thai text, and the large CJK/Thai fonts are only downloaded when a trip uses them. Layout runs on a background isolate to keep the UI smooth.
- Mobile — Paywall: "Export Trips as PDF" added to the Pro feature list. Free users tapping export are taken to the paywall.
- Mobile — Dependencies: added pdf and printing. No Android manifest or iOS Info.plist changes required.
- Mobile — Tests: first unit test suite (apps/test/) covering PDF layout, page flow, background-isolate rendering, text/file-name helpers, and community-feed parsing. Run with flutter test.
- Mobile — Platform targets: Android minSdk 24 with targetSdk/compileSdk 37 (Android 17), and iOS deployment target raised to 17.0. Set explicitly in apps/android/app/build.gradle.kts, the Podfile, and the Xcode project.
- Mobile — Dependency refresh: every package updated — GoRouter 18, Riverpod 3.4, Freezed 4, purchases_flutter 10 (RevenueCat), sign_in_with_apple 8, package_info_plus 10, flutter_map 8.3 and more. google_mobile_ads is held at 9.0.x on purpose: 9.1.0 fails CocoaPods iOS builds because its headers import a private AdMob header.
- Admin — Dependency refresh: Next.js 16.3, React 19.3, Tailwind CSS 4.3, Zod 4.6, openai 7, @vercel/blob 2.8, Drizzle Kit 0.31.10, TypeScript 6. The admin now declares Node 24 in engines, which is also what Vercel will build with.
- Fix — Community feed: feed cards showed “by Unknown”, never showed the liked state or trip length, and the traveler filter did nothing — the app read creator/travelers/isLiked while the API returns author/travelerType/isLikedByMe. The client now reads both shapes and derives trip length from the dates. No backend change or redeploy needed.
- Fix — Trip generation screen: a failed generation (no connection, missing API key) was reported as an unhandled exception instead of the screen's retry state, and the screen could overflow on short screens, in landscape, or with large system text. It now scrolls instead.
Support and licensing
If you need any assistance, have questions, or want to report a bug, please contact us:
What support covers
- Six months of support from purchase, extendable at checkout
- Support covers bugs in the template and questions about how it is put together
- It does not cover custom feature work, third-party API changes or store review outcomes
- The Regular licence covers one free end product. Charging users for the app itself needs the Extended licence