Skip to content

Recent feedback

GET
/api/feedback

The most recent submissions, newest first, in the public projection — which has no field the message could occupy. Follow the Link header’s rel="next" for the page after this one. partial is true where the run that built this answer did not reach the end of the archive: the rows are then an arbitrary subset rather than the newest, and total counts that subset.

This resource answers HEAD as well, with the same headers and no body.

It also answers OPTIONS, which returns Allow, a Link to this description, and — where the resource takes one — the query formats it reads, as Accept-Query. Because Allow names QUERY and the wire will not carry one, that answer also carries method-override-required, saying which method to send it as and under which header. This document already publishes that as a required parameter, so a caller reading it here does not need the field.

Query Parameters

limit?integer

How many items this response carries. Clamped rather than refused, so any integer is accepted: a value above 25 is answered with 25, and one below zero with zero. total in the body says how many exist either way.

Default10

Response Body

application/json

application/problem+json

curl -X GET "https://example.com/api/feedback"
{  "items": [    {      "kind": "missing",      "severity": "high",      "about": "string",      "surface": "api",      "receivedAt": "string"    }  ],  "count": 0,  "total": 0,  "partial": true}
---
title: "Recent feedback"
description: "The most recent submissions, newest first, in the public projection — which has no field the message could occupy. Follow the `Link` header’s `rel=\"next\"` for the page after this one. `partial` is `true` where the run that built this answer did not reach the end of the archive: the rows are then an arbitrary subset rather than the newest, and `total` counts that subset.\n\nThis resource answers `HEAD` as well, with the same headers and no body.\n\nIt also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`. Because `Allow` names `QUERY` and the wire will not carry one, that answer also carries `method-override-required`, saying which method to send it as and under which header. This document already publishes that as a required parameter, so a caller reading it here does not need the field."
url: "https://lateano.com/developers/api/reference/feedback/feedback.get"
author: "Colin Lateano"
---

# Recent feedback

The most recent submissions, newest first, in the public projection — which has no field the message could occupy. Follow the `Link` header’s `rel="next"` for the page after this one. `partial` is `true` where the run that built this answer did not reach the end of the archive: the rows are then an arbitrary subset rather than the newest, and `total` counts that subset.

This resource answers `HEAD` as well, with the same headers and no body.

