API reference

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 #

MethodPathScopeDescription
GET/meanyThe 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}submitDelete the account and everything tied to it, for good (ask the person first; without confirm: 400 confirmation_required)
POST/auth/devicenoneStart 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/tokennonePoll 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/tagsnoneValid tags, optionally filtered with ?kind=
GET/rulesnoneCurrent eligibility rules, media limits and llm_providers
POST/feedbacknoneSend 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 #

MethodPathScopeDescription
POST/projectssubmitCreate or claim a project from a GitHub repository
GET/projects?mine=truereadYour projects, 20 a page (limit, cursor)
GET/projects/{slug}noneOne project
PATCH/projects/{slug}submitUpdate summary, description, homepage_url or tags
POST/projects/{slug}/syncsubmitRe-import metadata and releases from GitHub
GET/projects/{slug}/statsreadYour project’s stats (owner only; ?period=7d, 30d or all), explained
DELETE/projects/{slug}?confirm={slug}submitDelete 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 #

MethodPathScopeDescription
GET/projects/{slug}/releasesnoneReleases, 20 a page (limit, cursor, status); drafts only for the owner
GET/projects/{slug}/releases/{tag}noneOne release
PATCH/projects/{slug}/releases/{tag}submitUpdate title, changelog or models
POST/projects/{slug}/releases/{tag}/checksubmitDry run: every rule, no side effects
POST/projects/{slug}/releases/{tag}/publishsubmitPublish 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 #

MethodPathScopeDescription
POST/projects/{slug}/mediasubmitAdd media by URL import or presigned upload
POST/media/{id}/completesubmitFinish a presigned upload
GET/media/{id}noneStatus, URLs and the quality report
PATCH/media/{id}submitUpdate alt_text, position, release_tag or role
DELETE/media/{id}submitRemove

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.

MethodPathDescription
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)"