EN /HU | Login

WECP

WECP — WarpEngine Catalog Protocol: the read side of the WarpEngine API, which the WarpEngine Client, the warpstore engines and the site talk to. JSON, camelCase keys, snake_case query parameters. Every response carries a WarpEngine-Version header. Authorization: Bearer … is optional everywhere. A 404 on any endpoint but /api/software means an older engine without that feature.

TS
type Timestamp = string   // ISO 8601 UTC, "2026-07-31T16:57:28.186Z"; missing = "0001-01-01T00:00:00Z"
type Path = string        // server-relative, "/file/…" or "/api/image/7"; prefix with the catalog's base URL

GET /api/service

Since 0.5. What this server is, and how to sign in. auth is null where there is no sign-in.

TS
interface Service {
  engine: "warp_engine"
  version: string                 // engine version, same as the WarpEngine-Version header
  api: {
    version: number               // shape version, the `api` field of every envelope (2)
    catalog: {
      pageSize: number            // per_page when the reader does not say
      maxPageSize: number         // the most a reader may ask for
    }
  }
  catalog: {
    gated: boolean                // can any title here require an entitlement
  }
  auth: null | {
    schemes: ["bearer"]
    device: {                     // device authorization grant, RFC 8628
      authorizeUrl: string        // POST here to start a sign-in
      tokenUrl: string            // POST here to poll for the token
      revokeUrl: string | null    // DELETE here to sign out
      verificationUrl: string     // where a person types the user code
      interval: number            // seconds between polls
    }
  }
}

GET /api/software

The catalog. Without page the whole catalog comes in one envelope; with page one page of it.

TS
interface SoftwareQuery {
  page?: number                   // from 1; absent = everything
  per_page?: number               // capped by maxPageSize
  status?: string                 // comma-separated status keys
  platform?: string               // one platform name
  category?: string               // one category key
  q?: string                      // matched against title and name
  owner_id?: number               // one publisher's softwares
  sort?: "title" | "updated" | "newest"
}

interface Catalog {
  api: number                     // 2; absent on engines before 0.13 (plain list)
  page: number
  perPage: number
  total: number                   // softwares matching the filters, all pages
  totalPages: number
  nextPage: number | null         // follow until null
  softwares: Entry[]
}

interface Entry {
  software: Software
  releases: Release[]             // newest first
  latestRelease: Release | null   // newest whose version is not "dev-…"
  webPlayableRelease: Release | null   // latestRelease when it has an "html" asset
  totalDownloads: number
  access: Access                  // since 0.5, present even under the open policy
}

interface Software {
  id: number
  ownerId: number
  name: string                    // the key of the title
  title: string
  author: string
  desc: string                    // short description
  story: string                   // long description
  license: string
  platform: string                // slug: "c64", "tic80", "godot", …; labels in /api/builds
  status: string                  // key from /api/software/vocabulary
  category: string | null         // since 0.12; key from /api/software/vocabulary
  highlighted: boolean
  imageUrl: Path | null           // default box art
  images: { url: Path, isDefault: boolean, position: number }[]
  externalLinks: { id: number, softwareId: number, label: string, url: string }[]
  platformLinks: { name: string, url: string }[]
  createdAt: Timestamp
  updatedAt: Timestamp
  deletedAt: Timestamp | null
}

interface Release {
  id: number
  softwareId: number
  version: string                 // "1.0.0", or "dev-1.0.0-branch" off the main branch
  assets: Asset[]                 // what a store installs from
  cartridgePath: Path | ""        // legacy flat fields, the same paths as the matching assets
  sourcePath: Path | ""
  htmlFolderPath: Path | ""
  docsFolderPath: Path | ""
  downloadCount: number
  createdAt: Timestamp
  updatedAt: Timestamp
  deletedAt: Timestamp | null
}

interface Asset {
  kind: "html" | "win_x86" | "win_x64" | "linux_x64" | "linux_arm64"
      | "mac_universal" | "mac_x64" | "mac_arm64" | "cartridge" | "source" | "docs"
  path: Path                      // hand it to GET /api/download?path=
}

interface Access {
  gated: boolean                  // this title can require an entitlement
  entitled: boolean | null        // true: may download; false: may not; null: anonymous caller
  price: { amountCents: number, currency: string } | null
  purchaseUrl: string | null
  webUrl: string | null
}

GET /api/software/facets

Since 0.13. Same filters as /api/software. Each count is what the value would show given the other filters.

TS
interface Facets {
  api: number
  total: number                   // matching every filter
  statuses: Record<string, number>     // status key → count
  platforms: Record<string, number>    // platform → count
  categories: Record<string, number>   // category key → count; "" for no category
}

GET /api/software/vocabulary

Since 0.13. The statuses and categories the catalog uses, in display order.

TS
interface Vocabulary {
  api: number
  statuses: {
    key: string                   // the value a software's status holds
    label: string
    position: number
    defaultVisible: boolean       // listed by a store with no preference of its own
    badge: string | null          // style hint
  }[]
  categories: {
    key: string                   // the value a software's category holds
    label: string
    position: number
    count: number                 // visible softwares carrying it
  }[]
}

GET /api/software/highlighted

The highlighted title, or null.

TS
type Highlighted = Entry | null

GET /api/builds

Platform labels and the asset kinds each platform expects.

TS
interface Builds {
  platforms: Record<string, {     // platform slug →
    label: string                 // "TIC-80", "Plus/4", "LÖVE"
    kinds: Asset["kind"][]        // what a release on this platform may carry
  }>
  allKinds: Asset["kind"][]
}

GET /api/softwares/:name/builds

Asset coverage of one title, per release.

TS
interface SoftwareBuilds {
  platform: string
  expected: Asset["kind"][]
  releases: Record<string, {      // version →
    actual: Asset["kind"][]
    missing: Asset["kind"][]
  }>
}

GET /api/download?path=

The file at path (an Asset.path), as an attachment, and a logged download. 302 to the storage URL when storage is not local — do not send the bearer token to that host. 400 blank path, 403 refused by the access policy, 404 not found.

GET /file/*path

The same files inline, without a download log: web-playable builds, docs, box art. 301 when storage is not local, 403 when refused.

POST /api/auth/device

Since 0.5, at auth.device.authorizeUrl. Starts a sign-in. 404 where /api/service says auth: null.

TS
interface DeviceRequest {
  client_name?: string            // what to call this device in the person's account
}

interface DeviceCode {
  deviceCode: string              // poll with this
  userCode: string                // show this, "WXYZ-1234"
  verificationUrl: string         // send the person here
  interval: number                // seconds between polls; wins over the descriptor's
  expiresIn: number               // seconds
}

POST /api/auth/device/token

At auth.device.tokenUrl. Poll until the state settles. 404 or 410: the grant is gone, start again.

TS
interface TokenRequest {
  device_code: string
}

interface TokenPoll {
  state: "pending" | "approved" | "denied" | "expired"
  token?: string                  // present exactly once, on the poll that finds it approved
}

DELETE /api/auth/token

At auth.device.revokeUrl. Revokes the bearer token on the request. 204 revoked, 401 no token.