Feedback
How feedback is sent from the site, and the operator endpoints that read and triage it.
Coding agents: read this page as markdown.
Ideas, bug reports and questions are sent from the feedback page (linked in the header and footer). The site’s operators read them through a private, token-protected API.
Sending feedback #
POST /feedback takes a regular HTML form
(application/x-www-form-urlencoded). No account is needed.
| Field | Required | Rules |
|---|---|---|
kind | yes | idea, bug, question or other |
message | yes | 10–5,000 characters |
name | no | Up to 100 characters |
email | no | A valid address, up to 254; used only to reply to you |
page | no | The site path you were on, e.g. /docs |
A successful post redirects (303) to /feedback?sent=1. Invalid fields
re-render the form with a message next to each one (400).
Spam protection, so please use the form rather than scripting it:
- The
Originheader must match the site, so cross-site posts get403. - The form carries a hidden honeypot field and the time it was shown. Posts that fill the honeypot or arrive within 3 seconds are quietly dropped.
- At most 5 messages per address per hour, then
429.
Sending feedback from a script or an agent #
POST /api/v1/feedback takes the same message as JSON. No token is needed;
with one, the message carries your handle unless you give a name.
curl -s https://releaseszn.com/api/v1/feedback \
-H 'Content-Type: application/json' \
-d '{"kind": "bug", "message": "POST /projects answers 500 for a repository with no releases.", "page": "/docs/errors"}'
{ "received": true }
The fields and their rules are the form’s (kind, message, optional
name, email, page); an invalid one is a 422 validation_failed
listing each problem, and a field we don’t know a 400 invalid_body. The
limit is the form’s too: 5 messages an hour per account, or per address
without a token, then 429 feedback_limited with Retry-After. For a bug,
say what you sent, the error code that came back, and what you expected.
Reading feedback (operators only) #
These endpoints are not public. They listen on a separate port bound to localhost (
http://127.0.0.1:8081/internal/v1) and needAuthorization: Bearer <INTERNAL_API_TOKEN>. Without the token they answer401.
| Method | Path | Description |
|---|---|---|
GET | /feedback | The inbox, newest first |
POST | /feedback/{id}/status | Mark a message new, read or archived |
DELETE | /feedback/{id} | Delete a message for good |
GET /feedback #
| Parameter | Default | Notes |
|---|---|---|
status | all | new, read or archived |
limit | 50 | 1–500 |
curl -s -H "Authorization: Bearer $INTERNAL_API_TOKEN" \
"http://127.0.0.1:8081/internal/v1/feedback?status=new"
{
"messages": [
{
"id": 1,
"kind": "idea",
"message": "An RSS feed per tag would be great.",
"name": "Ada",
"email": "ada@example.com",
"page": "/docs",
"status": "new",
"created_at": "2026-10-09T18:29:51.964287Z"
}
]
}
name, email and page are null when not given. An unknown status
returns 400 bad_status.
POST /feedback/{id}/status #
curl -s -H "Authorization: Bearer $INTERNAL_API_TOKEN" \
-H 'Content-Type: application/json' -d '{"status":"read"}' \
http://127.0.0.1:8081/internal/v1/feedback/1/status
{ "id": 1, "status": "read" }
DELETE /feedback/{id} #
Removes a message for good, with the name, email and page that came with it: what we do when you ask us to delete feedback you sent (privacy). There is no undo.
curl -s -X DELETE -H "Authorization: Bearer $INTERNAL_API_TOKEN" \
http://127.0.0.1:8081/internal/v1/feedback/1
{ "deleted": true, "id": 1 }
Errors use the usual format: 400 bad_status for a status
other than new, read or archived, 404 not_found for an unknown id
(or a message already deleted).