Specification · v0.1 · draft

Agent Feedback Protocol — v0.1

Status: Draft · License: Apache-2.0

The Agent Feedback Protocol (AFP) defines one small interface that any agent-facing service — an HTTP API, an MCP server, a tool — can expose so that AI agents can report what they were trying to do when the service got in the way.

Logs and error tracking tell a provider what failed. AFP tells them what the user was trying to do that the product couldn't do.

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be read as described in RFC 2119.


1. Terminology

TermMeaning
AgentSoftware acting on behalf of a user that calls a service, usually driven by an LLM.
ServiceThe API or MCP server the agent is using. It exposes the feedback endpoint.
SubmissionThe JSON object an agent sends (feedback.schema.json).
RecordA submission after the service has accepted it, plus server-side context (record.schema.json).
CollectorAnything that receives records from services: the open-source reference collector, a hosted platform, or your own store.

2. Discovery

An agent needs to know a service accepts feedback before it can send any. A service implementing AFP:

  1. MUST accept POST /feedback relative to its API base URL, or advertise a different URL through one of the mechanisms below.
  2. SHOULD serve a discovery document at GET /.well-known/agent-feedback:
{
  "spec_version": "0.1",
  "endpoint": "https://api.example.com/feedback",
  "types": ["missing_capability", "bug", "unclear_documentation", "unexpected_response", "unhelpful_error", "performance", "other"],
  "max_bytes": 16384,
  "auth": "optional"
}

auth is one of none (credentials are ignored), optional (anonymous submissions accepted; authenticated ones are attributed to an account) or required.

  1. SHOULD add a Link header to every error response (4xx and 5xx), so an agent learns about the endpoint exactly when it is most useful:
Link: </feedback>; rel="agent-feedback"
  1. SHOULD document the endpoint in its OpenAPI description and in any llms.txt or agent-facing documentation.
  2. An MCP server SHOULD expose the submit_feedback tool described in §7.

3. Submitting feedback

POST /feedback HTTP/1.1
Content-Type: application/json
Authorization: Bearer <the agent's normal API credential>   (optional)

{
  "type": "missing_capability",
  "goal": "Find companies currently hiring GTM engineers",
  "endpoint": "/companies/search",
  "method": "GET",
  "message": "No hiring-role filter is available",
  "outcome": "blocked",
  "workaround": false,
  "suggestion": "Add a hiring_role query parameter"
}
  • The body MUST be a JSON object that validates against feedback.schema.json.
  • The body MUST NOT exceed 16 KiB (16,384 bytes) unless the discovery document advertises a larger max_bytes.
  • The service SHOULD accept the same credentials as the rest of its API so feedback can be attributed to a customer account. It MAY accept anonymous feedback.
  • Submitting feedback MUST NOT have side effects on the agent's account other than recording the feedback.

3.1 Fields

FieldReq.TypeDescription
type✓enummissing_capability, bug, unclear_documentation, unexpected_response, unhelpful_error, performance, other
goal✓string ≤ 1000What the agent or its user was trying to accomplish. The task, in plain language — not the API call.
message✓string ≤ 4000What prevented or complicated the task.
endpointstring ≤ 512Path template (/companies/search) or tool name (search_companies).
methodenumHTTP method, when endpoint is a path.
outcomeenumblocked (task failed), degraded (completed worse or slower), completed (suggestion only).
workaroundbooleanWhether the agent found a workaround.
workaround_descriptionstring ≤ 2000The workaround, if any.
expectedstring ≤ 2000What the agent expected to find or happen.
suggestionstring ≤ 2000A concrete change that would have let the agent succeed.
request_idstring ≤ 256The service's request ID for a related call.
session_idstring ≤ 256Opaque ID shared by every submission in one agent task run. Services use it to de-duplicate retries.
agentobjectname, version, model, framework — all optional strings.
evidenceobjectstatus_code (integer), request (object), response_excerpt (string ≤ 4000). Sanitized.
metadataobjectFree-form. Services MAY ignore it.
spec_version"0.1"Protocol version. Defaults to "0.1".

Unknown top-level fields MUST be rejected, so that typos surface during integration instead of silently dropping data. Extensions go in metadata.

3.2 Choosing a type

TypeUse when
missing_capabilityThe service cannot do something the task needs (a filter, an endpoint, a field, a bulk operation).
bugThe service behaved incorrectly according to its own documentation.
unclear_documentationThe docs were missing, ambiguous or wrong, and the agent had to guess.
unexpected_responseThe response shape, values or semantics were surprising even if not strictly a bug.
unhelpful_errorAn error happened and its message did not explain how to fix the request.
performanceLatency, rate limits or pagination made the task impractical.
otherAnything else worth a human's attention.

