Skip to navigation

Customer feedback

Collecting tickets, interviews and sales notes into one page per customer problem.

Feedback about one problem can sit in a support tool, a folder of interview recordings and a CRM, in different words each time. When you capture all of it into one brain, each piece compiles onto the page for its problem, so a product manager can read one page and follow a link to every source.

How different sources reach one page

CapturesPageConnected pageSupport ticketInterview transcriptSales noteCustomer problem3 sourcesRelated pageSupport ticketInterview transcriptSales noteCustomer problem3 sourcesRelated page

A ticket, an interview and a sales note can describe the same problem without sharing any words. Routing compares each extracted fact with existing pages by meaning, so all three can land on the same page, and each one updates the page and adds a source.

The line to the related page comes from a separate check on new pages. A model judges whether two pages are worth reading together and stores its reason, so a connection can show something none of the captures say.

Try it from an AI tool

You can test this before writing any code. Connect a brain to your AI tool, paste in a few pieces of feedback and ask for them back by problem.

Hermes Agent capturing a note about a customer into a Research brain with the Living Brain CLI

A capture is one command, and the response comes back as pending while the brain compiles it. This example session comes from livingbrain.com.

Once it has compiled, try prompts such as:

What do customers struggle with?
Show me the sources behind that page.

How the parts fit

SourcesYour appBrainProduct teamwebhookcontestedTicketsInterviewsSales notesRedact and labelCaptureUpdate trackerCompilePagesDaily briefResolve conflictcontestedTicketsInterviewsSales notesRedact and labelCaptureCompile into pagesUpdatetrackerDaily briefResolveconflict
SourcesYour appThe brainProduct team
  1. Sources. Your app collects feedback from the tools where it already lives, so customers and support agents keep working as they do now.
  2. Redact and label. Your app removes personal details and sets a label and an originRef.
  3. Capture. The call returns straight away with an ingest source. Save its id against the customer in your own database.
  4. Compile. A background worker extracts facts, routes them to pages and records a new version of each page it changes.
  5. Pages to people. A webhook updates your tracker, a daily brief goes to the team, and contested pages wait for someone to decide.
const source = await client.captures.capture({
brainId,
body: {
kind: "note",
content: redact(ticket.body),
label: `Ticket ${ticket.id}`,
originRef: `tickets:${ticket.id}`,
},
});
// Your own table. The brain doesn't need to know who the customer is.
await db.feedback.insert({
sourceId: source.id,
customerId: ticket.customerId,
plan: ticket.plan,
});

A capture returns straight away, before any compiling happens:

Example response
{
"id": "6a2f1c9e-4d1b-4a57-9c0e-2f7b8e1d3a44",
"kind": "note",
"label": "Ticket 1042",
"originRef": "tickets:1042",
"status": "pending",
"affectedPageIds": []
}

Once compiling finishes, the status becomes completed and affectedPageIds lists the pages it changed.

Group by problem

Customers often describe the solution they have in mind, and a product team usually learns more from the problem behind it. Use the profile to tell the brain to organize feedback by problem:

await client.workspace.updateProfile({
brainId,
focus: "What customers struggle with in our product, and when it happens",
vaultOrganization: "One page per customer problem, named after the problem",
directives: {
flagContradictions: true,
custom: "Keep the customer's own words as quotes. Record requested features under the problem they would solve.",
},
});

Profile changes only apply to later captures, so set this before the first import.

Count customers yourself

Source counts don’t measure demand, because ten tickets from one customer and one ticket each from ten customers both give ten sources. To count customers, join a page’s sources to the table you saved at capture time:

const page = await client.workspace.getPage({ brainId, slug: problemSlug });
const sourceIds = page.sources.map((s) => s.ingestSourceId);
const rows = await db.feedback.whereSourceIn(sourceIds);
const customers = new Set(rows.map((r) => r.customerId)).size;
const enterprise = new Set(rows.filter((r) => r.plan === "enterprise").map((r) => r.customerId)).size;
Example output
customers: 7
enterprise: 3

Keep plan, revenue and region in that table too. You can join them to any page’s sources without sending them to the brain.

Keep other systems in step

To link pages to issues in your tracker, subscribe to brain.page.updated and key the link on the page ID, because a renamed page gets a new slug and keeps its ID. Read the change feed as well. After a merged event several pages have become one, and after a split event one page has become several, so your links need updating.

For a weekly review, read everything that changed since the last one:

for await (const ev of await client.workspace.changes({ brainId, since: lastReview })) {
if (ev.action === "created" || ev.action === "updated") review.add(ev.pageId);
}

To send pages to the team on a schedule, turn on daily briefs and bind a Slack channel with notifyBriefs on.

What a contested page can mean

When new feedback contradicts a page, the brain marks it contested and keeps both claims until someone resolves it.

The conflictWhat might be behind itWhat to do
A release note says it’s fixed, and a later ticket says it isn’tThe fix was partial, or only reached some plansReopen the issue and resolve the page with what’s now true
One customer wants it simpler, another wants more optionsTwo segments want different thingsCheck your table for the plan behind each claim and record both
An old interview disagrees with recent ticketsThe product changed in betweenResolve in favor of the recent claim

Personal data

Redact before you capture. Text from a capture can end up in pages and their earlier versions, and removing it from there is much harder.

If a customer asks you to delete their data, start with your table of source IDs, which shows the sources that belong to them. Deleting an ingest source leaves the pages derived from it in place, so use each source’s affectedPageIds to find those pages and edit them. An edit keeps the previous text in the page’s history, and only permanent deletion removes the versions too.

Limits to plan for

LimitWhat it means hereWhat to do
50,000 characters per inline captureA long interview transcript can exceed thisSplit it and give each part its own originRef
Compiling is asynchronousA ticket isn’t searchable as soon as it’s capturedReact to brain.page.updated
Batches process items independentlyA failed item doesn’t stop the others, so a batch can partly succeedList sources with status failed and retry them
Profile changes aren’t retroactiveAn import before the profile is set follows the old guidanceSet the profile first

When to use something else

Use a brain for written feedback that people need to read. The rate of an error or the share of users on a feature belongs in your analytics. A survey with fixed answers fits a spreadsheet, though its free-text comments are worth capturing.

How to build with Living Brain covers the write and read paths in more detail.