It also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`. Because `Allow` names `QUERY` and the wire will not carry one, that answer also carries `method-override-required`, saying which method to send it as and under which header. This document already publishes that as a required parameter, so a caller reading it here does not need the field.

```json
{
  "openapi": "3.1.1",
  "info": {
    "title": "lateano.com",
    "version": "1.0.0"
  },
  "paths": {
    "/api/feedback": {
      "get": {
        "operationId": "feedback.get",
        "summary": "Recent feedback",
        "tags": [
          "Feedback"
        ],
        "description": "The most recent submissions, newest first, in the public projection — which has no field the message could occupy. Follow the `Link` header’s `rel=\"next\"` for the page after this one. `partial` is `true` where the run that built this answer did not reach the end of the archive: the rows are then an arbitrary subset rather than the newest, and `total` counts that subset.\n\nThis resource answers `HEAD` as well, with the same headers and no body.\n\nIt also answers `OPTIONS`, which returns `Allow`, a `Link` to this description, and — where the resource takes one — the query formats it reads, as `Accept-Query`. Because `Allow` names `QUERY` and the wire will not carry one, that answer also carries `method-override-required`, saying which method to send it as and under which header. This document already publishes that as a required parameter, so a caller reading it here does not need the field.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 10
            },
            "description": "How many items this response carries. **Clamped rather than refused**, so any integer is accepted: a value above 25 is answered with 25, and one below zero with zero. `total` in the body says how many exist either way."
          }
        ],
        "responses": {
          "200": {
            "description": "Recent feedback",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedbackListBody"
                }
              }
            }
          },
          "default": {
            "description": "Every other status this operation answers with is a refusal, and carries `problem+json` (RFC 9457).\n\nEvery refusal this API can make, whatever the status. Match on the `type` URI’s last segment — the slug below — rather than on the whole URI, which follows whichever deployment answered.\n\n| Status | Slug | Title |\n| --- | --- | --- |\n| 400 | [`invalid-idempotency-key`](https://lateano.com/api/problems/invalid-idempotency-key) | The Idempotency-Key header carries a value this resource will not accept |\n| 400 | [`malformed-body`](https://lateano.com/api/problems/malformed-body) | The request body did not parse |\n| 400 | [`missing-idempotency-key`](https://lateano.com/api/problems/missing-idempotency-key) | This resource requires an Idempotency-Key header |\n| 400 | [`missing-media-type`](https://lateano.com/api/problems/missing-media-type) | The request carried a body with no media type |\n| 404 | [`no-such-resource`](https://lateano.com/api/problems/no-such-resource) | There is nothing at this address |\n| 405 | [`method-not-allowed`](https://lateano.com/api/problems/method-not-allowed) | This resource does not answer that method |\n| 406 | [`not-acceptable`](https://lateano.com/api/problems/not-acceptable) | This resource cannot answer in any media type the request accepts |\n| 409 | [`conflicting-idempotency-key`](https://lateano.com/api/problems/conflicting-idempotency-key) | That Idempotency-Key already names a different submission |\n| 412 | [`precondition-failed`](https://lateano.com/api/problems/precondition-failed) | The condition the request carried does not hold |\n| 415 | [`unsupported-media-type`](https://lateano.com/api/problems/unsupported-media-type) | The request body is in a media type this resource does not read |\n| 422 | [`invalid-query`](https://lateano.com/api/problems/invalid-query) | The query parsed but does not describe a question this resource can answer |\n| 422 | [`invalid-submission`](https://lateano.com/api/problems/invalid-submission) | The submission parsed but is not something this resource can record |\n| 429 | [`too-many-requests`](https://lateano.com/api/problems/too-many-requests) | The courtesy quota for this caller is spent |\n| 500 | [`unexpected-failure`](https://lateano.com/api/problems/unexpected-failure) | The resource failed while answering |",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDocument"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "FeedbackListBody": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublicFeedback"
            }
          },
          "count": {
            "type": "number",
            "description": "How many items this response carries."
          },
          "total": {
            "type": "number",
            "description": "How many items exist across every response."
          },
          "partial": {
            "type": "boolean",
            "description": "`true` where the rows are a subset rather than the newest, because the run that wrote them did not reach the end of the archive. `total` then counts the subset."
          }
        },
        "required": [
          "count",
          "items",
          "partial",
          "total"
        ],
        "additionalProperties": false,
        "description": "`PublicFeedback`, not `Feedback`: the public type has no field that prose can occupy, so a new field cannot leak a message."
      },
      "ProblemDocument": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "§ 3.1.1. Absolute, on the origin that answered, and it resolves."
          },
          "title": {
            "type": "string",
            "description": "§ 3.1.3. The catalogue row's, unchanged from occurrence to occurrence."
          },
          "status": {
            "type": "number",
            "description": "§ 3.1.2. The same number as the status line, derived rather than passed."
          },
          "instance": {
            "type": "string",
            "description": "§ 3.1.5 RECOMMENDS an absolute URI here. This field carries a full path from the root, which is the fallback form the same section permits."
          },
          "detail": {
            "type": "string",
            "description": "§ 3.1.4. This occurrence only, bounded by `DETAIL_MAX`, absent when there is nothing to add."
          }
        },
        "required": [
          "type",
          "title",
          "status",
          "instance"
        ],
        "additionalProperties": false,
        "description": "The schema generator publishes this declaration as the error schema, so a field here reaches the reference. RFC 9457 § 3.1 makes all five members optional, and this API narrows four of them to required: § 3.1.1 reads an absent `type` as `about:blank` and discards the resolvable URI. An optional member is absent, never present and `undefined`, because `JSON.stringify` drops the value and the wire cannot tell the two apart."
      },
      "PublicFeedback": {
        "type": "object",
        "properties": {
          "kind": {
            "$ref": "#/components/schemas/Kind"
          },
          "severity": {
            "$ref": "#/components/schemas/Severity"
          },
          "about": {
            "type": [
              "string",
              "null"
            ],
            "description": "A Tool name, a site path, or the caller's own text where it matched neither. `surface` alone separates the cases. This is the one public field that carries text a stranger wrote. A renderer must escape it, or must escape interpolation by default. The value also reaches agents over the Tool surface, where escaping does not apply."
          },
          "surface": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Surface"
              },
              {
                "type": "null"
              }
            ]
          },
          "receivedAt": {
            "type": "string"
          }
        },
        "required": [
          "kind",
          "severity",
          "about",
          "surface",
          "receivedAt"
        ],
        "additionalProperties": false,
        "description": "No `message`, no `requestId`: absent from the type, not filtered out of it, so no field exists that prose could occupy. The projection is written field by field, which makes a new `Feedback` field a compile error until its public fate is decided — a spread would compile, because TypeScript applies no excess-property check to one."
      },
      "Kind": {
        "type": "string",
        "enum": [
          "missing",
          "broken",
          "confusing",
          "inaccurate",
          "other"
        ]
      },
      "Severity": {
        "type": "string",
        "enum": [
          "high",
          "medium",
          "low"
        ]
      },
      "Surface": {
        "type": "string",
        "enum": [
          "api",
          "content",
          "site",
          "demo"
        ]
      }
    }
  }
}
```