{"openapi":"3.1.0","info":{"title":"WorthToTry API","version":"1.0.0","description":"Read the catalogue without authentication.\n\nWriting — submitting a tool, replying to a comment, adding a launch story — needs a bearer token, which you create in your dashboard. Agents connecting over MCP authenticate separately and never handle a token directly.\n\nAn agent can open a draft. It cannot publish, choose a launch date, or pay. A person always finishes the submission.\n\n### The admin API\n\nPaths under `https://worthtotry.com/api/admin/v1` are the same power the browser admin has, over HTTP: writing and publishing posts, editing listings, moving them between categories and tags, and working the review queue. They take the same personal access token, and answer 403 unless its owner holds the role the route names.\n\nThat is a wider boundary than the agent one above, deliberately. `/api/v1` and MCP are for makers and their agents acting on their own submission, which is why they can open a draft and never publish it. The admin API is the owner acting as the owner: the token belongs to the person who would otherwise be clicking the buttons, so it may do what those buttons do. What it still cannot do is skip the state machine — a listing becomes published only from approved, whichever door the request came through.\n\nEvery mutation writes an audit row against the token owner."},"servers":[{"url":"https://worthtotry.com/api/v1"}],"security":[],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A personal access token."}},"schemas":{"Error":{"type":"object","required":["ok","error"],"properties":{"ok":{"type":"boolean","enum":[false]},"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["unauthorized","forbidden","not_found","invalid_request","duplicate","rate_limited","server_error"]},"message":{"type":"string"},"fields":{"type":"object","additionalProperties":{"type":"string"}}}}}},"Tool":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"tagline":{"type":"string"},"url":{"type":"string","format":"uri"},"categories":{"type":"array","items":{"type":"string"}},"pricing":{"type":"string","enum":["free","freemium","paid","subscription","one_time","contact"],"nullable":true},"openSource":{"type":"boolean"},"upvotes":{"type":"integer"},"launchDate":{"type":"string","format":"date-time","nullable":true},"toolUrl":{"type":"string","format":"uri"}}},"AdminTool":{"type":"object","description":"A listing as the owner sees it: the public shape, plus its moderation state.","allOf":[{"$ref":"#/components/schemas/Tool"}],"properties":{"status":{"type":"string","enum":["draft","pending","approved","rejected","published"]},"featured":{"type":"boolean"},"deleted":{"type":"boolean"},"rejectionReason":{"type":"string","nullable":true},"comments":{"type":"integer"},"views":{"type":"integer"},"submittedVia":{"type":"string","enum":["web","api","mcp","import"]},"maker":{"type":"string","nullable":true},"updatedAt":{"type":"string","format":"date-time"},"adminUrl":{"type":"string","format":"uri"}}},"AdminPost":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"excerpt":{"type":"string","nullable":true},"kind":{"type":"string","enum":["editorial","founder","changelog","news"]},"status":{"type":"string","enum":["draft","pending","scheduled","published","rejected"]},"publishWithTool":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"toolSlug":{"type":"string","nullable":true},"readingMinutes":{"type":"integer","nullable":true},"views":{"type":"integer"},"scheduledFor":{"type":"string","format":"date-time","nullable":true},"publishedAt":{"type":"string","format":"date-time","nullable":true},"updatedAt":{"type":"string","format":"date-time"},"deleted":{"type":"boolean"},"postUrl":{"type":"string","format":"uri"}}}}},"paths":{"/tools":{"get":{"summary":"Search and page the catalogue","operationId":"listTools","parameters":[{"name":"query","in":"query","schema":{"type":"string","maxLength":120}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"pricing","in":"query","schema":{"type":"string","enum":["free","freemium","paid","subscription","one_time","contact"]}},{"name":"openSource","in":"query","schema":{"type":"string","enum":["true","false"]}},{"name":"sort","in":"query","schema":{"type":"string","enum":["newest","top"]}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},{"name":"cursor","in":"query","description":"Opaque. Pass back the `nextCursor` from the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of tools","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Tool"}},"nextCursor":{"type":"string","nullable":true}}}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tools/{slug}":{"get":{"summary":"One listing in full","operationId":"getTool","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Tool"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/tools/{slug}/comments":{"get":{"summary":"Read a listing’s discussion","operationId":"listComments","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The comments"},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Comment, or reply to one","operationId":"createComment","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body"],"properties":{"body":{"type":"string","maxLength":2000,"description":"No links — they are rejected."},"parentId":{"type":"string","description":"Reply to this comment. One level of threading only."}}}}}},"responses":{"201":{"description":"Created"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/categories":{"get":{"summary":"Every category, with how many tools each holds","operationId":"listCategories","responses":{"200":{"description":"The categories"}}}},"/readiness":{"post":{"summary":"Audit a URL before submitting it","description":"Scores the page, names every reason a listing would come back, and drafts the listing it would produce.","operationId":"checkReadiness","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"The audit"},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/submissions":{"post":{"summary":"Open a draft listing from a URL","description":"Audits the page, drafts the listing and returns a review link. Cannot publish, schedule or pay — a person finishes it.","operationId":"createSubmission","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"},"name":{"type":"string","maxLength":40},"tagline":{"type":"string","maxLength":60},"description":{"type":"string","maxLength":600},"categories":{"type":"array","maxItems":3,"items":{"type":"string"}},"pricing":{"type":"string","enum":["free","freemium","paid","subscription","one_time","contact"]},"openSource":{"type":"boolean"},"twitterHandle":{"type":"string","maxLength":15},"targetKeyword":{"type":"string","maxLength":100}}}}}},"responses":{"201":{"description":"Draft created"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with something that already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts":{"post":{"summary":"Add a launch story to your listing","description":"Saved as a draft. It publishes with the listing, or you publish it yourself. No links.","operationId":"createPost","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["toolSlug","title","body"],"properties":{"toolSlug":{"type":"string"},"title":{"type":"string","maxLength":120},"body":{"type":"string","minLength":200,"maxLength":20000},"publishWithTool":{"type":"boolean","default":true}}}}}},"responses":{"201":{"description":"Draft created"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with something that already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/me/submissions":{"get":{"summary":"Your listings and their status","operationId":"listMySubmissions","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Your submissions"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/posts":{"servers":[{"url":"https://worthtotry.com/api"}],"get":{"summary":"Every post, at any status","description":"Requires the **moderator** role or above.","operationId":"adminListPosts","security":[{"bearerAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["draft","pending","scheduled","published","rejected","deleted"]}},{"name":"kind","in":"query","schema":{"type":"string","enum":["editorial","founder","changelog","news"]}},{"name":"query","in":"query","schema":{"type":"string","maxLength":120}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of posts","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AdminPost"}},"nextCursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Write a post","description":"Requires the **admin** role.\n\nAlways saved as a draft — publishing is its own call, so the decision to put something in front of readers leaves its own audit row.\n\nEditorial posts may carry links. A `founder` post may not, and needs at least 200 characters and a listing.","operationId":"adminCreatePost","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","body"],"additionalProperties":false,"properties":{"title":{"type":"string","maxLength":120},"body":{"type":"string","maxLength":20000},"excerpt":{"type":"string","maxLength":320,"nullable":true},"coverImage":{"type":"string","format":"uri","nullable":true},"seoTitle":{"type":"string","maxLength":70,"nullable":true},"seoDescription":{"type":"string","maxLength":160,"nullable":true},"targetKeyword":{"type":"string","maxLength":100,"nullable":true},"canonicalUrl":{"type":"string","format":"uri","nullable":true},"scheduledFor":{"type":"string","format":"date-time","nullable":true},"tags":{"type":"array","maxItems":8,"items":{"type":"string"}},"toolSlug":{"type":"string","nullable":true,"description":"The listing a founder or changelog post belongs to."},"publishWithTool":{"type":"boolean"},"kind":{"type":"string","enum":["editorial","founder","changelog","news"],"default":"editorial"},"slug":{"type":"string","maxLength":80,"description":"Derived from the title when absent."}}}}}},"responses":{"201":{"description":"Created, as a draft","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminPost"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with something that already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/posts/{slug}":{"servers":[{"url":"https://worthtotry.com/api"}],"get":{"summary":"One post, body included","description":"Requires the **moderator** role or above.","operationId":"adminGetPost","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminPost"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Edit a post","description":"Requires the **admin** role.\n\nThe slug is not editable: a published URL is the one thing other people have already linked.","operationId":"adminUpdatePost","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"title":{"type":"string","maxLength":120},"body":{"type":"string","maxLength":20000},"excerpt":{"type":"string","maxLength":320,"nullable":true},"coverImage":{"type":"string","format":"uri","nullable":true},"seoTitle":{"type":"string","maxLength":70,"nullable":true},"seoDescription":{"type":"string","maxLength":160,"nullable":true},"targetKeyword":{"type":"string","maxLength":100,"nullable":true},"canonicalUrl":{"type":"string","format":"uri","nullable":true},"scheduledFor":{"type":"string","format":"date-time","nullable":true},"tags":{"type":"array","maxItems":8,"items":{"type":"string"}},"toolSlug":{"type":"string","nullable":true,"description":"The listing a founder or changelog post belongs to."},"publishWithTool":{"type":"boolean"}}}}}},"responses":{"200":{"description":"The post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminPost"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"summary":"Take a post down","description":"Requires the **admin** role.\n\nSoft delete — the row stays so a published URL can still be traced.","operationId":"adminDeletePost","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/posts/{slug}/publish":{"servers":[{"url":"https://worthtotry.com/api"}],"post":{"summary":"Publish a post","description":"Requires the **admin** role.\n\nA founder post is refused until the listing it argues for is live.","operationId":"adminPublishPost","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminPost"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/posts/{slug}/unpublish":{"servers":[{"url":"https://worthtotry.com/api"}],"post":{"summary":"Pull a post back to draft","description":"Requires the **admin** role.\n\n`publishedAt` is kept, so republishing does not jump it to the top of the blog.","operationId":"adminUnpublishPost","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminPost"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/tools":{"servers":[{"url":"https://worthtotry.com/api"}],"get":{"summary":"Every listing, at any status","description":"Requires the **moderator** role or above.","operationId":"adminListTools","security":[{"bearerAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["draft","pending","approved","rejected","published","deleted"]}},{"name":"query","in":"query","schema":{"type":"string","maxLength":120}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of listings","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/AdminTool"}},"nextCursor":{"type":"string","nullable":true}}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/tools/{slug}":{"servers":[{"url":"https://worthtotry.com/api"}],"get":{"summary":"One listing, moderation state included","description":"Requires the **moderator** role or above.","operationId":"adminGetTool","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTool"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"summary":"Edit a listing","description":"Requires the **admin** role.\n\nEvery field is held to the rule the submission form applies. `status` is not a field — use `/approve`, `/reject` or `/publish`, each of which records why it moved.","operationId":"adminUpdateTool","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","maxLength":40},"tagline":{"type":"string","maxLength":60},"description":{"type":"string","maxLength":600},"url":{"type":"string","format":"uri"},"pricing":{"type":"string","enum":["free","freemium","paid","subscription","one_time","contact"]},"openSource":{"type":"boolean"},"twitterHandle":{"type":"string","maxLength":15},"demoVideoUrl":{"type":"string","format":"uri"},"targetKeyword":{"type":"string","maxLength":100},"logo":{"type":"string"},"launchDate":{"type":"string","format":"date-time","nullable":true},"featured":{"type":"boolean"}}}}}},"responses":{"200":{"description":"The listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTool"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with something that already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/tools/{slug}/categories":{"servers":[{"url":"https://worthtotry.com/api"}],"put":{"summary":"Move a listing between categories","description":"Requires the **moderator** role or above.\n\nReplaces the whole set. Slugs and display names both resolve; an unknown one is refused.","operationId":"adminSetToolCategories","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["categories"],"additionalProperties":false,"properties":{"categories":{"type":"array","minItems":1,"maxItems":3,"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"The listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTool"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/tools/{slug}/tags":{"servers":[{"url":"https://worthtotry.com/api"}],"put":{"summary":"Replace a listing’s tags","description":"Requires the **moderator** role or above.\n\nAn empty array clears them. A tag that does not exist yet is created.","operationId":"adminSetToolTags","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tags"],"additionalProperties":false,"properties":{"tags":{"type":"array","maxItems":8,"items":{"type":"string","maxLength":40}}}}}}},"responses":{"200":{"description":"The listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTool"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/tools/{slug}/approve":{"servers":[{"url":"https://worthtotry.com/api"}],"post":{"summary":"Approve a submission","description":"Requires the **moderator** role or above.\n\nPublishes immediately unless the maker asked for a future launch date — read the `status` that comes back to tell which happened.","operationId":"adminApproveTool","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTool"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/tools/{slug}/reject":{"servers":[{"url":"https://worthtotry.com/api"}],"post":{"summary":"Reject a submission","description":"Requires the **moderator** role or above.\n\nThe reason is required — the maker sees it and has to act on it.","operationId":"adminRejectTool","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reason"],"additionalProperties":false,"properties":{"reason":{"type":"string","minLength":1,"maxLength":500}}}}}},"responses":{"200":{"description":"The listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTool"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/tools/{slug}/publish":{"servers":[{"url":"https://worthtotry.com/api"}],"post":{"summary":"Publish an approved listing now","description":"Requires the **moderator** role or above.\n\nOnly an `approved` listing can become `published`. Nothing here can skip review.","operationId":"adminPublishTool","security":[{"bearerAuth":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The listing","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTool"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such resource","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited — see the retry-after header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/admin/v1/queue":{"servers":[{"url":"https://worthtotry.com/api"}],"get":{"summary":"What is waiting for a decision","description":"Requires the **moderator** role or above.\n\nOldest first, with the count at every status.","operationId":"adminReviewQueue","security":[{"bearerAuth":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50}}],"responses":{"200":{"description":"The queue"},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated, but not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The request body or query is invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}