Endpoints
Every endpoint of the submission API, with a complete example.
Coding agents: read this page as markdown.
All paths are relative to https://releaseszn.com/api/v1. Endpoints marked
with a scope need an API token with that scope; the others are public. See
API overview for authentication and errors.
The same reference, machine-readable: the OpenAPI 3.1 spec at
/api/v1/openapi.json, generated from the server’s
code, with every request field, response and error format.
Account #
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /me | any | The token’s owner, the token’s name, scopes and expiry, its rate limit, and whether GitHub still accepts the owner’s sign-in (github.status) |
DELETE | /me?confirm={handle} | submit | Delete the account and everything tied to it, for good (ask the person first; without confirm: 400 confirmation_required) |
POST | /auth/device | none | Start a device sign-in: optional client_name; answers device_code (secret), user_code, verification_uri_complete to show the author, expires_in (600) and interval (5). 10 an hour per address |
POST | /auth/device/token | none | Poll with device_code every interval seconds: authorization_pending, slow_down, access_denied or expired_token until the author approves, then the new submit token, once |
GET | /tags | none | Valid tags, optionally filtered with ?kind= |
GET | /rules | none | Current eligibility rules, media limits and llm_providers |
POST | /feedback | none | Send the operators an idea, a bug report or a question: kind (idea, bug, question, other), message (10–5,000 characters), optional name, email, page. With a token the message carries your handle. 5 an hour per account, or per address without a token (429 feedback_limited) |
Projects #
| Method | Path | Scope | Description |
|---|---|---|---|
POST | /projects | submit | Create or claim a project from a GitHub repository |
GET | /projects?mine=true | read | Your projects, 20 a page (limit, cursor) |
GET | /projects/{slug} | none | One project |
PATCH | /projects/{slug} | submit | Update summary, description, homepage_url or tags |
POST | /projects/{slug}/sync | submit | Re-import metadata and releases from GitHub |
GET | /projects/{slug}/stats | read | Your project’s stats (owner only; ?period=7d, 30d or all), explained |
DELETE | /projects/{slug}?confirm={slug} | submit | Delete the project permanently: releases, screenshots, votes; ask the author first |
Create a project:
{
"repo_url": "https://github.com/alice/ripfast",
"summary": "A grep that is fast on huge monorepos",
"tags": ["cat/cli", "platform/linux", "ai/assisted"]
}
The response is 201 with import_status: "pending", or 200 if you already
own the project. Language and license tags are detected during import.
Releases #
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /projects/{slug}/releases | none | Releases, 20 a page (limit, cursor, status); drafts only for the owner |
GET | /projects/{slug}/releases/{tag} | none | One release |
PATCH | /projects/{slug}/releases/{tag} | submit | Update title, changelog or models |
POST | /projects/{slug}/releases/{tag}/check | submit | Dry run: every rule, no side effects |
POST | /projects/{slug}/releases/{tag}/publish | submit | Publish the release |
Releases are imported from GitHub; they move from draft to published.
Older releases are imported as history and never ranked. A check response
lists every rule:
{
"tag": "v1.2.0",
"publishable": true,
"eligible": false,
"gates": [
{ "gate": "hero", "ok": true },
{ "gate": "changelog", "ok": true },
{ "gate": "semver_kind", "ok": false, "code": "patch_release",
"fix": "Patch releases reach followers but aren't ranked." }
]
}
Optionally report which LLMs helped build the release, if the author agrees
(at most 5; provider is one of anthropic, openai, google, meta,
mistral, deepseek, xai, alibaba, other):
{
"models": [
{ "provider": "anthropic", "model": "claude-opus-5-5" },
{ "provider": "other", "model": "my-local-finetune" }
]
}
Sending "models": [] removes them. Models are shown on the release page
and never affect ranking.
publishable means the release can be published. eligible means it will
also be ranked; see Ranking.
A release’s kind (initial, major, minor, patch or other) is set
when it’s imported, so read it before tagging a new release just to be
ranked; Ranking says how it’s decided. A draft reads
"eligible": false with "ineligible_reason": "not_decided": eligibility
is recorded when it’s published, and check shows it before.
publish answers with the release, now published, under release, and
the report it passed under report:
{
"release": {
"tag": "v1.2.0",
"kind": "minor",
"status": "published",
"posted_at": "2026-10-17T10:31:00Z",
"forge_published_at": "2026-10-16T18:02:11Z",
"eligible": true,
"ineligible_reason": null,
"url": "https://releaseszn.com/p/ripfast/v1.2.0"
},
"report": { "tag": "v1.2.0", "publishable": true, "eligible": true, "gates": [] }
}
posted_at is when it was published here, forge_published_at when it was
released on GitHub. The example leaves out fields (changelog, media,
models and others), which are the same as in GET.
Media #
| Method | Path | Scope | Description |
|---|---|---|---|
POST | /projects/{slug}/media | submit | Add media by URL import or presigned upload |
POST | /media/{id}/complete | submit | Finish a presigned upload |
GET | /media/{id} | none | Status, URLs and the quality report |
PATCH | /media/{id} | submit | Update alt_text, position, release_tag or role |
DELETE | /media/{id} | submit | Remove |
Import a screenshot from a URL, the simplest option for agents:
{
"source_url": "https://raw.githubusercontent.com/alice/ripfast/v1.2.0/docs/screenshot.png",
"release_tag": "v1.2.0",
"role": "hero",
"alt_text": "ripfast results for TODO across the Linux kernel, grouped by file"
}
The response is 202 with status: "pending"; the server fetches and checks
the file. To upload a local file instead, send filename, content_type
(image/png, image/jpeg or image/webp; for the carousel also
image/gif, or application/x-asciicast for an asciinema .cast terminal
recording) and bytes. The 201 response
includes upload.url and upload.headers; PUT the file there, then call
POST /media/{id}/complete:
curl -sS -X PUT "$UPLOAD_URL" -H "Content-Type: image/png" --data-binary @screenshot.png
curl -sS -X POST "$API/media/$ID/complete" -H "$AUTH"
If the file fails processing you can PUT another to the same URL and
complete once more; after that (409 upload_spent) add it as new media.
GET /media/{id} reports progress. Every media response has a next
sentence saying what to do. Once status is ready, urls has the
processed files. While the release is a draft they are private: the links
are for you, work for an hour, and a new request gives fresh ones.
Publishing the release makes them the permanent public addresses.
quality lists each check, with a fix for those that failed:
{
"id": 110, "role": "hero", "status": "ready", "width": 2560, "height": 1600,
"hero_ok": false,
"quality": { "hero_ok": false, "checks": [
{ "check": "resolution", "ok": true, "value": "2560x1600", "min": "1200x630" },
{ "check": "borders", "ok": false, "value": "top 22%, right 0%, bottom 0%, left 0%",
"max": "15%",
"fix": "Crop away the empty borders: about 352 px off the top (22%). …" }
] },
"next": "The image doesn't pass every hero check yet. Follow the `fix` …"
}
A file that can’t be used ends with status: "failed" and an error with
its own fix, e.g. a GitHub page instead of the raw file. See
Errors for every code.
Public read API #
These need no token.
| Method | Path | Description |
|---|---|---|
GET | /feed/{hot,fresh,new} | Release cards |
GET | /boards/{day,week,month} | Current standings |
GET | /boards/{board}/{period} | Final placements, e.g. /boards/week/2026-W41 |
GET | /users/{handle} | A public profile and its projects |
Complete example #
Publishing alice/ripfast v1.2.0 with curl:
API=https://releaseszn.com/api/v1
AUTH="Authorization: Bearer $RELEASESZN_TOKEN"
# 1. Create the project (safe to repeat)
curl -sS -X POST "$API/projects" -H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"repo_url": "https://github.com/alice/ripfast",
"summary": "A grep that is fast on huge monorepos",
"tags": ["cat/cli", "platform/linux", "ai/assisted"]}'
# 2. Write the changelog
curl -sS -X PATCH "$API/projects/ripfast/releases/v1.2.0" -H "$AUTH" \
-H "Content-Type: application/json" \
-d '{"title": "Parallel directory walker",
"changelog": "- Traversal runs on all cores: 3.1x faster on the Linux kernel tree."}'
# 3. Attach the hero screenshot
curl -sS -X POST "$API/projects/ripfast/media" -H "$AUTH" \
-H "Content-Type: application/json" -H "Idempotency-Key: $(uuidgen)" \
-d '{"source_url": "https://raw.githubusercontent.com/alice/ripfast/v1.2.0/docs/screenshot.png",
"release_tag": "v1.2.0", "role": "hero",
"alt_text": "ripfast results for TODO across the Linux kernel"}'
# 4. Check, then publish
curl -sS -X POST "$API/projects/ripfast/releases/v1.2.0/check" -H "$AUTH"
curl -sS -X POST "$API/projects/ripfast/releases/v1.2.0/publish" -H "$AUTH" \
-H "Idempotency-Key: $(uuidgen)"