Developer Documentation

Two public read-only endpoints for in-app update checks and maintenance notices, plus the contract for your own release page HTML.

Both APIs are public, credential-less and CORS-enabled (Access-Control-Allow-Origin: *). Replace <appname> with the app path you set in the console. No API key is required, and no download URL is ever returned — downloads only happen on the release page.

Version Check API

Returns the metadata of the app's current version.

GET https://api.apprelease.click/<appname>/version/latest

Response fields

FieldTypeDescription
versionstringVersion label you entered when uploading, free-form text (e.g. 1.2.3). The platform never parses or compares it.
notesstringRelease notes, may be empty.
filenamestringOriginal file name of the package.
sizenumberFile size in bytes.
uploaded_atstringUpload time, ISO 8601 UTC.
page_urlstringPermanent release page URL to open for the download.
min_supported_versionstringOldest client version you still support. Empty string when unset — compare it yourself if you use it.
force_updatebooleanWhether the current release is a mandatory update.

Example

{
  "version": "1.2.3",
  "notes": "Fixed login crash on Android 14.",
  "filename": "pixelcraft-1.2.3.apk",
  "size": 18432000,
  "uploaded_at": "2025-05-04T09:12:30Z",
  "page_url": "https://apprelease.click/pixelcraft",
  "min_supported_version": "1.2.0",
  "force_update": false
}

Status codes

CodeMeaning
200Success.
404App not found or banned, or no package uploaded yet. Body: {"error":"..."}.

Maintenance API

Reports whether the app is under maintenance. It always answers 200 even when the app is fine, so your client only needs one code path. Maintenance never affects the release page or downloads.

GET https://api.apprelease.click/<appname>/maintenance
{
  "maintenance": true,
  "notice": "Upgrading the backend, back around 06:00 UTC.",
  "resume_at": "2025-05-04T06:00:00Z"
}

notice and resume_at are null when unset. resume_at is informational only — the app stays in maintenance until you turn it off in the console.

Release Page Contract

Upload your own HTML in the console under Release Page, or use the built-in default download page.

Download action

Add the class apprelease-download-action to any element that should trigger the download:

<a class="apprelease-download-action" href="#">Download for Android</a>

Your HTML is served in a cross-origin frame on the main domain. Clicks on elements with that class are intercepted and forwarded to the shell page, which opens the download confirmation and serves the file from the dedicated download domain. Write nothing else on the element — no URL, no JavaScript. With no package uploaded yet the action is inert.

Placeholders

TokenReplaced with
{{version}}Version label of the current package (HTML-escaped; empty when no package).
{{sha256}}SHA256 checksum of the current package (HTML-escaped; empty when unknown).

Whitespace inside the braces is tolerated: {{ version }} works too. Everything else in your HTML is served as uploaded.

Code Samples

curl

curl -s https://api.apprelease.click/pixelcraft/version/latest

JavaScript / TypeScript

const res = await fetch('https://api.apprelease.click/pixelcraft/version/latest');
if (!res.ok) throw new Error('version check failed: ' + res.status);
const latest = await res.json();

if (latest.version !== currentVersion) {
  // latest.force_update decides whether the user may skip this update.
  openInBrowser(latest.page_url);
}

Kotlin (Android)

val json = URL("https://api.apprelease.click/pixelcraft/version/latest")
    .openConnection()
    .run { (this as HttpURLConnection).connectTimeout = 5000; inputStream.bufferedReader().readText() }
val latest = JSONObject(json)
val forceUpdate = latest.getBoolean("force_update")

Swift (iOS / macOS)

let url = URL(string: "https://api.apprelease.click/pixelcraft/version/latest")!
let (data, _) = try await URLSession.shared.data(from: url)
let latest = try JSONDecoder().decode(Latest.self, from: data)

struct Latest: Decodable {
  let version: String
  let notes: String
  let pageURL: String
  let forceUpdate: Bool
  enum CodingKeys: String, CodingKey {
    case version, notes
    case pageURL = "page_url"
    case forceUpdate = "force_update"
  }
}

Dart (Flutter)

final res = await http.get(
  Uri.parse('https://api.apprelease.click/pixelcraft/version/latest'),
);
if (res.statusCode == 200) {
  final latest = jsonDecode(res.body) as Map<String, dynamic>;
  final forceUpdate = latest['force_update'] as bool;
  // Send the user to latest['page_url'] — downloads only happen there.
}

Client Guidance

The endpoints are read-only and cheap, but they are still served from a single origin — check for updates at most once per app launch, and cache the answer for a few minutes on your side.