1. How the ZIP is structured
A card ZIP bundles a game name and attribute definitions, card information, and images. If you only want to use someone else's ZIP, go to the import steps in Getting started.
my-cards/
├── game.json
├── cards/
│ └── cards.json
└── images/
├── DMO-R05.jpg
├── DMO-R12.jpg
└── DMO-R12_p1.jpg2. Create game.json
Save it as UTF-8 text. Make sure the extension does not end up as .json.txt. Keep format and version as shown; set the name and attributes to fit your game.
{
"format": "tcgt-cards",
"version": 1,
"game": {
"name": "Demo Card Game",
"fields": [
{ "key": "color", "label": "Color", "type": "select", "options": ["Red", "Blue", "Green", "Yellow"] },
{ "key": "kind", "label": "Type", "type": "select", "options": ["Unit", "Spell", "Field"] },
{ "key": "cost", "label": "Cost", "type": "number" },
{ "key": "power", "label": "Power", "type": "number" }
]
}
}key is the attribute's identifier and label is its display name. Put the choices for a select in options. Do not add the set number or card name to fields.
3. Create the card JSON
Save an array of cards in cards/cards.json. code and name are required. The first entry in images is the main art; any after it are alternate art.
[
{
"code": "DMO-R05",
"name": "Hearthtail Cub",
"attributes": { "color": "Red", "kind": "Unit", "cost": 1, "power": 1000 },
"images": ["images/DMO-R05.jpg"]
},
{
"code": "DMO-R12",
"name": "Moltencrown Beast",
"attributes": { "color": "Red", "kind": "Unit", "cost": 6, "power": 9000 },
"images": ["images/DMO-R12.jpg", "images/DMO-R12_p1.jpg"]
}
]| Attribute type | Example value |
|---|---|
| text | "Card text" |
| number | 1000 |
| select | "Red" |
| multiselect | ["Red", "Blue"] |
| boolean | true |
4. Match the image paths
- Save your images in images/. You do not write image URLs.
- File names are easiest to handle with ASCII letters, digits and - _ . only.
- Paths are relative to the inside of the ZIP. Do not use a leading / or ./, or .. to go up a level.
- Match upper and lower case and the extension exactly.
- Remove the paths of any alternate art images you do not provide.
Cards with the same set number are treated as the same card. Keeping different art in one card's images makes them easier to manage.
5. Compress into a ZIP
Put game.json, cards/ and images/ in the same folder, and compress that folder in ZIP format. One outer folder level is stripped when reading, but do not wrap it in several layers.
Using Python
With Python 3 available, save this as make_zip.py next to my-cards and run python3 make_zip.py. It does not overwrite an existing ZIP.
from pathlib import Path
import json
import zipfile
source = Path(__file__).resolve().parent / "my-cards"
output = source.parent / "my-cards.zip"
assert (source / "game.json").is_file(), "game.json is missing"
assert list((source / "cards").glob("*.json")), "No card JSON found"
files = sorted(p for p in source.rglob("*") if p.is_file())
assert len(files) < 65535, "Too many files"
for path in [source / "game.json", *sorted((source / "cards").glob("*.json"))]:
json.loads(path.read_text(encoding="utf-8"))
with zipfile.ZipFile(output, "x", compression=zipfile.ZIP_STORED,
allowZip64=False) as archive:
for path in files:
archive.write(path, path.relative_to(source).as_posix())
print(output)6. Import into the app and check
- Save the ZIP to your device or a cloud location you can open.
- Open it with "+ Add" → "Choose a ZIP" in the card list. On Android, you can also pick the extracted folder with "Choose a folder (unzipped)" (you cannot pick the Downloads folder itself, so choose a folder inside it).
- Check the counts of cards and images, and any warnings. In this example there are 2 cards and 3 images.
- Review and save the drafts, then check names, set numbers, attributes and alternate art.
Importing into a new test game first lets you check without adding to your existing cards. If you import it as a new game, set up the board and decks for playing separately.
7. Add more cards, or use AI
You can split cards into several files, such as cards/set1.json and cards/set2.json. Each file is an array of cards.
Give the AI the spec from the app's "Copy spec for AI", along with your card information and a list of the image file names that actually exist. Ask it to list missing information rather than guess, and for large sets, ask for a generation script and steps to run it. Check the import with a few cards before adding more.
From cards you have already registered, you can also make a ZIP in the same format with "Export" in the card list. Cards without a set number ask you to confirm numbering.
If it cannot be read
| Problem | What to check |
|---|---|
| game.json is missing | Folder level, file name, extension |
| The JSON cannot be read | Commas, quotes, brackets, UTF-8 |
| Images are not found | The actual files and relative paths, upper and lower case |
| Attributes come out empty | key, value type, match with options |
| Fewer cards than expected | Whether some were merged under the same set number |
| Nothing new after re-importing | Cards with the same set number and the same art are skipped |