{"openapi":"3.1.0","info":{"title":"submitby.ai","version":"0.1.0","description":"MCP/API-only project submissions. Private writes use project-scoped control tokens."},"servers":[{"url":"https://submitby.ai"}],"components":{"securitySchemes":{"ControlToken":{"type":"http","scheme":"bearer"}}},"paths":{"/api/v1/submissions":{"post":{"summary":"Create private draft","parameters":[{"in":"header","name":"Idempotency-Key","required":true,"schema":{"type":"string","minLength":32}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"url":{"type":"string","maxLength":2048},"name":{"type":"string","minLength":2,"maxLength":80},"description":{"default":"","type":"string","maxLength":500},"icon_url":{"description":"Optional public HTTPS icon URL. Omit or set null to discover the first Apple touch icon, then the favicon.","anyOf":[{"type":"string","minLength":1,"maxLength":2048},{"type":"null"}]},"category":{"type":"string","maxLength":100},"tags":{"type":"string","maxLength":1500},"route":{"default":"badge","type":"string","enum":["badge","paid"]},"tagline":{"description":"One line, in your own words. Shown on cards.","type":"string","minLength":10,"maxLength":100},"long_description":{"description":"Plain text, 200-800 words. Separate paragraphs with a blank line. Explain what it does, how it works, and how it differs. Do not copy website text.","type":"string","minLength":300,"maxLength":6000},"audience":{"description":"Who the project is for.","type":"string","minLength":10,"maxLength":500},"not_for":{"description":"Who should choose something else.","type":"string","minLength":10,"maxLength":500},"screenshots":{"description":"Up to 8 screenshots. We copy them; hosted images must stay public until verification finishes.","maxItems":8,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"description":"Public HTTPS PNG, JPEG, or WebP, max 2 MB."},"alt":{"type":"string","minLength":3,"maxLength":200}},"required":["url","alt"],"additionalProperties":false}},"video_url":{"description":"Public demo video page.","type":"string","maxLength":2048},"pricing":{"type":"object","properties":{"model":{"type":"string","enum":["free","freemium","free_trial","paid","open_source","contact_sales"]},"starting_price":{"description":"For example: $6/month.","type":"string","maxLength":40},"free_plan":{"type":"boolean"}},"required":["model"],"additionalProperties":false},"platforms":{"maxItems":12,"type":"array","items":{"type":"string","enum":["web","ios","ipados","watchos","visionos","android","macos","windows","linux","chrome","firefox","safari","vscode","cli","api","mcp","wordpress","figma","slack","other"]}},"links":{"description":"Official profiles and pages of the project.","type":"object","propertyNames":{"type":"string","enum":["x","bluesky","mastodon","linkedin","github","discord","youtube","reddit","producthunt","docs","changelog","pricing","status","blog"]},"additionalProperties":{"type":"string","maxLength":2048}},"mcp_url":{"description":"Remote MCP endpoint. We check that it answers initialize.","type":"string","maxLength":2048},"openapi_url":{"type":"string","maxLength":2048},"app_store_url":{"type":"string","maxLength":2048},"play_store_url":{"type":"string","maxLength":2048},"alternatives":{"description":"Websites of similar products.","maxItems":5,"type":"array","items":{"type":"string","maxLength":2048}},"faq":{"description":"Answer the faq_questions from get_submission, or your own. Answers must match the website.","maxItems":10,"type":"array","items":{"type":"object","properties":{"question":{"type":"string","minLength":5,"maxLength":200},"answer":{"type":"string","minLength":20,"maxLength":1000}},"required":["question","answer"],"additionalProperties":false}}},"required":["url","name","description","route"],"additionalProperties":false}}}},"responses":{"200":{"description":"Success"},"201":{"description":"Draft, control_token, verification, next_actions"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/submissions/{id}":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"security":[{"ControlToken":[]}],"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}},"patch":{"security":[{"ControlToken":[]}],"requestBody":{"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":2,"maxLength":80},"description":{"default":"","type":"string","maxLength":500},"icon_url":{"description":"Optional public HTTPS icon URL. Omit or set null to discover the first Apple touch icon, then the favicon.","anyOf":[{"type":"string","minLength":1,"maxLength":2048},{"type":"null"}]},"category":{"type":"string","maxLength":100},"tags":{"type":"string","maxLength":1500},"route":{"default":"badge","type":"string","enum":["badge","paid"]},"tagline":{"anyOf":[{"description":"One line, in your own words. Shown on cards.","type":"string","minLength":10,"maxLength":100},{"type":"null"}]},"long_description":{"anyOf":[{"description":"Plain text, 200-800 words. Separate paragraphs with a blank line. Explain what it does, how it works, and how it differs. Do not copy website text.","type":"string","minLength":300,"maxLength":6000},{"type":"null"}]},"audience":{"anyOf":[{"description":"Who the project is for.","type":"string","minLength":10,"maxLength":500},{"type":"null"}]},"not_for":{"anyOf":[{"description":"Who should choose something else.","type":"string","minLength":10,"maxLength":500},{"type":"null"}]},"screenshots":{"anyOf":[{"description":"Up to 8 screenshots. We copy them; hosted images must stay public until verification finishes.","maxItems":8,"type":"array","items":{"type":"object","properties":{"url":{"type":"string","maxLength":2048,"description":"Public HTTPS PNG, JPEG, or WebP, max 2 MB."},"alt":{"type":"string","minLength":3,"maxLength":200}},"required":["url","alt"],"additionalProperties":false}},{"type":"null"}]},"video_url":{"anyOf":[{"description":"Public demo video page.","type":"string","maxLength":2048},{"type":"null"}]},"pricing":{"anyOf":[{"type":"object","properties":{"model":{"type":"string","enum":["free","freemium","free_trial","paid","open_source","contact_sales"]},"starting_price":{"description":"For example: $6/month.","type":"string","maxLength":40},"free_plan":{"type":"boolean"}},"required":["model"],"additionalProperties":false},{"type":"null"}]},"platforms":{"anyOf":[{"maxItems":12,"type":"array","items":{"type":"string","enum":["web","ios","ipados","watchos","visionos","android","macos","windows","linux","chrome","firefox","safari","vscode","cli","api","mcp","wordpress","figma","slack","other"]}},{"type":"null"}]},"links":{"anyOf":[{"description":"Official profiles and pages of the project.","type":"object","propertyNames":{"type":"string","enum":["x","bluesky","mastodon","linkedin","github","discord","youtube","reddit","producthunt","docs","changelog","pricing","status","blog"]},"additionalProperties":{"type":"string","maxLength":2048}},{"type":"null"}]},"mcp_url":{"anyOf":[{"description":"Remote MCP endpoint. We check that it answers initialize.","type":"string","maxLength":2048},{"type":"null"}]},"openapi_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]},"app_store_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]},"play_store_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]},"alternatives":{"anyOf":[{"description":"Websites of similar products.","maxItems":5,"type":"array","items":{"type":"string","maxLength":2048}},{"type":"null"}]},"faq":{"anyOf":[{"description":"Answer the faq_questions from get_submission, or your own. Answers must match the website.","maxItems":10,"type":"array","items":{"type":"object","properties":{"question":{"type":"string","minLength":5,"maxLength":200},"answer":{"type":"string","minLength":20,"maxLength":1000}},"required":["question","answer"],"additionalProperties":false}},{"type":"null"}]}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/submissions/{id}/verify":{"post":{"security":[{"ControlToken":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success"},"202":{"description":"Queued"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/submissions/{id}/badge":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"summary":"Get public badge code without a control token","security":[],"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/submissions/{id}/public":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"summary":"Read status for a public ChatGPT submission only","security":[],"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/projects":{"get":{"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/projects/{slug}":{"get":{"summary":"Read one published listing by slug or listing ID: details, FAQ, screenshots, verified signals, and passed agent-readiness checks","security":[],"parameters":[{"in":"path","name":"slug","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/submissions/{id}/checkout":{"post":{"summary":"Create or resume Stripe Checkout for any eligible submission; no control token required","security":[],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"checkout_url and expiry, or confirmed paid status"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"},"503":{"description":"Payments are disabled"}}}},"/api/v1/submissions/{id}/payment":{"post":{"summary":"Request a $4.99 badge waiver payment","x-payment-info":{"intent":"charge","method":"stripe","amount":"499","currency":"usd","description":"One-time badge waiver for an approved, verified submission. Amount in minor currency units."},"description":"Requires verified website control and approved content. No submission control token is required. When MPP is enabled, returns a direct Stripe MPP challenge with HTTP 402. Direct MPP purchases are sold by submitby.ai and do not use Managed Payments. Use /checkout for a hosted Managed Checkout link. When MPP is disabled and Managed Payments is enabled, this endpoint returns Checkout with HTTP 200. Never repeat an uncertain payment with a new purchase.","security":[],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Success"},"402":{"description":"Payment required; use WWW-Authenticate with an MPP client"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"},"503":{"description":"Payments disabled or not configured"}}}},"/api/v1/submissions/{id}/recover":{"post":{"summary":"Recover control by installing a fresh website file","description":"POST {} to receive a private new_control_token and challenge. Install the file, then POST recovery_id to activate the new token. The old token remains valid until proof succeeds.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"recovery_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/categories":{"get":{"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}},"/api/v1/tags":{"get":{"responses":{"200":{"description":"Success"},"404":{"description":"Unknown submission or invalid control token"},"409":{"description":"State conflict; follow the returned next action"},"422":{"description":"Invalid input"},"429":{"description":"Rate limited; see Retry-After"}}}}}}