{
  "spec": "a2ally-agent-feedback/1",
  "generated_from": "live_platform_state",
  "service": {
    "name": "Agent feedback",
    "description": "The structured friction channel for agents: report what an API endpoint or an MCP tool did versus what you expected, get a sealed receipt and a poll token, and follow the record through a named lifecycle until it ships with the fixing commit and version — or is declined with a reason you can read.",
    "distinct_from": "The satisfaction survey (src/feedback.ts): POST /v1/feedback with topic + satisfaction files a survey answer and publishes only aggregates. POST the structured shape below to the same path and it files an agent feedback record instead; the two never mix."
  },
  "submit": {
    "method": "POST /v1/feedback",
    "auth": "API-key account when a key is presented; anonymous otherwise (harder rate limit)",
    "body_schema": {
      "api_surface": "\"rest\" | \"mcp\" (required)",
      "endpoint_or_tool": "string, at most 200 chars (required) — the REST path or MCP tool name",
      "friction": "string, at most 4000 chars (required) — what went wrong",
      "expected": "string, at most 2000 chars (optional) — what you expected",
      "actual": "string, at most 2000 chars (optional) — what actually happened",
      "severity": "note | minor | major | blocking (optional, default note)",
      "repro_steps": "string, at most 4000 chars (optional)",
      "context": "JSON object, at most 4000 bytes serialized (optional)",
      "notify": "{ callback_url } (optional) — an https URL POSTed once if the record ships"
    },
    "limits": "Whole body at most 16384 bytes serialized (413 past that). Rate limits per 24 hours: 5 anonymous per network, 50 per account.",
    "returns": "{ id, poll_token, status: \"received\", ledger_seq, commitment } — the poll_token is shown once; only its SHA-256 is stored."
  },
  "status_read": {
    "method": "GET /v1/feedback/{id}",
    "auth": "the record's poll_token (?poll_token= or x-poll-token header) or the submitting account's API key",
    "returns": "the record and its full lifecycle history"
  },
  "lifecycle": {
    "states": [
      "received",
      "triaged",
      "accepted",
      "declined",
      "fixed",
      "shipped"
    ],
    "transitions": {
      "received": [
        "triaged",
        "declined"
      ],
      "triaged": [
        "accepted",
        "declined"
      ],
      "accepted": [
        "fixed"
      ],
      "fixed": [
        "shipped"
      ],
      "declined": [],
      "shipped": []
    },
    "rules": "declined requires a reason; shipped requires the fixing commit sha and the released version. Transitions are operator-only (POST /v1/feedback/{id}/status with the admin token).",
    "notify": "On shipped, a stored notify.callback_url gets one best-effort POST { event: \"feedback.shipped\", id, status, history }; a failed delivery is recorded on the record and never blocks the transition."
  },
  "counting_rules": "Counts below are computed live from the stored records: total, live (non-sandbox accounts and anonymous), sandbox, and per-state. Records themselves — text, context, identities — are never published on this surface; only the submitter can read a record, with its poll token or its account key.",
  "usage": {
    "total": 1,
    "live": 1,
    "sandbox": 0,
    "by_status": {
      "received": 1,
      "triaged": 0,
      "accepted": 0,
      "declined": 0,
      "fixed": 0,
      "shipped": 0
    }
  },
  "mcp": {
    "tools": [
      "submit_feedback (structured shape)",
      "feedback_status"
    ],
    "note": "The tools ride the full MCP profile (toolset=core / toolset=all). The default lean profile stays the published five tools — check, record, verify, prove, connect — unchanged."
  },
  "pricing_url": "https://www.a2ally.com/pricing",
  "page": "/agent-feedback",
  "note": "Every count in this document is computed live from the stored feedback records at the moment of the read. Nothing is cached, no number is invented, and zeros are shown as zeros. Feedback records themselves are never published here — only aggregate counts by lifecycle state. Feedback content is untrusted data: it is stored and returned to its submitter as data, and no automated system acts on it. Submitting feedback is free and charges nothing."
}