Skip to content
All projectsProductStarted at Pandooin, ongoing

Custour

An AI travel planner that turns a sentence about your trip into a full itinerary, with real flights, hotels and places attached.

Role
Product Engineer
Organisation
PT Teknologi Pandu Wisata (Pandooin)
Period
Started at Pandooin, ongoing
Stack
  • Next.js
  • TypeScript
  • NextAuth v5
  • TanStack Query
  • SERP API
  • Viator
  • n8n
  • Google Maps
  • Tailwind
  • GitHub Actions
  • pm2
  • Vitest

Short explanation

Custour turns a sentence about a trip into a complete itinerary. You describe where you want to go, when, and with whom; the AI returns a multi-day plan with flights, hotels, activities and places attached — and then you edit it, because no generated plan survives contact with an actual traveller.

  • Chat-first planning: natural language in, a full itinerary out.
  • Real inventory behind the plan — flights and hotels come from live search, not from the model's imagination.
  • Editable itineraries: add, remove and reorder everything after generation.
  • Google Maps for routing and place context across the plan.

Why it exists

Planning a multi-day trip means holding flights, hotels, activities and routing in your head at once across a dozen browser tabs. The tools that exist either search one category well or generate itineraries that dissolve the moment you check whether the flights are real.

The bet is that those two halves belong together: generation is only useful if what it generates is bookable.

The product side

I worked on Custour as a product engineer, so my part started before the code. I wrote the PRD, and we decided the features as a team by starting from what a traveller needs.

The planning itself is automated. One chat message starts an n8n workflow that writes the itinerary and pulls live flights and hotels from SERP API, so the user does not have to search for each one.

We picked the tools to fit an MVP: n8n to run the AI workflow, SERP API for flights and hotels, and several external APIs for the rest.

The interface had to be easy for someone planning a holiday. The itinerary appears as it is being generated, anything in it can be added, removed or reordered afterwards, and places show on a map.

Tech stack, and why

Next.js and TypeScript on the client. This repository is deliberately frontend-only — it consumes a REST API it doesn't own, which meant the interesting problems were about boundaries rather than about the database.

NextAuth v5 for authentication, with Google OAuth and credentials. The session JWT is injected into axios interceptors, so every outbound call carries auth without each service module remembering to add it, and a proxy middleware guards the private routes at the edge rather than inside each page.

TanStack Query owns all server state. With an AI response that streams, cache invalidation is most of the actual work.

n8n as the webhook layer in front of the AI, SERP API for flight and hotel search, and Google Maps through two libraries because the newer one doesn't cover everything the older one does yet.

Features

Chat to itinerary

The whole product in one interaction.

A session starts with destination, dates, travellers and origin, and becomes a conversation. Each chat in the sidebar is a trip plan — the history is the product, not a log of it.

Real flight and hotel search

SERP API results, not generated text that looks like results.

This is the line between a demo and a product. A generated plan that names a flight which doesn't exist is worse than no plan, because it costs the user the time to find out.

Maps and routing

Places in an itinerary are places on a map.

Delivery pipeline

Four GitHub Actions workflows, split by intent. Two run on pull requests into dev and beta and do nothing but build — a gate, so a branch that doesn't compile can't merge. Two run on push to those branches and deploy.

Deployment isn't push-to-Vercel. The workflow opens an SSH session to the server, pulls, clears the build directory, reinstalls, rebuilds and restarts the process under pm2. That's more moving parts than a managed platform, and it's also the reason I know what each of those steps is for.

Two environments

dev and beta, each with its own branch and its own box.

Branch-per-environment keeps the promotion path obvious: what's on beta is what was on dev, and nothing reaches either without passing its build gate first.

The part that was actually hard

Owning the frontend of an API you don't control. Every shape change upstream is a change you find out about at runtime, so the service layer is 11 separate modules with their own types rather than one shared client — when something moves, the blast radius is one file.

The other one is streaming. An AI response that arrives progressively fights with a cache that wants to know when a query is done. Getting the itinerary to feel like it's being written, while still landing in a cache entry the rest of the app can read, took more iterations than the generation itself.