{
  "openapi": "3.1.0",
  "info": {
    "title": "Feedback by OpenApps API",
    "version": "1.0.0",
    "summary": "Free feedback boards with voting, a public roadmap and a changelog.",
    "description": "A free alternative to Canny. No account and no key: a board's id is its secret, like its link. POST /boards returns an ownerKey; send it in the x-owner-key header to change statuses, hide ideas, reply as the team and write the changelog. Votes come from people in the browser, one per person per idea; the API does not vote. Errors are JSON with an `error` sentence that says what to change. Guide for assistants: https://feedback-free.openapps.xyz/llms.txt",
    "contact": {
      "name": "OpenApps",
      "url": "https://free-apps.openapps.xyz/"
    }
  },
  "externalDocs": {
    "description": "llms.txt: the full guide for AI assistants",
    "url": "https://feedback-free.openapps.xyz/llms.txt"
  },
  "servers": [
    {
      "url": "https://functions.bounded.sh/apps/54b9c9d7fdc2b68029ce2e57/api"
    }
  ],
  "paths": {
    "/boards": {
      "post": {
        "operationId": "createBoard",
        "summary": "Create a feedback board",
        "description": "Makes a board and returns its link, a private admin link and the owner key. Share url; keep adminUrl and ownerKey private.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "intro": {
                    "type": "string",
                    "maxLength": 1000
                  },
                  "tags": {
                    "type": "array",
                    "maxItems": 20,
                    "items": {
                      "type": "string",
                      "maxLength": 24
                    },
                    "description": "Tag names. Defaults to [\"Idea\", \"Bug\"]."
                  }
                }
              },
              "example": {
                "name": "Acme feedback",
                "intro": "Tell us what would make Acme better.",
                "tags": [
                  "Idea",
                  "Bug"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedBoard"
                },
                "example": {
                  "boardId": "Hn4kWq8ZtR2mXc7pLs9a",
                  "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a",
                  "adminUrl": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/admin#k=OWNER_KEY",
                  "ownerKey": "OWNER_KEY",
                  "board": {
                    "id": "Hn4kWq8ZtR2mXc7pLs9a",
                    "name": "Acme feedback",
                    "intro": null,
                    "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a",
                    "roadmapUrl": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/roadmap",
                    "changelogUrl": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/changelog",
                    "tags": [
                      "Idea",
                      "Bug"
                    ],
                    "posts": 0,
                    "byStatus": {
                      "new": 0,
                      "review": 0,
                      "planned": 0,
                      "progress": 0,
                      "done": 0,
                      "closed": 0
                    }
                  },
                  "note": "Share url with the people you want ideas from. Keep adminUrl and ownerKey private: they let anyone set statuses, hide posts and write the changelog."
                }
              }
            }
          },
          "400": {
            "description": "Missing or too long fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/boards/{boardId}": {
      "get": {
        "operationId": "getBoard",
        "summary": "Read a board and its top requests",
        "description": "The board, a count of ideas per status, and the ideas that match the filters. Pinned ideas come first.",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The board's id: the 20 characters after /b/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8,32}$"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "top (most votes), trending (votes and comments weighed by age) or new.",
            "schema": {
              "type": "string",
              "enum": [
                "top",
                "trending",
                "new"
              ],
              "default": "top"
            }
          },
          {
            "name": "status",
            "in": "query",
            "description": "active (new, review, planned, progress), all, or one status.",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "all",
                "new",
                "review",
                "planned",
                "progress",
                "done",
                "closed"
              ],
              "default": "active"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "description": "A tag name on the board, like Bug.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "description": "Words that must all appear in the title, details or tags.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 50
            }
          },
          {
            "name": "x-owner-key",
            "in": "header",
            "required": false,
            "description": "Optional. With the board's owner key, hidden ideas are included.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The board and its matching ideas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "board": {
                      "$ref": "#/components/schemas/Board"
                    },
                    "filter": {
                      "type": "object"
                    },
                    "matching": {
                      "type": "integer"
                    },
                    "posts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Post"
                      }
                    }
                  }
                },
                "example": {
                  "board": {
                    "id": "Hn4kWq8ZtR2mXc7pLs9a",
                    "name": "Acme feedback",
                    "intro": null,
                    "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a",
                    "roadmapUrl": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/roadmap",
                    "changelogUrl": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/changelog",
                    "tags": [
                      "Idea",
                      "Bug"
                    ],
                    "posts": 12,
                    "byStatus": {
                      "new": 7,
                      "review": 2,
                      "planned": 2,
                      "progress": 1,
                      "done": 0,
                      "closed": 0
                    }
                  },
                  "filter": {
                    "sort": "top",
                    "status": "active"
                  },
                  "matching": 12,
                  "posts": [
                    {
                      "id": "Qm3TfVb8KpXa",
                      "title": "Dark mode",
                      "details": "Easier on the eyes at night.",
                      "status": "planned",
                      "statusLabel": "Planned",
                      "votes": 42,
                      "comments": 5,
                      "tags": [
                        "Idea"
                      ],
                      "pinned": false,
                      "author": "Sam",
                      "createdAt": "2026-10-19T12:00:00.000Z",
                      "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/p/Qm3TfVb8KpXa"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "A filter value is not one of the allowed ones.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The owner key does not match this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No board with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/boards/{boardId}/roadmap": {
      "get": {
        "operationId": "getRoadmap",
        "summary": "Read the public roadmap",
        "description": "Ideas grouped by status: review, planned and progress (most voted first) and done (most recently finished first).",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The board's id: the 20 characters after /b/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8,32}$"
            }
          },
          {
            "name": "x-owner-key",
            "in": "header",
            "required": false,
            "description": "Optional. With the board's owner key, hidden ideas are included.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The roadmap columns.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "board": {
                      "$ref": "#/components/schemas/Board"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "columns": {
                      "type": "object",
                      "properties": {
                        "review": {
                          "type": "object",
                          "properties": {
                            "label": {
                              "type": "string"
                            },
                            "posts": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Post"
                              }
                            }
                          }
                        },
                        "planned": {
                          "type": "object",
                          "properties": {
                            "label": {
                              "type": "string"
                            },
                            "posts": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Post"
                              }
                            }
                          }
                        },
                        "progress": {
                          "type": "object",
                          "properties": {
                            "label": {
                              "type": "string"
                            },
                            "posts": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Post"
                              }
                            }
                          }
                        },
                        "done": {
                          "type": "object",
                          "properties": {
                            "label": {
                              "type": "string"
                            },
                            "posts": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/Post"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "board": {
                    "id": "Hn4kWq8ZtR2mXc7pLs9a",
                    "name": "Acme feedback",
                    "intro": null,
                    "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a",
                    "roadmapUrl": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/roadmap",
                    "changelogUrl": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/changelog",
                    "tags": [
                      "Idea",
                      "Bug"
                    ],
                    "posts": 12,
                    "byStatus": {
                      "new": 7,
                      "review": 2,
                      "planned": 2,
                      "progress": 1,
                      "done": 0,
                      "closed": 0
                    }
                  },
                  "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/roadmap",
                  "columns": {
                    "review": {
                      "label": "Under review",
                      "posts": []
                    },
                    "planned": {
                      "label": "Planned",
                      "posts": [
                        {
                          "id": "Qm3TfVb8KpXa",
                          "title": "Dark mode",
                          "details": "Easier on the eyes at night.",
                          "status": "planned",
                          "statusLabel": "Planned",
                          "votes": 42,
                          "comments": 5,
                          "tags": [
                            "Idea"
                          ],
                          "pinned": false,
                          "author": "Sam",
                          "createdAt": "2026-10-19T12:00:00.000Z",
                          "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/p/Qm3TfVb8KpXa"
                        }
                      ]
                    },
                    "progress": {
                      "label": "In progress",
                      "posts": []
                    },
                    "done": {
                      "label": "Done",
                      "posts": []
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The owner key does not match this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No board with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/boards/{boardId}/posts": {
      "post": {
        "operationId": "createPost",
        "summary": "Post an idea or bug report",
        "description": "Adds an idea to the board. It starts with one vote, from the API. Also takes an HTML form post (urlencoded or multipart, fields title, details, tag, name) and then answers with a thank-you page; a filled _gotcha field is quietly dropped.",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The board's id: the 20 characters after /b/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8,32}$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewPost"
              },
              "example": {
                "title": "Export reports to CSV",
                "details": "Our finance team needs the monthly totals in a spreadsheet.",
                "tags": [
                  "Idea"
                ],
                "name": "Sam"
              }
            },
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "details": {
                    "type": "string"
                  },
                  "tag": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "_gotcha": {
                    "type": "string"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "details": {
                    "type": "string"
                  },
                  "tag": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "_gotcha": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new idea.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                },
                "example": {
                  "id": "Qm3TfVb8KpXa",
                  "title": "Export reports to CSV",
                  "details": "Easier on the eyes at night.",
                  "status": "new",
                  "statusLabel": "New",
                  "votes": 1,
                  "comments": 0,
                  "tags": [
                    "Idea"
                  ],
                  "pinned": false,
                  "author": "Sam",
                  "createdAt": "2026-10-19T12:00:00.000Z",
                  "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/p/Qm3TfVb8KpXa",
                  "note": "Posted. It starts with one vote, from the API. People vote on it at url."
                }
              },
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "For an HTML form post (unless the request accepts application/json): a short thank-you page linking to the idea and back to the referring site."
                }
              }
            }
          },
          "400": {
            "description": "Missing title, too long fields, or a tag the board does not have.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No board with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/boards/{boardId}/posts/{postId}": {
      "get": {
        "operationId": "getPost",
        "summary": "Read one idea with its comments",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The board's id: the 20 characters after /b/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8,32}$"
            }
          },
          {
            "name": "postId",
            "in": "path",
            "required": true,
            "description": "The idea's id: the characters after /p/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{6,32}$"
            }
          },
          {
            "name": "x-owner-key",
            "in": "header",
            "required": false,
            "description": "Optional. With the board's owner key, hidden ideas are included.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The idea, its comments and the changelog entries that shipped it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Post"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "commentList": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Comment"
                          }
                        },
                        "shippedIn": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "title": {
                                "type": "string"
                              },
                              "publishedAt": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "url": {
                                "type": "string",
                                "format": "uri"
                              }
                            }
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "id": "Qm3TfVb8KpXa",
                  "title": "Dark mode",
                  "details": "Easier on the eyes at night.",
                  "status": "planned",
                  "statusLabel": "Planned",
                  "votes": 42,
                  "comments": 5,
                  "tags": [
                    "Idea"
                  ],
                  "pinned": false,
                  "author": "Sam",
                  "createdAt": "2026-10-19T12:00:00.000Z",
                  "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/p/Qm3TfVb8KpXa",
                  "commentList": [
                    {
                      "id": "Zt7RkPq2WmXa",
                      "body": "We need this for our audit too.",
                      "author": "Ana",
                      "team": false,
                      "at": "2026-10-19T13:00:00.000Z"
                    }
                  ],
                  "shippedIn": []
                }
              }
            }
          },
          "403": {
            "description": "The owner key does not match this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No idea with that id on this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updatePost",
        "summary": "Change an idea's status, tags, pin or visibility (owner)",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The board's id: the 20 characters after /b/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8,32}$"
            }
          },
          {
            "name": "postId",
            "in": "path",
            "required": true,
            "description": "The idea's id: the characters after /p/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{6,32}$"
            }
          },
          {
            "name": "x-owner-key",
            "in": "header",
            "required": true,
            "description": "The board's owner key, from POST /boards or the admin link.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "description": "new, review, planned, progress, done or closed. Also accepts \"under review\", \"in progress\", \"complete\" and \"shipped\"."
                  },
                  "title": {
                    "type": "string",
                    "maxLength": 140
                  },
                  "details": {
                    "type": "string",
                    "maxLength": 5000
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "pinned": {
                    "type": "boolean"
                  },
                  "hidden": {
                    "type": "boolean"
                  }
                }
              },
              "example": {
                "status": "in progress"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated idea.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Post"
                },
                "example": {
                  "id": "Qm3TfVb8KpXa",
                  "title": "Dark mode",
                  "details": "Easier on the eyes at night.",
                  "status": "progress",
                  "statusLabel": "In progress",
                  "votes": 42,
                  "comments": 5,
                  "tags": [
                    "Idea"
                  ],
                  "pinned": false,
                  "author": "Sam",
                  "createdAt": "2026-10-19T12:00:00.000Z",
                  "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/p/Qm3TfVb8KpXa"
                }
              }
            }
          },
          "400": {
            "description": "A value is not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No owner key was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The owner key does not match this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No idea with that id on this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/boards/{boardId}/posts/{postId}/comments": {
      "post": {
        "operationId": "createComment",
        "summary": "Comment on an idea",
        "description": "With the owner key, the comment is marked as a reply from the team.",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The board's id: the 20 characters after /b/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8,32}$"
            }
          },
          {
            "name": "postId",
            "in": "path",
            "required": true,
            "description": "The idea's id: the characters after /p/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{6,32}$"
            }
          },
          {
            "name": "x-owner-key",
            "in": "header",
            "required": false,
            "description": "Optional. With the board's owner key, hidden ideas are included.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "properties": {
                  "body": {
                    "type": "string",
                    "maxLength": 3000
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 60
                  }
                }
              },
              "example": {
                "body": "We need this for our audit too.",
                "name": "Ana"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new comment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "body": {
                      "type": "string"
                    },
                    "author": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "team": {
                      "type": "boolean"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                },
                "example": {
                  "id": "Zt7RkPq2WmXa",
                  "body": "We need this for our audit too.",
                  "author": "Ana",
                  "team": false,
                  "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/p/Qm3TfVb8KpXa"
                }
              }
            }
          },
          "400": {
            "description": "Missing or too long body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The owner key does not match this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No idea with that id on this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/boards/{boardId}/changelog": {
      "get": {
        "operationId": "getChangelog",
        "summary": "Read the changelog",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The board's id: the 20 characters after /b/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8,32}$"
            }
          },
          {
            "name": "x-owner-key",
            "in": "header",
            "required": false,
            "description": "Optional. With the board's owner key, hidden ideas are included.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entries, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "board": {
                      "type": "object"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Entry"
                      }
                    }
                  }
                },
                "example": {
                  "board": {
                    "id": "Hn4kWq8ZtR2mXc7pLs9a",
                    "name": "Acme feedback",
                    "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a"
                  },
                  "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/changelog",
                  "entries": [
                    {
                      "id": "Lp4XqWn7TzRa",
                      "title": "Dark mode is here",
                      "body": "It follows your system setting.",
                      "publishedAt": "2026-10-19T15:00:00.000Z",
                      "shipped": [
                        {
                          "id": "Qm3TfVb8KpXa",
                          "title": "Dark mode",
                          "details": "Easier on the eyes at night.",
                          "status": "done",
                          "statusLabel": "Done",
                          "votes": 42,
                          "comments": 5,
                          "tags": [
                            "Idea"
                          ],
                          "pinned": false,
                          "author": "Sam",
                          "createdAt": "2026-10-19T12:00:00.000Z",
                          "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/p/Qm3TfVb8KpXa"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The owner key does not match this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No board with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createChangelogEntry",
        "summary": "Publish a changelog entry (owner)",
        "description": "Links the ideas it shipped, so their voters see it, and by default marks them done.",
        "parameters": [
          {
            "name": "boardId",
            "in": "path",
            "required": true,
            "description": "The board's id: the 20 characters after /b/ in its link.",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{8,32}$"
            }
          },
          {
            "name": "x-owner-key",
            "in": "header",
            "required": true,
            "description": "The board's owner key, from POST /boards or the admin link.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 140
                  },
                  "body": {
                    "type": "string",
                    "maxLength": 10000,
                    "description": "Markdown."
                  },
                  "posts": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "type": "string"
                    },
                    "description": "Ids of the ideas it shipped."
                  },
                  "markDone": {
                    "type": "boolean",
                    "default": true
                  }
                }
              },
              "example": {
                "title": "Dark mode is here",
                "body": "It follows your system setting. Thanks to everyone who voted.",
                "posts": [
                  "Qm3TfVb8KpXa"
                ],
                "markDone": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new entry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "body": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "shipped": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Post"
                      }
                    }
                  }
                },
                "example": {
                  "id": "Lp4XqWn7TzRa",
                  "title": "Dark mode is here",
                  "body": "It follows your system setting. Thanks to everyone who voted.",
                  "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/changelog",
                  "shipped": [
                    {
                      "id": "Qm3TfVb8KpXa",
                      "title": "Dark mode",
                      "details": "Easier on the eyes at night.",
                      "status": "done",
                      "statusLabel": "Done",
                      "votes": 42,
                      "comments": 5,
                      "tags": [
                        "Idea"
                      ],
                      "pinned": false,
                      "author": "Sam",
                      "createdAt": "2026-10-19T12:00:00.000Z",
                      "url": "https://feedback-free.openapps.xyz/b/Hn4kWq8ZtR2mXc7pLs9a/p/Qm3TfVb8KpXa"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Missing title, or a post id that is not on this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "No owner key was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The owner key does not match this board.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No board with that id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "A sentence that says what to change."
          }
        }
      },
      "Board": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "intro": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "roadmapUrl": {
            "type": "string",
            "format": "uri"
          },
          "changelogUrl": {
            "type": "string",
            "format": "uri"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "posts": {
            "type": "integer"
          },
          "byStatus": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          }
        }
      },
      "CreatedBoard": {
        "type": "object",
        "properties": {
          "boardId": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "adminUrl": {
            "type": "string",
            "format": "uri",
            "description": "Private: opens the board as an owner on any device."
          },
          "ownerKey": {
            "type": "string",
            "description": "Private: send as x-owner-key for owner actions."
          },
          "board": {
            "$ref": "#/components/schemas/Board"
          },
          "note": {
            "type": "string"
          }
        }
      },
      "NewPost": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 140
          },
          "details": {
            "type": "string",
            "maxLength": 5000,
            "description": "Markdown."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tag names from the board."
          },
          "name": {
            "type": "string",
            "maxLength": 60,
            "description": "Author name shown with the idea."
          }
        }
      },
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "details": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "review",
              "planned",
              "progress",
              "done",
              "closed"
            ]
          },
          "statusLabel": {
            "type": "string"
          },
          "votes": {
            "type": "integer"
          },
          "comments": {
            "type": "integer"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "pinned": {
            "type": "boolean"
          },
          "hidden": {
            "type": "boolean",
            "description": "Present and true only for hidden ideas, which only owners see."
          },
          "mergedInto": {
            "type": "string",
            "description": "Present when the idea was merged into another one."
          },
          "author": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "author": {
            "type": [
              "string",
              "null"
            ]
          },
          "team": {
            "type": "boolean"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Entry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "publishedAt": {
            "type": "string",
            "format": "date-time"
          },
          "shipped": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Post"
            }
          }
        }
      }
    }
  }
}
