Build on the public
file catalog.
Read current file versions and poll observed publications and updates. The public API returns JSON metadata, not downloads. It requires IPB Downloads, but neither Downloads Plus nor Discord File Notifications.
https://weoxide.netAll example IDs, products, timestamps, versions, prices and event cursors below are illustrative, not live data. This page makes no API calls and remains readable when API access is disabled.
Enable two switches, then test as a guest
- Install
File-Updates-API-1.1.1.tarin AdminCP → System → Applications. Upgrade an existing installation with Upload New Version; do not uninstall, so cursors and integrations are preserved. - In Community → File Updates API → Settings, enable both Enable the File Updates API and Enable public access without API keys, then save.
- Configure IPB’s cron task runner for reliable scanning and cleanup. Ensure Guests can access this application’s front module and the relevant Downloads categories.
- Test
https://weoxide.net/db/?category=allsigned out. Expect JSON. If friendly routing fails, use the fallback routes below and rebuild IPB caches through AdminCP → Support.
Three GET endpoints
| Route | Contract |
|---|---|
/db/?category=all&after_id=0&limit=25 | Current catalog, ascending file ID. Omit category for all categories, or pass a single positive category ID. A category includes only files directly in it, not subcategories. No comma-separated IDs or title/author/tags/rating search filters. |
/db/files/522/ | One current file object, not a list envelope. The ID must be positive. |
/db/events//db/events/?after=123&limit=25 | Without after, returns an empty list and the current cursor, without replay. With a nonnegative cursor, scans events after it in ascending event ID order. after=0 requests available history. |
limit is an integer from 1–100, default 25. after_id defaults to 0 and must be nonnegative. Cursor IDs are integers, not timestamps. Public browser GETs support cross-origin access without cookies; OPTIONS is supported for preflight, not as a data endpoint.
Query-string routes without friendly URL rewriting
https://weoxide.net/index.php?app=fileupdates&module=catalog&controller=browse&category=all&after_id=0&limit=25
https://weoxide.net/index.php?app=fileupdates&module=catalog&controller=browse&id=522
https://weoxide.net/index.php?app=fileupdates&module=catalog&controller=browse&resource=events&after=123&limit=25Omit after from the last route to obtain the current cursor.
JSON shapes and every exported field
There is no schema compatibility promise with Codefling. File versions are creator-provided strings, not guaranteed semantic versions. Timestamps are integer Unix seconds.
Illustrative single-file response
{
"id": 522,
"title": "Example Rust Plugin",
"version": "release-2026b",
"published_at": 1780000000,
"updated_at": 1780003600,
"creator": { "id": 42, "name": "Example Creator" },
"category_id": 1,
"url": "https://weoxide.net/files/file/522-example-rust-plugin/",
"description": "Illustrative plain-text description.",
"change_notes": "Illustrative fixes and improvements.",
"is_paid": false,
"price_label": "Free"
}| Field / type | Meaning |
|---|---|
id · integer | Downloads file ID. |
title, version · string | Current title and creator’s release label. |
published_at, updated_at · integer | Submission and current update timestamps. |
creator · object | id (integer member ID) and name (string display name). |
category_id · integer | Direct Downloads category ID. |
url · string | Current marketplace page URL; never a raw download URL. |
description · string | Plain text, truncated to 2,000 characters. |
change_notes · string | Current changelog converted to plain text, truncated to 8,000 characters. |
is_paid · boolean | Whether Downloads considers the file paid. |
price_label · string | Human-readable native currency-aware price, up to 256 characters; Free for unpaid files, Unavailable if a paid price is unavailable. Not a billing amount. |
Illustrative catalog and event envelopes
Catalog data contains file objects with all fields above. This empty example is valid when scanned files are not public:
{ "data": [], "next_after_id": 525, "has_more": true }Feed data contains event objects. Both supported types are shown:
{
"data": [
{
"id": 124, "type": "published", "observed_at": 1780000005,
"file_id": 522, "version": "release-2026a", "updated_at": 1780000000,
"change_notes": "", "url": "https://weoxide.net/files/file/522-example-rust-plugin/"
},
{
"id": 125, "type": "updated", "observed_at": 1780003605,
"file_id": 522, "version": "release-2026b", "updated_at": 1780003600,
"change_notes": "Illustrative fixes and improvements.",
"url": "https://weoxide.net/files/file/522-example-rust-plugin/"
}
],
"next_cursor": 125,
"has_more": false
}A no-after feed response looks like {"data":[],"next_cursor":125,"has_more":false}; an installation with no events has cursor 0.
| Field / type | Meaning |
|---|---|
id · integer | Event ID; use for deduplication. |
type · string | published or updated. Becoming visible again can produce a publication. |
observed_at · integer | When the application observed the release, not necessarily when it happened. |
file_id · integer | Downloads file ID for current metadata lookup. |
version · string | Observed version snapshot, capped at 255 characters. |
updated_at · integer | File update timestamp captured at observation. |
change_notes · string | Observed update notes, plain text up to 8,000 characters. Publications have empty notes. |
url · string | Current marketplace page URL, resolved at request time. |
Observed notes, version and update timestamp are snapshots, not rewritten by later releases. An event is not a complete current file object.
Advance through scanned rows, even empty pages
limit counts database rows scanned, not public results returned. Catalog scans files; feed scans events. Hidden, private, deleted or newly restricted event files are skipped at request time. A page may contain fewer results than the limit—or none—and still have has_more: true.
Always use the returned next_after_id for catalog pages or next_cursor for event pages, including empty pages. has_more means more rows remain to scan, not that another visible result is guaranteed. Keep category selection consistent while paging. Catalog pagination is an ID walk, not a snapshot or a change feed; edits to previously scanned IDs require the feed or a fresh catalog walk.
Checkpoint, bootstrap, then poll
- Call
/db/events/withoutafterand persistnext_cursor. - If needed, bootstrap
/db/, walkingnext_after_idwhilehas_moreis true. - Poll
/db/events/?after=<saved cursor>every 60–300 seconds. Drain pages whilehas_moreis true, respecting the shared rate budget. - Deduplicate by event
id. Handle a page successfully before persisting itsnext_cursor, including empty pages. Fetch/db/files/<id>/for current product metadata. - Respect throttling, back off on temporary server errors and check AdminCP switches for persistent disabled responses.
The application sends no outbound webhooks. Your integration decides what notification or update prompt to show.
Use metadata, not download automation
curl · current file and cursor
curl --header 'Accept: application/json' 'https://weoxide.net/db/files/522/'
curl --header 'Accept: application/json' 'https://weoxide.net/db/events/'
curl --header 'Accept: application/json' 'https://weoxide.net/db/events/?after=123&limit=25'Browser JavaScript · no credentials, safe text rendering
Run in your own application, with a DOM element whose ID is version-status. This documentation does not execute the example.
async function showVersion() {
const response = await fetch('https://weoxide.net/db/files/522/', {
method: 'GET',
credentials: 'omit',
headers: { Accept: 'application/json' }
});
if (!response.ok) throw new Error(`API HTTP ${response.status}`);
const file = await response.json();
document.getElementById('version-status').textContent =
`${file.title}: ${file.version}`; // Never use innerHTML for API strings.
}
showVersion().catch(error => {
document.getElementById('version-status').textContent = error.message;
});The server sends Retry-After on 429, but does not expose that header through CORS. Cross-origin browser code may not be able to read it; use a conservative wait of at least 60 seconds plus jitter when unavailable.
Plugin-friendly version check · opaque labels, not semver
C# example for a plugin’s existing HTTP callback (for example, a host web-request facility with Newtonsoft.Json). Supply the locally installed release label; the host should schedule periodic calls and retries, not block its game thread.
void HandleVersionResponse(int status, string body, string installedVersion)
{
if (status == 429) {
// Schedule a retry using Retry-After if the host exposes headers;
// otherwise wait at least 60 seconds plus jitter.
return;
}
if (status != 200) return; // Back off; log safely in the host integration.
try {
var file = Newtonsoft.Json.Linq.JObject.Parse(body);
var remote = file.Value<string>("version");
if (remote == null) return;
if (!System.String.Equals(remote, installedVersion,
System.StringComparison.Ordinal)) {
// Notify: catalog version differs. Do not claim it is newer,
// and do not automatically download or install anything.
}
} catch (Newtonsoft.Json.JsonException) {
// Invalid response: retain local state and retry later with backoff.
}
}
// GET https://weoxide.net/db/files/522/ with Accept: application/json.
// No API key or cookies. Pass status/body to the callback above.String inequality detects a different release label, not ordering. Do not use semantic-version parsers, numeric comparison or automatic upgrades unless that specific creator supplies a separate versioning contract.
60 requests per IP, per clock minute
Catalog, file and feed requests share 60 requests per IP address per clock minute, not a rolling 60-second window. Clients behind a NAT or proxy share the budget. HTTP 429 includes Retry-After seconds until the next minute. Pace pagination as well as polling.
Configure IPB’s real-client-IP handling correctly behind proxies; do not trust arbitrary forwarded-IP headers. This application throttle is not DDoS protection: keep hosting/CDN protection. The scheduled task cleans expired counters even when API access is off.
| HTTP | Error / action |
|---|---|
| 400 | INVALID_PARAMETER · fix invalid integers, categories or limits. |
| 404 | FILE_NOT_FOUND for missing or private files; ENDPOINT_NOT_FOUND for unknown resources. |
| 405 | METHOD_NOT_ALLOWED · use GET. Public API allows GET and OPTIONS. |
| 429 | RATE_LIMITED · wait for Retry-After. |
| 503 | API_DISABLED, PUBLIC_API_DISABLED or SERVICE_UNAVAILABLE · back off; verify settings if persistent. |
Guest visibility on every request
Even signed-in callers receive only non-hidden files that Guests can view. Raw download URLs, customer data, support requests, private/hidden files and payment data are never exported. There are no write, purchase or commission operations. Native download permissions are separate from this metadata API.
Metadata is cached server-side for up to 60 seconds, refreshed immediately when title, version or update timestamp changes. Guest visibility is rechecked on every request, including cache hits. Price is calculated through Downloads’ native currency-aware price() on each request, never reused from another visitor’s cache. Responses use Cache-Control: no-store so browser/CDN caching does not bypass visibility checks.
Descriptions and notes are stripped of markup and decoded as plain text. All strings remain untrusted: render them with textContent or your UI’s escaping, never as HTML. If making marketplace links clickable, validate the URL and permitted scheme in your own application. Do not interpret price labels as billing values.
Native authenticated routes are optional
The original routes /api/fileupdates/files, /api/fileupdates/files/{id} and /api/fileupdates/events remain available with IPB API keys and their respective GET permissions. Use HTTP Basic authentication with the key as username and a blank password; keep keys in trusted server-side integrations, not browser code.
Disabling the public-access switch does not disable native routes. Disabling the master API switch disables both. Grant only this application’s GET permissions; native Downloads API/download permissions are separate and unnecessary for metadata-only integrations.