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
- Browse upcoming town halls and rallies with RSVP
- Policy positions across major issues, served as rich cards
- Volunteer signup with role selection
- Tiered donation flow with custom amount support
- Polling location lookup and voting info
A political campaign chatbot that runs entirely over RCS. Voters can RSVP to upcoming events, read policy positions, sign up to volunteer, donate at fixed or custom amounts, and check their voter registration — all from inside the messages app.
This guide walks you from a fresh clone to a working campaign demo on your phone in under 10 minutes.
What you'll build
- A Pinnacle RCS agent for voter outreach and engagement
- Tiered donation flow with a
processDonationconfirmation step - Custom donation amount input flow
- Event RSVP, volunteer signup, and policy position cards
- Voter registration check inside the conversation
Prerequisites
- Node.js 18+
- A Pinnacle account — sign up. Add an RCS test agent for development
- An API key and a webhook signing secret
1. Clone and install
git clone https://github.com/pinnacle-samples/Ascend-2028
cd Ascend-2028
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
TEST_MODE=false
PORT=30003. 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 Ascend 2028 landing card with Events, Policies, Donate, Volunteer, and Voting Info entry points.
How the pieces fit together
Ascend-2028/
├── 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 # AscendAgent — every action handler
│ └── data.ts # Events, policies, donation tiers, volunteer roles
Action handlers
| Action | What it does |
|---|---|
mainMenu / showMainMenu | Landing card with all entry points |
viewEvents / rsvpEvent | Browse and RSVP to town halls and rallies |
viewPolicies | Policy positions as a card carousel |
donate / processDonation | Tiered donation flow with confirmation |
customDonation | Free-form custom amount input |
volunteer / signUpVolunteer | Volunteer role picker and signup |
votingInfo / checkRegistration | Voter info + registration check |
Customize the campaign
lib/data.ts is where everything lives. Drop in your own:
upcomingEvents— town halls and rallies, with name, date, time, address, and capacitypolicyPositions— issues and stances, served as rich card carouselsvolunteerOpportunities— roles voters can sign up fordonationTiers— preset amounts and tier namesvotingInfo— registration and voting deadlines for your district
Custom donations
The agent has a pendingCustomDonations set for users in the middle of a custom donation flow — they tap "Other amount", the agent waits for a number, and then processDonation confirms. The sendStrictFormatMessage helper repeats donation buttons whenever a user sends free-form text outside of that flow, keeping the conversation on rails.
Going to production
- Set
TEST_MODE=falseand submit your agent for carrier approval - Wire donations to your real payment processor (Stripe, ActBlue, WinRed)
- Replace the in-memory state with Postgres
- Use proactive RCS messages for GOTV reminders, debate alerts, and event RSVPs
Compliance note
Political messaging is heavily regulated. Make sure to:
- Honor STOP and HELP keywords (the SDK does this for you)
- Comply with TCPA opt-in/opt-out rules
- Add required disclosures to fundraising messages — your campaign's compliance lead will know what's required in your jurisdiction
Resources
- Repo: github.com/pinnacle-samples/Ascend-2028
- Docs: docs.pinnacle.sh
- Dashboard: app.pinnacle.sh/dashboard
- Support: founders@trypinnacle.app

