Observability tells you what failed. Backloop tells you what the agent was trying to do.
An open protocol that lets agents report what blocked them. A platform that clusters the reports, ranks them by impact and hands the fix to a coding agent. You approve the PR.
Agents call your API for your customers. When they hit a gap, they improvise or give up. The reason never reaches your team.
What your logs capture
10:04:11Z200 GET /companies/search?hiring_role=gtm_engineer
total=20 unknown parameter ignored10:04:12Z200 GET /companies/cmp_001/jobs
10:04:12Z200 GET /companies/cmp_002/jobs
10:04:12Z200 GET /companies/cmp_003/jobs
… 496 more, all 200
→
What the agent reports
{
"type": "missing_capability",
"goal": "Find companies currently hiring GTM engineers",
"endpoint": "/companies/search",
"message": "No hiring-role filter is available",
"workaround": true,
"workaround_description": "500 calls to /companies/{id}/jobs"
}
One endpoint
POST /feedback. That's the protocol.
A JSON schema, a discovery document and a header on errors. Implement it in an afternoon. No vendor required.
GET /.well-known/agent-feedbackAgents discover the endpoint.
Link: </feedback>; rel="agent-feedback"Every error response points to it.
known_issueThe 202 tells agents a problem is already tracked, with a workaround.
conformance/Shared test cases. Both SDKs pass them.
POST /feedback HTTP/1.1
Content-Type: application/json
Authorization: Bearer <the agent's API key>
{
"type": "unhelpful_error",
"goal": "Find logistics companies in the UK",
"message": "country=United Kingdom returns 400 invalid request",
"suggestion": "Name the parameter and the expected format",
"outcome": "degraded"
}
HTTP/1.1 202 Accepted
{ "id": "fb_01JAX3ZK…", "status": "accepted" }
// Your API: accept feedback, forward it to a collector.import { createFeedbackHandler, forwardTo, hashAccount } from"@backloop/sdk";
const feedback = createFeedbackHandler({
service: "acme-companies-api",
identify: (req) => hashAccount(apiKeyOf(req), SECRET),
onRecord: forwardTo({ url: BACKLOOP_URL, ingestKey }),
});
app.post("/feedback", (c) => feedback.submit(c.req.raw));
// Your agent: report, never fail the task over it.awaitnew FeedbackClient({ baseUrl }).trySubmit({ type, goal, message });
from backloop import FeedbackClient, feedback_tool
client = FeedbackClient(base_url="https://api.acme.com", api_key=key)
client.try_submit({
"type": "missing_capability",
"goal": "Find companies currently hiring GTM engineers",
"message": "No hiring-role filter on /companies/search",
})
# Give your LLM agent the tool, in Claude's tool format.
tools = [search_companies, feedback_tool()]
// Any MCP server: one line adds submit_feedback.import { registerFeedbackTool } from"@backloop/mcp";
registerFeedbackTool(server, {
service: "acme-mcp",
onRecord: forwardTo({ url: BACKLOOP_URL, ingestKey }),
});
// Any agent: report on any API that speaks the protocol.$ claude mcp add feedback -- npx @backloop/mcp
The loop
From complaint to pull request.
Six steps between an agent hitting a wall and your team merging the fix. Five run on their own. One is yours.
01
CollectStructured reports from every agent, de-duplicated per run.
02
ClusterDifferent words, same problem. Grouped by intent.
03
PrioritizeRanked by failed workflows, affected accounts and workarounds.
04
InvestigateAn issue written from the evidence, with your API docs as context.
05
BuildA coding agent writes the change and runs your tests in an isolated worktree.
06
ApproveA human reviews. Only then does a draft PR open.
APPROVEHuman
BUILDCoding agent
INVESTIGATEIssue
PRIORITIZEImpact
CLUSTERIntent
COLLECTPOST /feedback
Signal, not noise
Twenty-four agents. One missing filter.
From the demo dataset: 67 simulated reports from 17 accounts, clustered into 8 problems
Priority78of 100
Add a hiring_role filter to /companies/search
24reports
13accounts
71%blocked
29%workaround
What agents were trying to do
Priority weighs frequency, failed workflows and affected accounts, discounted by workarounds and age. Every component is shown.
Humans approve
The agent proposes. You decide.
Changes run in a git worktree, never your main checkout.
The coding agent gets no web tools and can't push.
Feedback is data, never instructions, in every prompt.
Only text a human wrote goes back to agents.
Approving opens a draft PR. Merging stays with your team.
claude-code · backloop/hiring-role-filter
Proposed change
Tests pass
src/companies.js@@ -32,6 +32,9 @@ export function searchCompanies(params) if (params.country) results = results.filter(…);+ if (params.hiring_role) {+ results = results.filter((c) =>+ c.jobs.some((job) => job.role === params.hiring_role));+ }test/companies.test.js+test("search filters by hiring role", () => { … });
Built in the open
Open protocol. Hosted loop.
The spec and schemas are public. Any API can implement them without us. The SDKs, MCP tool and a self-hostable collector are Apache-2.0. The platform adds the part that turns reports into shipped fixes.
Because you ask it to. The protocol ships standard agent instructions and a tool definition. Agents using your API through MCP get submit_feedback like any other tool, and every error response links to the endpoint.
Isn't this just error tracking?
No. Error tracking sees the failed call. Feedback carries the goal, what was missing and what the agent did instead. Many of the useful reports come from calls that returned 200.
What stops junk and prompt injection?
Schema validation, per-account rate limits and de-duplication per agent run. Feedback is treated as data in every prompt. Only human-written text is sent back to agents. The coding agent gets no web tools and can't push, and nothing ships without approval.
Do I need the platform?
No. The protocol is open. Implement POST /feedback, store the records, read them yourself. The platform is for teams that want clustering, prioritization and the path to a PR handled.
Which coding agents work with it?
Claude Code today, run headless in an isolated git worktree. The agent interface is small, so others can plug in.
Start listening
Every agent is a user interview. Start taking notes.
GET /.well-known/agent-feedback # advertise the endpointPOST /feedback # accept reports, return 202Link: </feedback>; rel="agent-feedback" # on every error