All endpoints are served on the same port as the frontend (default 3001).
Write endpoints require a token when AUTH_TOKEN is configured on the server. Pass it as either:
Authorization: Bearer <token>
or
X-Auth-Token: <token>
If AUTH_TOKEN is empty, all write endpoints are open. Read endpoints are always public.
All limits are per IP:
| Bucket | Endpoints | Default limit |
|---|---|---|
| Upload | POST /api/dump/upload, POST /api/dump/import |
10 / 10 s |
| Delete-by-key | GET/POST /api/delete/:key |
10 / 10 s |
| General | GET /api/dump/:id, …/manifest, GET /api/dumps, DELETE /api/dump/:id |
100 / 10 s (GENERAL_RATE_LIMIT) |
| Modpack | GET /api/dump/:id/modpack |
10 / 60 s (MODPACK_RATE_LIMIT) |
Token-gated endpoints additionally share an auth-failure budget: only requests answered with 401 count, so legitimate use is never throttled. After AUTH_FAIL_LIMIT failures (default 10) within 15 minutes, the IP receives 429 for all token-gated requests — even with a valid token — until the window expires. Failed auth attempts are also delayed by AUTH_FAIL_DELAY_MS (default 500 ms) before the 401 is sent.
Health check.
Response 200: { "ok": true }
Upload a dump zip from disk.
- Auth: required if
AUTH_TOKENis set - Content-Type:
multipart/form-data - Field:
file— the.zipfile (max 512 MB) - Field:
ttl(optional) — how long the dump survives on the server, in seconds. Max31536000(1 year). Defaults to 1 year.
Response 200:
{ "id": "550e8400-e29b-41d4-a716-446655440000", "deleteKey": "<base64url-encoded key>" }The id is the manifest_id from manifest.json inside the zip (UUID v4). The deleteKey can be used with GET/POST /api/delete/:key to delete the dump later without the server auth token.
curl example:
# Upload with default TTL (1 year)
curl -X POST http://localhost:3001/api/dump/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@my-dump.zip"
# Upload with a 7-day TTL
curl -X POST http://localhost:3001/api/dump/upload \
-H "Authorization: Bearer $TOKEN" \
-F "file=@my-dump.zip" \
-F "ttl=604800"Fetch a dump zip from a remote URL and store it on the server.
- Auth: required if
AUTH_TOKENis set - Content-Type:
application/json - Body:
{ "url": "https://example.com/dump.zip", "ttl": 604800 }url— required. Must be a publichttp/httpsaddress. Private and loopback addresses are blocked (SSRF protection). Up to 5 HTTP redirects are followed; each redirect target is validated against the same rules. Max size: 512 MB.ttl(optional) — how long the dump survives on the server, in seconds. Max31536000(1 year). Defaults to 1 year.
Response 200:
{ "id": "550e8400-e29b-41d4-a716-446655440000", "deleteKey": "<base64url-encoded key>" }curl example:
# Import with a 24-hour TTL
curl -X POST http://localhost:3001/api/dump/import \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/dump.zip", "ttl": 86400}'Download the raw zip for a stored dump. Always public — no auth required.
GET /api/dump/550e8400-e29b-41d4-a716-446655440000
Returns the zip with Content-Type: application/zip. Returns 404 if no dump with that id exists, 400 if the id is not a valid UUID v4.
Response headers:
| Header | Description |
|---|---|
X-Expires-At |
ISO 8601 timestamp when the dump will be deleted (e.g. 2027-03-16T12:00:00.000Z). Present only when a TTL sidecar exists. |
Expires |
Same timestamp in HTTP-date format, for caches. Present only when a TTL sidecar exists. |
Return the parsed manifest.json from a stored dump as JSON. Always public — no auth required.
GET /api/dump/550e8400-e29b-41d4-a716-446655440000/manifest
Returns the raw manifest object, including the hashes field for v2 dumps. Useful for programmatic access (e.g. building Modrinth/CurseForge/Prism modpacks).
Response 200:
{
"manifest_version": 2,
"manifest_id": "550e8400-e29b-41d4-a716-446655440000",
"versions": { "skyblockbuilder": "2.0", "minecraft": "1.21.1" },
"hashes": {
"somemod": { "md5": "abc123", "sha1": "def456", "sha512": "ghi789" }
},
"files": []
}Returns 404 if no dump with that id exists, 400 if the id is not a valid UUID v4.
Delete a stored dump.
- Auth: required if
AUTH_TOKENis set
Response: 204 No Content on success.
curl example:
curl -X DELETE http://localhost:3001/api/dump/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer $TOKEN"Show a confirmation page for deleting a stored dump using the delete key returned by the upload or import endpoint. No auth token required — the key itself is the credential. Rate limited.
GET /api/delete/<deleteKey>
The key encodes the dump id using the server's RSA private key. The server recovers the id from the key and, if the dump exists, returns an HTML page with a delete button. The actual deletion happens via POST /api/delete/:key (the page's form submits to the same URL), so opening the link — or a link prefetcher following it — never deletes anything by itself.
Response 200: HTML confirmation page
Response 400: Invalid delete key — key could not be decoded or did not contain a valid id
Response 404: Not found — key is valid but the dump no longer exists (already deleted or expired)
Delete a stored dump using its delete key. No auth token required — the key itself is the credential. Rate limited.
Response 200: Deleted (plain text)
Response 400: Invalid delete key — key could not be decoded or did not contain a valid id
Response 404: Not found — key is valid but the dump no longer exists (already deleted or expired)
curl example:
curl -X POST http://localhost:3001/api/delete/<deleteKey>List all stored dumps, sorted newest first.
- Auth: required if
AUTH_TOKENis set
Response 200:
{
"dumps": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"size": 4096,
"createdAt": "2025-01-01T00:00:00.000Z",
"expiresAt": "2026-01-01T00:00:00.000Z",
"manifestVersion": 2,
"versions": {
"skyblockbuilder": "2.0",
"minecraft": "1.21.1",
"neoforge": "21.1.80"
}
}
]
}manifestVersion is the dump's manifest format version and versions is the full versions object from the manifest (all mod versions, stored verbatim). Both are null if the zip or its manifest is unreadable. The fields are recorded in the .meta sidecar at upload time; for dumps stored before this field existed they are extracted from the zip and backfilled into the sidecar on the first listing.
curl example:
curl http://localhost:3001/api/dumps \
-H "Authorization: Bearer $TOKEN"