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
- Top headlines across general, world, business, tech, sports, and more
- Search news by free-form keyword
- Per-user dedup so users never see the same headline twice
- Gemini Flash with Google Search tool for fresh, grounded results
- Direct deep-links to Google News search for each story
A personalized news chatbot that delivers fresh headlines over RCS. Users browse top stories by category, search by keyword, and never see the same headline twice — all powered by Gemini Flash with Google Search grounding.
This guide walks you from a fresh clone to live news in your messages app in under 10 minutes.
What you'll build
- A Pinnacle RCS agent that returns rich news cards on demand
- A Gemini-grounded fetcher that pulls today's headlines in real time
- Per-user dedup so the same article never shows up twice
- A clean separation between transport, news fetching, and presentation
Prerequisites
- Node.js 18+
- A Pinnacle account — sign up. Add an RCS test agent for development
- An API key and a webhook signing secret
- A Gemini API key — Google AI Studio
1. Clone and install
git clone https://github.com/pinnacle-samples/Vantage
cd Vantage
npm install2. Configure environment
cp .env.example .envPINNACLE_API_KEY=your_pinnacle_api_key_here
PINNACLE_AGENT_ID=your_agent_id_here
PINNACLE_SIGNING_SECRET=your_signing_secret_here
GEMINI_API_KEY=your_gemini_api_key_here
TEST_MODE=false
PORT=3000The Gemini key is required — without it the news fetcher returns an empty array and the agent will tell users no articles were found.
3. Expose your webhook
ngrok http 30004. Connect the webhook
In the Webhooks dashboard:
- Add
https://<your-tunnel-domain>/webhook - Attach it to your RCS agent
- Copy the signing secret into
PINNACLE_SIGNING_SECRET
5. Run it
npm run devSend MENU or START to your agent. You'll see the Vantage main menu — tap a category or type a topic and the agent fetches today's top stories live.
How the pieces fit together
Vantage/
├── 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 # Agent — formats articles into cards
│ ├── cache.ts # Optional disk cache for repeat queries
│ └── news/
│ ├── fetch.ts # Gemini + Google Search article fetcher
│ └── type.ts # Article, NewsPayload types
Action handlers
| Action | What it does |
|---|---|
showMainMenu | Landing card + category quick replies |
sendExplorePage | Top headlines / category browse view |
sendArticles | Fetches and renders articles for a category or query |
sendHelp | Help text and search hints |
The router also handles free-form text — typing a topic like "climate change" triggers sendArticles with that as the query.
How freshness works
lib/news/fetch.ts uses Gemini Flash with the google_search tool plus a strict prompt:
- Hard cutoff: only articles from today or yesterday
- Adds
after:<yesterday>to the search query - Rejects week-old roundups and evergreen explainers
- Trims headlines and summaries to RCS card field limits
Headlines a user has already seen are tracked in an in-memory Map keyed by phone number. For production, swap this for Redis — the function signatures are async so you can drop in a real client without touching call sites.
Customize categories
Categories are defined in lib/agent.ts. Add or remove entries to match your audience — the explore page rebuilds automatically.
Going to production
- Replace the in-memory dedup
Mapinlib/news/fetch.tswith Redis - Set
TEST_MODE=falseand submit your agent for carrier approval - Add scheduled push notifications by wiring a cron to
agent.sendMessagefor each subscriber
Resources
- Repo: github.com/pinnacle-samples/Vantage
- Docs: docs.pinnacle.sh
- Gemini API: ai.google.dev
- Support: founders@trypinnacle.app

