TITLE=WECP ROUTE=/engines/warp-engine/wecp LOCALE=en MODIFIED=1792409344 URL=https://wiki.teletypegames.org/engines/warp-engine/wecp L=1 #22 L=2 #46 L=3 #24 L=4 #53 q│ bWECP — WarpEngine Catalog Protocolq: the read side of the 1WarpEngine API, q│ which the 2WarpEngine Client, the 3warpstore engines and the site talk to. q│ JSON, camelCase keys, snake_case query parameters. Every response carries a q│ cWarpEngine-Versionq header. cAuthorization: Bearer …q is optional everywhere. A q│ 404 on any endpoint but c/api/softwareq means an older engine without that q│ feature. p type Timestamp = string // ISO 8601 UTC, "2026-07-31T16:57:28.186Z"; missi» p ng = "0001-01-01T00:00:00Z" p type Path = string // server-relative, "/file/…" or "/api/image/7"; p» p refix with the catalog's base URL 1GET /api/service 1════════════════ Since 0.5. What this server is, and how to sign in. cauth0 is null where there is no sign-in. p interface Service { p engine: "warp_engine" p version: string // engine version, same as the WarpEngine-» p Version header p api: { p version: number // shape version, the `api` field of every» p envelope (2) p catalog: { p pageSize: number // per_page when the reader does not say p maxPageSize: number // the most a reader may ask for p } p } p catalog: { p gated: boolean // can any title here require an entitleme» p nt p } p auth: null | { p schemes: ["bearer"] p device: { // device authorization grant, RFC 8628 p authorizeUrl: string // POST here to start a sign-in p tokenUrl: string // POST here to poll for the token p revokeUrl: string | null // DELETE here to sign out p verificationUrl: string // where a person types the user code p interval: number // seconds between polls p } p } p admin: null | { // since 1.0; null while the host has it o» p ff p version: number // Admin API version (1) p url: string // its base address p openapiUrl: string // its OpenAPI 3.1 document p mcpUrl: string | null // its MCP endpoint, null while MCP is off p schemes: ["bearer"] p } p } cadmin0 points at the 4Admin API, which is not part of WECP: a catalog reader needs none of it, and everything there wants an admin token. WarpEngine 1.0 changed nothing else a reader sees — the envelope is still capi: 20. 1GET /api/software 1═════════════════ The catalog. Without cpage0 the whole catalog comes in one envelope; with cpage one page of it. p interface SoftwareQuery { p page?: number // from 1; absent = everything p per_page?: number // capped by maxPageSize p status?: string // comma-separated status keys p platform?: string // one platform name p category?: string // one category key p q?: string // matched against title and name p owner_id?: number // one publisher's softwares p sort?: "title" | "updated" | "newest" p } p p interface Catalog { p api: number // 2; absent on engines before 0.13 (plain» p list) p page: number p perPage: number p total: number // softwares matching the filters, all pag» p es p totalPages: number p nextPage: number | null // follow until null p softwares: Entry[] p } p p interface Entry { p software: Software p releases: Release[] // newest first p latestRelease: Release | null // newest whose version is not "dev-…" p webPlayableRelease: Release | null // latestRelease when it has an "html» p " asset p totalDownloads: number p access: Access // since 0.5, present even under the open » p policy p } p p interface Software { p id: number p ownerId: number p name: string // the key of the title p title: string p author: string p desc: string // short description p story: string // long description p license: string p platform: string // slug: "c64", "tic80", "godot", …; label» p s in /api/builds p status: string // key from /api/software/vocabulary p category: string | null // since 0.12; key from /api/software/voca» p bulary p highlighted: boolean p imageUrl: Path | null // default box art p images: { url: Path, isDefault: boolean, position: number }[] p externalLinks: { id: number, softwareId: number, label: string, url: strin» p g }[] p platformLinks: { name: string, url: string }[] p createdAt: Timestamp p updatedAt: Timestamp p deletedAt: Timestamp | null p } p p interface Release { p id: number p softwareId: number p version: string // "1.0.0", or "dev-1.0.0-branch" off the » p main branch p assets: Asset[] // what a store installs from p cartridgePath: Path | "" // legacy flat fields, the same paths as t» p he matching assets p sourcePath: Path | "" p htmlFolderPath: Path | "" p docsFolderPath: Path | "" p downloadCount: number p createdAt: Timestamp p updatedAt: Timestamp p deletedAt: Timestamp | null p } p p interface Asset { p kind: "html" | "win_x86" | "win_x64" | "linux_x64" | "linux_arm64" p | "mac_universal" | "mac_x64" | "mac_arm64" | "cartridge" | "source" |» p "docs" p path: Path // hand it to GET /api/download?path= p } p p interface Access { p gated: boolean // this title can require an entitlement p entitled: boolean | null // true: may download; false: may not; nul» p l: anonymous caller p price: { amountCents: number, currency: string } | null p purchaseUrl: string | null p webUrl: string | null p } 1GET /api/software/facets 1════════════════════════ Since 0.13. Same filters as c/api/software0. Each count is what the value would show given the other filters. p interface Facets { p api: number p total: number // matching every filter p statuses: Record // status key → count p platforms: Record // platform → count p categories: Record // category key → count; "" for no ca» p tegory p } 1GET /api/software/vocabulary 1════════════════════════════ Since 0.13. The statuses and categories the catalog uses, in display order. p interface Vocabulary { p api: number p statuses: { p key: string // the value a software's status holds p label: string p position: number p defaultVisible: boolean // listed by a store with no preference of» p its own p badge: string | null // style hint p }[] p categories: { p key: string // the value a software's category holds p label: string p position: number p count: number // visible softwares carrying it p }[] p } 1GET /api/software/highlighted 1═════════════════════════════ The highlighted title, or cnull0. p type Highlighted = Entry | null 1GET /api/builds 1═══════════════ Platform labels and the asset kinds each platform expects. p interface Builds { p platforms: Record p allKinds: Asset["kind"][] p } 1GET /api/softwares/:name/builds 1═══════════════════════════════ Asset coverage of one title, per release. p interface SoftwareBuilds { p platform: string p expected: Asset["kind"][] p releases: Record p } 1GET /api/download?path= 1═══════════════════════ The file at cpath0 (an cAsset.path0), 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. 1GET /file/*path 1═══════════════ The same files inline, without a download log: web-playable builds, docs, box art. 301 when storage is not local, 403 when refused. 1POST /api/auth/device 1═════════════════════ Since 0.5, at cauth.device.authorizeUrl0. Starts a sign-in. 404 where c/api/service0 says cauth: null0. p interface DeviceRequest { p client_name?: string // what to call this device in the person'» p s account p } p p interface DeviceCode { p deviceCode: string // poll with this p userCode: string // show this, "WXYZ-1234" p verificationUrl: string // send the person here p interval: number // seconds between polls; wins over the de» p scriptor's p expiresIn: number // seconds p } 1POST /api/auth/device/token 1═══════════════════════════ At cauth.device.tokenUrl0. Poll until the state settles. 404 or 410: the grant is gone, start again. p interface TokenRequest { p device_code: string p } p p interface TokenPoll { p state: "pending" | "approved" | "denied" | "expired" p token?: string // present exactly once, on the poll that » p finds it approved p } 1DELETE /api/auth/token 1══════════════════════ At cauth.device.revokeUrl0. Revokes the bearer token on the request. 204 revoked, 401 no token.