{"openapi":"3.0.3","info":{"title":"Release Manager API","version":"v1","description":"Versioned JSON+file API for pushing release packages from CI and pulling them back down. All endpoints are under `/api/v1`, rate-limited to 120 requests/60s per IP, and write an audit log entry on every call.\n\n**Auth:** bearer tokens are scoped to exactly one project with explicit abilities (`upload`, `download`, `read`); a token issued for one project always gets 403 on another. `GET` endpoints on **public** projects work without a token. Create tokens under **API Tokens** in the web UI.\n\nClick **Authorize** below and paste a bearer token to try requests directly from this page."},"servers":[{"url":"https://release.joeduke.cz/api/v1","description":"This server"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Projects"},{"name":"Releases"},{"name":"Assets"}],"paths":{"/projects":{"get":{"tags":["Projects"],"summary":"List public projects","description":"Only projects with `visibility=public` are listed here; private projects must be fetched directly by slug with an authorized token.","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}}}}}}}}},"/projects/{slug}":{"get":{"tags":["Projects"],"summary":"Get a project","description":"Requires the `read` ability unless the project is public.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"demo"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Project"}}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Token not authorized for this project or missing the 'read' ability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{slug}/releases":{"get":{"tags":["Releases"],"summary":"List releases for a project","description":"Requires the `read` ability unless the project is public.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"demo"},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","schema":{"type":"integer","default":20,"maximum":100}},{"name":"channel","in":"query","schema":{"type":"string","enum":["stable","beta","rc"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Release"}},"meta":{"$ref":"#/components/schemas/PageMeta"}}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Token not authorized for this project or missing the 'read' ability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Releases"],"summary":"Create or update a release","description":"Upserts by `version` - safe for CI to call every build. Requires the `upload` ability (uploader/admin role).","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"demo"}],"requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["version"],"properties":{"version":{"type":"string","example":"1.2.0","description":"Release/tag identifier; matches `[A-Za-z0-9][A-Za-z0-9.\\-_+]*`"},"channel":{"type":"string","enum":["stable","beta","rc"],"default":"stable"},"name":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true,"description":"Changelog, rendered on the release page"},"is_prerelease":{"type":"boolean","default":false},"is_draft":{"type":"boolean","default":false}}}}}},"responses":{"200":{"description":"Existing release updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Release"}}}}}},"201":{"description":"New release created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Release"}}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Token not authorized for this project, missing the 'upload' ability, or the token owner's role can't upload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid version string or channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{slug}/releases/latest":{"get":{"tags":["Releases"],"summary":"Get the latest release on a channel","description":"Requires the `read` ability unless the project is public.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"demo"},{"name":"channel","in":"query","schema":{"type":"string","default":"stable"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Release"}}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Token not authorized for this project or missing the 'read' ability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project not found, or no releases on that channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{slug}/releases/{version}":{"get":{"tags":["Releases"],"summary":"Get a release and its assets","description":"Requires the `read` ability unless the project is public.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"demo"},{"name":"version","in":"path","required":true,"schema":{"type":"string"},"example":"1.2.0"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Release"}}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Token not authorized for this project or missing the 'read' ability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project or release not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{slug}/releases/{version}/assets":{"post":{"tags":["Assets"],"summary":"Upload a release asset","description":"Multipart upload of one package file. Auto-creates the release (channel `stable`) if it doesn't exist yet - this is the single call a GitHub Actions build step needs. Requires the `upload` ability. Set `X-Checksum-SHA256` to have the server verify the upload and reject it (422) on mismatch.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"demo"},{"name":"version","in":"path","required":true,"schema":{"type":"string"},"example":"1.2.0"},{"name":"X-Checksum-SHA256","in":"header","required":false,"description":"Optional client-computed sha256 of the file; verified server-side.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file"],"properties":{"file":{"type":"string","format":"binary"},"platform":{"type":"string","nullable":true,"example":"windows"},"arch":{"type":"string","nullable":true,"example":"x64"}}}}}},"responses":{"200":{"description":"Existing asset replaced","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssetUploadResult"}}}}}},"201":{"description":"New asset stored","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssetUploadResult"}}}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Token not authorized for this project, missing the 'upload' ability, or the token owner's role can't upload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"No/invalid file, disallowed extension, file too large, or checksum mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/projects/{slug}/releases/{version}/assets/{filename}":{"get":{"tags":["Assets"],"summary":"Download a release asset","description":"Streamed file download; increments the asset's download_count. Requires the `download` ability unless the project is public.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"example":"demo"},{"name":"version","in":"path","required":true,"schema":{"type":"string"},"example":"1.2.0"},{"name":"filename","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The file bytes","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Token not authorized for this project or missing the 'download' ability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Project, release, or asset not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Create a token under API Tokens in the web UI. Tokens are scoped to one project and carry explicit abilities (upload/download/read)."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"}}}}},"PageMeta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"}}},"Project":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"github_repo":{"type":"string","nullable":true,"example":"owner/repo"},"description":{"type":"string","nullable":true},"visibility":{"type":"string","enum":["public","private"]}}},"Asset":{"type":"object","properties":{"filename":{"type":"string"},"size_bytes":{"type":"integer"},"sha256":{"type":"string"},"content_type":{"type":"string","nullable":true},"platform":{"type":"string","nullable":true},"arch":{"type":"string","nullable":true},"download_count":{"type":"integer"},"url":{"type":"string","format":"uri"}}},"AssetUploadResult":{"type":"object","properties":{"filename":{"type":"string"},"size_bytes":{"type":"integer"},"sha256":{"type":"string"},"url":{"type":"string","format":"uri"}}},"Release":{"type":"object","properties":{"version":{"type":"string","example":"1.2.0"},"name":{"type":"string","nullable":true},"channel":{"type":"string","enum":["stable","beta","rc"]},"notes":{"type":"string","nullable":true},"is_prerelease":{"type":"boolean"},"is_draft":{"type":"boolean"},"published_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"},"url":{"type":"string","format":"uri"},"assets":{"type":"array","items":{"$ref":"#/components/schemas/Asset"},"description":"Only present on the single-release and latest-release responses."}}}}}}