Back to samples
Zenith Airways logo
Travel

Zenith Airways

Boarding passes, upgrades, gate changes — over RCS

3 min read

Note: the visuals in this demo recording have since been refreshed with sharper brand assets. The conversation flow is identical to what you'll get from a fresh clone.

What's inside

  • Pull up upcoming and past flights from one menu
  • Boarding pass card with QR, gate, seat, and boarding time
  • Request seat upgrades inside the conversation
  • Per-user session state for multi-step flows
  • Hooks for proactive gate-change and delay alerts

A flight management chatbot that lives inside RCS. Travelers can view their flights, pull up boarding passes, request bid-style seat upgrades, check flight status, cancel flights, and opt in to proactive notifications — without ever opening an app.

This guide walks you from a fresh clone to a working demo on your phone in under 10 minutes.

What you'll build

  • A Pinnacle RCS agent that walks travelers through trip management
  • A typed Express webhook with per-user session state for multi-step flows
  • A boarding pass card with QR, gate, seat, and boarding time
  • A bid-for-upgrade flow that takes the user from main menu to confirmation in three taps
  • Hooks for proactive flight notifications

Prerequisites

1. Clone and install

Bash
git clone https://github.com/pinnacle-samples/Zenith-Airways
cd Zenith-Airways
npm install

2. Configure environment

Bash
cp .env.example .env
env
PINNACLE_API_KEY=your_pinnacle_api_key_here
PINNACLE_AGENT_ID=your_agent_id_here
PINNACLE_SIGNING_SECRET=your_signing_secret_here
TEST_MODE=false
PORT=3000

3. Expose your webhook

Bash
ngrok http 3000

4. Connect the webhook

In the Webhooks dashboard:

  1. Add https://<your-tunnel-domain>/webhook
  2. Attach it to your RCS agent
  3. Copy the signing secret into PINNACLE_SIGNING_SECRET

5. Run it

Bash
npm run dev

Send MENU or START to your agent. You'll see the Zenith Airways landing card with My Flights, Get Boarding Pass, View Upgrades, and Cancel Flights buttons.

How the pieces fit together

Zenith-Airways/
├── server.ts              # Express bootstrap
├── router.ts              # /webhook POST — verifies + dispatches
├── lib/
│   ├── rcsClient.ts       # PinnacleClient instance
│   ├── baseAgent.ts       # Shared send + typing helpers
│   ├── typing.ts          # Fire-and-forget typing indicator
│   ├── agent.ts           # ZenithAgent — every action handler
│   ├── data.ts            # Flights, upgrade options, agentInfo (brand)
│   ├── session.ts         # Per-user multi-step session state
│   └── types.ts           # Flight, BoardingPass, UpgradeOption

Action handlers

ActionWhat it does
showMainMenuLanding card with all entry points
viewMyFlightsUpcoming flights as a card carousel
getBoardingPassBoarding pass card with QR, gate, seat
checkFlightStatusOn-time / delay status for a chosen flight
viewUpgrades / startBiddingBrowse and bid on seat upgrades
viewCancelFlights / cancelFlight / confirmCancelFlightCancellation flow
enableNotifications / noThanksOpt in to proactive alerts

Customize the airline brand

agentInfo in lib/data.ts controls the airline name, tagline, and logo. Swap those fields and the main menu rebrands automatically.

flights is a static array per phone number. In production you'll fan out to your reservation system on demand:

TypeScript
async getFlightsByPhone(from: string): Promise<Flight[]> {
  return await reservationApi.lookupByPhone(from);
}

Sessions and multi-step flows

lib/session.ts keeps a small Map<string, Session> so the agent remembers what the user was doing across messages — for example, which flight they're upgrading. Replace with Redis when you outgrow a single process.

Going to production

  • Set TEST_MODE=false and submit your agent for carrier approval
  • Wire getFlightsByPhone to your real reservation system
  • Add proactive notifications by calling agent.sendMessage(from, ...) from a cron or queue worker on gate changes / delays

Resources

© 2026 Pinnacle Software Development, Inc.