Shop server API
Reference for running or integrating a shop backend compatible with CyberFoil.
Works best with AeroFoil. Derived from shopInstall.cpp and save_sync.cpp.
See also: Network & download · Install pipeline
Connecting from CyberFoil
Set the shop URL in Settings or save a profile under sdmc:/switch/CyberFoil/shops/.
- Default HTTP port:
8465(HTTPS defaults to443) - Example URL:
http://192.168.1.2:8465 - Auth: optional HTTP Basic (
shopUser/shopPassin config, or per-profile inshops/*.json) - Trailing slashes are stripped; bare hostnames get
http://prepended
Shop profile file example:
{
"shop": {
"protocol": "http",
"host": "192.168.1.2",
"path": "",
"port": 8465,
"username": "user",
"password": "pass",
"title": "My LAN shop",
"favourite": false
}
}
How CyberFoil loads a catalog
- If
shopLegacyModeis on → GET shop root only (Tinfoil-style). - Otherwise → GET
/api/shop/sections. - On 404 or network failure → fallback to root GET (legacy JSON or TINFOIL binary).
- After load, optional MOTD from root JSON field
success(skipped if body is TINFOIL). - Search is client-side only - filters item names in the current section. No search HTTP API.
HTTP endpoints
All catalog requests are GET. File installs use GET on each item's url (with Basic auth forwarded).
| Method | Path | Purpose |
|---|---|---|
GET | /api/shop/sections | Modern catalog (preferred) |
GET | / | Legacy catalog, MOTD, or TINFOIL payload |
GET | /api/shop/icon/{TITLEID} | Icon - 16 hex digits, uppercase (e.g. 01007EF00011E000) |
GET | {item.url} | Download NSP/NSZ/XCI/XCZ for install |
GET | /api/saves/list | Remote save backups (non-legacy mode) |
POST | /api/saves/upload/{TITLEID} | Upload save zip (multipart) |
GET | /api/saves/download/{TITLEID}.zip | Download latest save |
GET | /api/saves/download/{TITLEID}/{saveId}.zip | Download specific version |
DELETE | /api/saves/delete/{TITLEID} | Delete save(s) for title |
DELETE | /api/saves/delete/{TITLEID}/{saveId} | Delete one version |
Shop fetch: 30s timeout, up to 4 retries on 408/429/5xx. SSL verify is disabled for shop requests.
Modern response: /api/shop/sections
Content-Type should be JSON. Error object:
{ "error": "Invalid credentials" }
Success shape:
{
"sections": [
{
"id": "base",
"title": "Games",
"items": [
{
"name": "Example Game",
"url": "/files/example.nsp",
"size": 1234567890,
"title_id": "01007EF00011E000",
"app_version": 65536,
"app_type": "base",
"icon_url": "/api/shop/icon/01007EF00011E000",
"release_date": 20240115
}
]
}
],
"success": "Welcome - shown as message of the day"
}
Section id hints used client-side: all, updates/update, dlc, installed (local), saves/save.
Item fields
| Field | Aliases | Notes |
|---|---|---|
url | - | Required. Relative paths resolved against shop base. URL fragment used as display name if name absent. |
name | - | Display title |
size | - | File size in bytes |
title_id | - | 16-char hex or decimal |
app_version | - | Version number |
app_type | - | base, upd/update/patch, dlc/addon, or 0/1/2 |
icon_url | iconUrl | If omitted and title_id set → auto /api/shop/icon/{ID} |
release_date | releaseDate, date | Integer YYYYMMDD |
After parsing, CyberFoil may prompt to include related updates and DLC for selected bases.
Legacy / Tinfoil mode
Enable shopLegacyMode in Settings (Tinfoil Mode). Changes client behaviour:
- Only GET shop root - no
/api/shop/sections - User-Agent header is empty
- Save sync API is disabled
- Legacy headers report version
20.0.2
Legacy JSON at root
At least one of:
sections- same as modern APIfiles/paths- array of objects or map of"Display Name": "path_or_url"directories- URLs fetched recursively for nested manifests
Per-entry URL keys: url, path, file, download_url, downloadUrl, or a plain string.
TINFOIL binary
Body starts with magic TINFOIL (optional BOM/whitespace prefix). May be AES-encrypted and/or zstd/zlib compressed. Decrypts to JSON. Encrypted shops without the required library blob are rejected with an error.
{
"files": {
"Game [01007EF00011E000][base]": "/nsp/game.nsp"
}
}
Request headers
HTTP Basic
Standard Authorization: Basic … when username or password is configured.
User-Agent (non-legacy shop fetch)
httpUserAgentMode | Sent value |
|---|---|
default | cyberfoil |
chrome / safari / firefox | Browser preset string |
tinfoil | (empty) |
custom | httpUserAgent from config |
Tinfoil legacy headers
When legacy auth support is available in the build, shop requests may also send:
Theme:64 zero digitsUID:derived from console CIDVersion:/Revision:from app versionLanguage:e.g.en,frHAUTH:HMAC-like token from shop URLUAUTH:token from URL + credentials
401/403 or HTML login pages are treated as auth failures. Redirect to a URL containing /login fails login.
Save sync (/api/saves/*)
Requires non-legacy shop mode. Uses the same shop URL and Basic auth. Compatible with AeroFoil-style servers.
List - GET /api/saves/list
JSON array or { "saves": [ … ] }:
{
"saves": [
{
"title_id": "01007EF00011E000",
"name": "Example Game",
"save_id": "abc123",
"note": "Before final boss",
"created_at": "2024-01-15T12:00:00Z",
"created_ts": 1705312800,
"size": 1048576,
"download_url": "/api/saves/download/01007EF00011E000/abc123.zip"
}
]
}
Field aliases: titleId, saveId, save_note, downloadUrl, etc.
Upload - POST /api/saves/upload/{TITLEID}
Multipart form:
file- zip archivetitle_id/application_id- 16-char hexnote- required from UI for bulk backup
Expect HTTP 2xx on success.
Download / delete
Download via GET on constructed or provided download_url. DELETE endpoints remove by title or by save_id.
Related client settings
| Key | Default | Effect |
|---|---|---|
shopHideInstalled | true | Hide already-installed titles in shop UI |
shopHideInstalledSection | true | Hide synthetic Installed section |
shopAllBaseOnly | true | All section shows bases only |
shopLegacyMode | false | Tinfoil compatibility mode |
shopStartGridMode | false | Open shop in grid view |
Icon cache: sdmc:/switch/CyberFoil/shop_icons/
Minimal compatible server
- Listen on HTTP port
8465(or HTTPS443). - Implement
GET /api/shop/sectionsreturning{ "sections": [ … ] }. - Serve files at URLs referenced in items; support relative paths.
- Optional:
GET /api/shop/icon/{TITLEID}, rootsuccessMOTD string. - For saves: implement
/api/saves/list|upload|download|deleteas above. - For old Tinfoil shops: root JSON or TINFOIL binary + legacy headers if encrypted.