Resistbot
Write to your officials from your phone. The bot finds them, writes the letter, and delivers it however each office accepts mail: its web form, Congress’s own message API, email, fax, or postal mail.
American civic life is a graph of relationships, and most of its edges don’t exist yet. We build the missing ones and keep them running.
Resistbot turns a text message into a letter in front of the right elected official, and it has answered on the same short code since 2017. Amendment is the layer on top: every bill in every legislature, the people who vote on them, and the tools organizations use to take positions, endorse, and raise money. Four engineers build and run both, on machinery we wrote ourselves and opened where we could. The discipline that keeps the texting up carries over to the AI, where model output has to pass graders in CI before it ships. We take on outside work, and we talk to anyone who wants to build on what’s here.
Write to your officials from your phone. The bot finds them, writes the letter, and delivers it however each office accepts mail: its web form, Congress’s own message API, email, fax, or postal mail.
Every bill in every legislature, the people who vote on them, AI summaries with a grading harness, and the primitives an organization needs to move people and money.
We write each conversation once. A forked RapidPro engine (we merge back upstream) and a forked Go gateway that abstracts 36 channel types run the same 773 flows on SMS, iMessage, web chat, Telegram, Messenger, and Instagram, plus Discord through a bridge we published. Apple Pay inside iMessage is its own service: stateless payment sessions encoded into the request ID (no database at all), a webhook path with no compute behind it, and a default-deny WAF whose Stripe allowlist is re-fetched from Stripe’s published ranges on every deploy.
Every office publishes a different contact form; half sit behind a CAPTCHA; they change without notice. Mercury is a generic form interpreter: a 12-verb YAML DSL with 28 meta-variables (every office splits a phone number differently), running in a Chrome fleet that scales from zero to a hundred on queue depth. It fingerprints six CAPTCHA families before solving, invokes the site’s own JavaScript callback after, proves success three different ways when a form gives no receipt, classifies failures into a 17-category taxonomy grown from production data, and carries a VCR-style replay layer so an engineer can fix a selector without ever re-submitting to a live office. About 1.2 million form-fill runs a month.
Discovery is the address book of American government: 13 official types, 22 party codes (including New York’s fusion tickets), 56 jurisdictions, and for each person an ordered list of how they can actually be reached. It encodes the legislatures that don’t fit, like New Hampshire’s 43 floterial districts, Puerto Rico’s dual representation, and Idaho’s NA/NB seats. It refreshes hourly from a dozen upstream sources and keeps a field-level provenance record, so a human correction survives the next automated import. The public half is contact-officials: 83 public-domain form definitions covering 47 states and D.C., written with meta-variables so election churn needs zero file changes. Opened in 2018 and still maintained.
278 scrapers over 59 jurisdiction modules feed 279,656 bills, 7,485 politicians, and 2,160 elections into one GraphQL API. Base classes cover the commercial code hosts most cities publish through, so municipal code lands on the same graph as federal and state law. Durable work runs on 14 Temporal workflows, including ones that repair the platform’s own officeholder data. We publish 46,000+ pages, and the crawler policy lets GPTBot and ClaudeBot in on purpose.
Thirteen LangGraph agents work behind a supervisor to answer questions across the whole corpus of federal and state bills, state law, and municipal code. Among them are a search planner, a query validator, a preflight validator, a prompt revisor, a summarizer, and a writer. Retrieval is pgvector, cosine similarity with MMR and metadata filtering, routed per collection. Claude runs on Bedrock with multi-model fallback and jittered backoff, and agent state checkpoints to DynamoDB so a long run survives a restart. Answers stream to the browser as structured objects. The same graph is reachable by agents directly: our MCP server runs locally over stdio and in production as an OAuth 2.1 resource server.
LLM-as-judge evaluators grade both the bill summaries and the letters we send. They run as their own CI tier, with dedicated llm and llm_full_eval pytest markers and a separate job, inside a suite of 693 test files. A summary that regresses fails the build the way a broken unit test does. It’s the same habit behind our tracing: decide what “working” means before shipping, then keep measuring it.
Tracing has been a build-time requirement since 2020. Four codebases in three languages share one header contract, so a single Honeycomb trace follows a message from the conversation engine to the API, the PDF renderer, and delivery: 24 million spans a week across 17 services. Even the 24-commit URL shortener ships with Sentry and Honeycomb. When no Koa or Flask-3 instrumentation existed, we wrote it.
Every pull request stands up a complete, DNS-resolvable AWS stack with its own API, certificate, and hosted zone, down to a Stripe webhook managed as a CloudFormation resource. It runs live integration tests, diffs against stage and prod, and deletes itself on a TTL. Fifteen reusable workflow templates and a handful of platform libraries are how four engineers keep a dozen services running.
Our traffic is consumer-shaped: it arrives when the news does. A bill moves and a Tuesday triples, with no warning and no scheduling window.
Deliveries by the state each sender wrote from. California alone accounts for one in eight.
We name our repositories after spaceflight: voyager, deepspace, discovery, highgain, xenon.
We can do something for every node on that graph. What differs is how much of it already exists.
Every letter is one identified person taking one voluntary action, written only to their own members, from a first-party, opt-in list. We deliver letters. The contact list isn’t for sale at any price.
Four engineers, nine years, two products we still run ourselves. Everything on this page is ours, so we can tell you what it costs to keep a texting program up through two presidential elections, and what it takes to put a model’s answer in front of people and stand behind it.
If you have that kind of work, tell us what you need.