How to close the feedback loop with customers
Short answer. Closing the loop means telling the specific people who asked for something what happened to it, whether it shipped or won't. It takes three pieces of bookkeeping: record who asked when they ask, link the request to the work item, and start the follow-up from the merge or release instead of someone's memory. If your requests live on a voting board, the board covers the last step: Canny and Featurebase email voters when a post's status changes. If requests arrive in Slack, Discord, support tickets, and calls, Modem (our product) keeps everyone who asked linked to one topic. Its opt-in Close the loop automation posts a note in your team's Slack when a linked GitHub PR merges, naming the customers to follow up with. Its agent drafts replies when asked, and in Slack and, by default, in the dashboard a customer reply waits for your approval before it goes out, and only through Slack, Discord, Intercom, Pylon, or Plain.
Somewhere in your product is a feature that shipped last quarter, which a customer asked for last year, and that customer doesn't know it exists. That's the default outcome, not a lapse. By the time work merges, the request is nine months old, the PM who heard it is on another project, and "who asked for this?" means an afternoon of searching Slack, support tickets, and the tracker. The fix ships as a changelog line or a tweet, and never as a message to the people who actually raised it.
Closing the loop is not a sentiment program. It's bookkeeping, and each step fails for a different reason. (For a comparison of tools, see our tools roundup; this guide is the process.)
Step 1: record who asked, at the moment they ask
You can't close a loop whose start was never written down. The requester has to be captured with the request: not "several customers want SSO" but "SSO: asked by [requester] at [account] (Zendesk #[id]), in #customers-[account] on [date], and on two recorded calls."
The failure mode is friction. If recording a request means opening another tool and filling a form, it happens for a week and stops. Two low-friction versions:
- A tracker convention. Every customer-driven issue gets a "requested by" section, and adding a requester to an existing issue is a one-line edit. Support and sales never summarize a request without naming who made it.
- A reaction hook. A 📌 reaction in Slack pipes the message, requester attached, into your intake through a simple workflow.
Duplicates belong here too. The fifth person to ask for an audit log is one more name on the notify list, not a fifth ticket. See how to handle duplicate feature requests.
Step 2: link the request to the work item
The request and the engineering work live in different systems: the request in Zendesk or Slack, the work in Linear, Jira, or GitHub. Closing the loop needs a durable link between them, made when the work is created, because rebuilding it later is archaeology nobody does.
The convention: when a request becomes a ticket, the ticket carries the request and its requesters. When new requests arrive for existing work, they're appended, not filed again. If requests arrive faster than a person can file and link them, automate this step first; see turning feedback into tracked issues.
Step 3: trigger the follow-up from the work, not from memory
This is where almost every loop dies. The feature merges, the release goes out, and the follow-up depends on a PM remembering a nine-month-old promise. A "notify requesters" checklist item on the Done column is better than nothing, and it rarely survives the first busy sprint. The fix is structural: make the event that finishes the work start the follow-up.
Two details matter:
- Merged is not shipped. A merged PR may sit behind a deploy, a feature flag, or a staged rollout. Telling a customer "it's live" when it isn't is worse than silence. Fire on the merge, then wait for your release before anyone writes to the customer.
- The trigger has to know who asked. A merge event alone tells you nothing about requesters. It only works if steps 1 and 2 left a link from the work back to the people.
How tools handle this step:
| Tool | What fires the follow-up | Who gets told |
|---|---|---|
| Canny | A post's status changes | Voters, by email (on by default) |
| Featurebase | A post's status changes, including from a linked GitHub issue | Upvoters, by email |
| Productboard | You post a Portal card update | People who voted on the card or have insights linked to the feature (Plus plan and up) |
| Savio | You email requesters when a feature ships | Many requesters at once, with a record of who was notified |
| Lane | A status change you configure, such as Shipped | Customers, in the Slack thread, Intercom conversation, or email the feedback came from |
| Modem | A linked GitHub PR merges (opt-in automation) | Your team, in Slack, with the customers to follow up with |
Voting boards are the simplest answer when customers actually use the board: the requesters are the voters, so the status change reaches them. The gap is everyone who asked somewhere else.
Where Modem fits. We build Modem for teams whose requests arrive in conversations rather than on a board. It ties each captured message to a person and company and groups messages about the same request into a topic, across Slack, Discord, support tools, calls, and GitHub. People are matched across platforms by email; Discord members have no email, so they can be merged by hand. "Who wanted this" stays a lookup, even nine months later.
The follow-up is opt-in. Install the Close the loop when fixes and features ship template from Automations > Templates. When a GitHub PR merges, it finds the topics that PR is directly linked to, looks up the customers on them, and posts one note in your team's Slack naming each customer, the linked topics, and the PR, with a suggestion to reach out. If no customer is linked, it posts nothing. It favors precision: a missed link costs less than telling a customer their fix shipped when it didn't. Because merged isn't released, you can add a delay of up to 7 days before it runs.
From there, a person asks Modem's agent to draft the replies, using what each customer originally said. In Slack and, by default, in the dashboard, a customer reply only goes out after you approve it, and only through a channel Modem can reply in: Slack, Discord, Intercom and Pylon (once an admin enables replies), and Plain. For anywhere else, such as a Zendesk ticket or an email to the customer, a person sends the message. We built it this way because, in our own words, "we'd ship a fix and forget to tell the person who reported it" (Automation Templates). Modem's job is to remember who asked; a message in your company's name to a customer gets a human look first.
Step 4: write it like a reply, not an announcement
Generic release notes don't close loops. The message that works is specific and personal. A template:
Hi [requester], back in [month] you asked for [feature] for [account]'s [reason]. It's live as of today; docs are here: [link]. Does this unblock [their goal]?
Three properties matter. It names what they asked for. It arrives where they asked, in their Slack channel, support thread, or Discord thread, not a generic email. And it ends with a question, because a loop-closing message is also the best user-research prompt you'll send. Send the changelog too, but the changelog is a broadcast and this is a reply. (Modem's separate weekly changelog template drafts that broadcast for approval and deliberately leaves customer names out.)
Step 5: close negative loops too
"We're not building this, and here's why" is also a closed loop. It's uncomfortable, which is why silence is the default, but a clear no keeps the customer willing to tell you things. An ignored request teaches them to stop. One or two honest sentences are enough: "We looked at the Gantt view request seriously. It's not on the roadmap this year because we're betting on the timeline redesign instead."
The same trigger works here. When an issue is closed as not planned, or a topic is dismissed, the requesters are owed a message as much as when it ships.
Step 6: check that the trigger still fires
Automated loops fail quietly. An integration loses its token, a usage limit is hit, a status gets renamed, or a repo moves, and the automation stops firing. Nobody notices, because the symptom is silence, and silence is what a broken loop looked like before you automated it. A team can go months without noticing, until a customer mentions they never heard back.
Two checks catch it:
- A monthly audit. Pick the customer-requested items that merged or closed last month and confirm each requester got a message. If any didn't, find out which link broke.
- An alert on quiet. If your follow-up automation hasn't run in two release cycles, treat that as an incident, not a slow month. Give the automation an owner who gets that alert.
Run one loop backwards on last month's release
Don't build the whole pipeline first. Do one loop, backwards: pick one feature you shipped in the last month, spend 20 minutes finding everyone who asked for it (search Slack, support, and your tracker), and send each of them the reply from step 4. The answers you get back are the evidence that makes the team want the systematic version.