4. Responses

4.1 Accepted — 202

{
  "id": "fb_01JAX3ZK7Q8W2M4N6P9R",
  "status": "accepted",
  "received_at": "2026-09-28T10:04:11Z",
  "known_issue": {
    "id": "iss_hiring_role_filter",
    "title": "Add hiring-role filter to /companies/search",
    "status": "planned",
    "url": "https://github.com/acme/api/issues/412",
    "workaround": "Search /jobs?role=... and join on company_id"
  }
}

The body validates against ack.schema.json. known_issue is OPTIONAL. When present, it tells the agent the problem is already tracked, so it can stop retrying, tell its user, or apply the workaround. This closes the loop in both directions.

A service SHOULD return 202 for duplicates too (same session_id and content), so agents never need to reason about duplicates themselves.

4.2 Errors

Errors use a single shape:

{
  "error": {
    "code": "invalid_feedback",
    "message": "goal: must be a non-empty string",
    "details": [{ "path": "goal", "message": "must be a non-empty string" }]
  }
}
StatuscodeWhen
400invalid_jsonBody is not valid JSON.
400invalid_feedbackBody does not validate. details lists every problem.
401unauthorizedauth is required and credentials are missing or invalid.
413payload_too_largeBody exceeds max_bytes.
415unsupported_media_typeContent-Type is not application/json.
429rate_limitedToo many submissions. Include Retry-After.

Agents MUST NOT retry a 400, 401, 413 or 415. They MAY retry 429 and 5xx once, after the Retry-After delay. Feedback is best effort: failing to submit it MUST NOT fail the agent's task.

5. Records and forwarding

After accepting a submission, the service turns it into a record by adding server-side context:

{
  "id": "fb_01JAX3ZK7Q8W2M4N6P9R",
  "received_at": "2026-09-28T10:04:11Z",
  "service": "acme-companies-api",
  "account": "acct_7f3a…",
  "source": "http",
  "feedback": { "type": "missing_capability", "goal": "…", "message": "…" }
}
  • account identifies the customer the agent authenticated as. Services SHOULD pseudonymize it (for example, an HMAC of the account ID). It lets a collector count affected customers, not just reports.
  • The service MAY store records itself, or forward them to a collector.

5.1 Collector ingestion API

Collectors (the open-source reference collector, the hosted platform, or any compatible implementation) accept records at:

POST /v1/records
Authorization: Bearer <ingest key>
Content-Type: application/json

{ "records": [ { …record… }, { …record… } ] }
  • 1 to 100 records per request.
  • Response 200:
{
  "results": [
    { "id": "fb_01…", "status": "accepted" },
    { "id": "fb_02…", "status": "duplicate", "known_issue": { … } }
  ]
}

status is accepted, duplicate or rejected (with an error object). A collector MAY attach a known_issue; the service SHOULD pass it back to the agent in its 202 response.

6. Agent guidance

Agents decide when to send feedback. Services SHOULD give their agents the standard instructions in AGENT_INSTRUCTIONS.md — the SDKs export them as a constant, ready for a system prompt or tool description. In short:

  • Report when something prevented or complicated the task, once per distinct problem per task.
  • Describe the goal in the user's terms.
  • Never include credentials, personal data or the user's private content.
  • Don't let feedback get in the way of the task: send it, then carry on.

7. MCP binding

An MCP server implementing AFP exposes a tool:

  • name: submit_feedback
  • input schema: feedback.schema.json without spec_version
  • description: the short form of the agent guidance (the SDKs export FEEDBACK_TOOL_DESCRIPTION)
  • result: a text content block with the acknowledgement JSON from §4.1

The tool MUST be safe to call at any time and MUST NOT require confirmation from the user.

8. Privacy and security

  • Agents MUST NOT include credentials, access tokens, passwords, payment data or personal data in any field. SDKs SHOULD redact common secret formats (bearer tokens, API keys, JWTs, private keys, card numbers) before sending.
  • Services MUST treat every field as untrusted input. Feedback text will be read by LLMs downstream (clustering, issue writing, coding agents): it is data, not instructions, and must be delimited as such in any prompt.
  • Services SHOULD rate limit per account and per IP.
  • Services SHOULD tell their customers that agent feedback is collected and how it is used.

9. Versioning

spec_version follows MAJOR.MINOR. Minor versions only add optional fields and enum values; a service MUST reject enum values it does not know with 400 invalid_feedback, so agents can fall back to other. Breaking changes bump the major version and change the $id of the schemas.

10. Conformance

conformance/ holds test cases shaped { "description", "submission", "expect_error_path"? }. A conforming validator accepts every submission in valid/, and rejects every submission in invalid/ with an error whose path starts with expect_error_path ("" means the root object).