Read-only · No API key · Guest-visible

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.

Base deployment: https://weoxide.net
All 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

  1. Install File-Updates-API-1.1.1.tar in AdminCP → System → Applications. Upgrade an existing installation with Upload New Version; do not uninstall, so cursors and integrations are preserved.
  2. In Community → File Updates API → Settings, enable both Enable the File Updates API and Enable public access without API keys, then save.
  3. 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.
  4. Test https://weoxide.net/db/?category=all signed out. Expect JSON. If friendly routing fails, use the fallback routes below and rebuild IPB caches through AdminCP → Support.

Three GET endpoints

Friendly routes relative to the base deployment
RouteContract
/db/?category=all&after_id=0&limit=25Current 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=25

Omit 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"
}
File object fields
Field / typeMeaning
id · integerDownloads file ID.
title, version · stringCurrent title and creator’s release label.
published_at, updated_at · integerSubmission and current update timestamps.
creator · objectid (integer member ID) and name (string display name).
category_id · integerDirect Downloads category ID.
url · stringCurrent marketplace page URL; never a raw download URL.
description · stringPlain text, truncated to 2,000 characters.
change_notes · stringCurrent changelog converted to plain text, truncated to 8,000 characters.
is_paid · booleanWhether Downloads considers the file paid.
price_label · stringHuman-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.

Event object fields
Field / typeMeaning
id · integerEvent ID; use for deduplication.
type · stringpublished or updated. Becoming visible again can produce a publication.
observed_at · integerWhen the application observed the release, not necessarily when it happened.
file_id · integerDownloads file ID for current metadata lookup.
version · stringObserved version snapshot, capped at 255 characters.
updated_at · integerFile update timestamp captured at observation.
change_notes · stringObserved update notes, plain text up to 8,000 characters. Publications have empty notes.
url · stringCurrent 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

Read current cursor
Bootstrap catalog
Poll & drain pages
Handle, then persist
Capture the cursor before bootstrapping so observed changes during the catalog walk can be processed afterward.
  1. Call /db/events/ without after and persist next_cursor.
  2. If needed, bootstrap /db/, walking next_after_id while has_more is true.
  3. Poll /db/events/?after=<saved cursor> every 60–300 seconds. Drain pages while has_more is true, respecting the shared rate budget.
  4. Deduplicate by event id. Handle a page successfully before persisting its next_cursor, including empty pages. Fetch /db/files/<id>/ for current product metadata.
  5. Respect throttling, back off on temporary server errors and check AdminCP switches for persistent disabled responses.
This is an observed-release feed, not an audit log. Listeners observe normal submissions and edits immediately. A task scans up to 100 files per minute in ID order, repeating after the end; this catches approvals and separate Downloads version uploads. Large catalogs take several minutes per pass. Multiple uploads between observations may collapse into the latest release. Existing files are baselined on the initial scan without release alerts; a change before the first baseline may still be seen through current file metadata. There are no removal events. Reconcile the current catalog periodically if your integration must notice removals.

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.

Public error responses have shape {"error":"ERROR_NAME"}
HTTPError / action
400INVALID_PARAMETER · fix invalid integers, categories or limits.
404FILE_NOT_FOUND for missing or private files; ENDPOINT_NOT_FOUND for unknown resources.
405METHOD_NOT_ALLOWED · use GET. Public API allows GET and OPTIONS.
429RATE_LIMITED · wait for Retry-After.
503API_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.