For tinkerers

The .flickdeck file format

Format version 1 · Last updated July 7, 2026

The idea

Already have your material in a spreadsheet, a script, or another app? You can build a Flickdeck import file yourself. A .flickdeck file is plain JSON, no compression, no tricks. Make one, AirDrop it to your iPhone (or open it from the Files app), and it imports like any shared deck.

Download sample.flickdeck to see a working file, or copy the example below. The app's own test suite imports this exact document, so if it's on this page, it works.

A complete example

One deck, two text cards, never studied:

{
  "version": 1,
  "exportedAt": "2026-07-07T00:00:00Z",
  "decks": [
    {
      "id": "7E6C2E8A-0000-4000-8000-000000000001",
      "name": "Spanish Verbs",
      "createdAt": "2026-07-07T00:00:00Z",
      "goalMode": { "keepFresh": {} },
      "notificationsEnabled": false,
      "archived": false,
      "newCardsPerDay": 15,
      "deadlinePromptShown": false
    }
  ],
  "cards": [
    {
      "id": "7E6C2E8A-0000-4000-8000-000000000101",
      "deckID": "7E6C2E8A-0000-4000-8000-000000000001",
      "front": { "text": "hablar" },
      "back": { "text": "to speak" },
      "createdAt": "2026-07-07T00:00:00Z",
      "suspended": false,
      "scheduling": {
        "state": "new",
        "stability": 0,
        "difficulty": 0,
        "lapseCount": 0,
        "reviewCount": 0
      }
    },
    {
      "id": "7E6C2E8A-0000-4000-8000-000000000102",
      "deckID": "7E6C2E8A-0000-4000-8000-000000000001",
      "front": { "text": "comer" },
      "back": { "text": "to eat" },
      "createdAt": "2026-07-07T00:00:00Z",
      "suspended": false,
      "scheduling": {
        "state": "new",
        "stability": 0,
        "difficulty": 0,
        "lapseCount": 0,
        "reviewCount": 0
      }
    }
  ],
  "reviewLogs": [],
  "media": {}
}

Save it with a .flickdeck extension. The filename itself doesn't matter; the deck's name field is what shows in the app.

Field notes

Every key shown in the example is required. The importer is strict: a missing key fails the whole file, so start from the example and edit rather than writing from scratch.

  • ids are UUIDs. Generate real ones (any UUID tool or library); the sequential ones in the example are just readable. Every card's deckID must match the deck's id.
  • Dates are ISO-8601 UTC, like 2026-07-07T00:00:00Z.
  • goalMode: use { "keepFresh": {} }. Deadline goals are better set in the app, where the date picker lives.
  • scheduling: for cards that haven't been studied, copy the block from the example exactly. The zeros mean "new card"; the app's scheduler takes over from the first review.
  • front / back each take a text string. At least one side should have something on it.
  • version is 1. Files claiming a newer version are refused.

From a CSV

A small Python script that turns a two-column words.csv (front, back) into an importable deck:

import csv, json, uuid, datetime

deck_id = str(uuid.uuid4()).upper()
now = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
fresh = {"state": "new", "stability": 0, "difficulty": 0,
         "lapseCount": 0, "reviewCount": 0}

cards = []
with open("words.csv", newline="", encoding="utf-8") as f:
    for front, back in csv.reader(f):
        cards.append({
            "id": str(uuid.uuid4()).upper(),
            "deckID": deck_id,
            "front": {"text": front},
            "back": {"text": back},
            "createdAt": now,
            "suspended": False,
            "scheduling": fresh,
        })

deck = {"id": deck_id, "name": "My Deck", "createdAt": now,
        "goalMode": {"keepFresh": {}}, "notificationsEnabled": False,
        "archived": False, "newCardsPerDay": 15, "deadlinePromptShown": False}

with open("My Deck.flickdeck", "w", encoding="utf-8") as f:
    json.dump({"version": 1, "exportedAt": now, "decks": [deck],
               "cards": cards, "reviewLogs": [], "media": {}}, f,
              ensure_ascii=False, indent=2)

Run it next to your CSV, then get My Deck.flickdeck onto your iPhone however you like: AirDrop, Messages, iCloud Drive. Tap it and Flickdeck opens.

Optional extras

  • Images and audio. Add "imagePath" or "audioPath" to a face (a filename like "hablar.jpg"), then put the file's bytes in media, base64-encoded, keyed by that same filename: "media": { "hablar.jpg": "<base64>" }. JPEG/PNG for images, M4A for audio.
  • Two-way cards. A pair is two card entries with swapped faces: give each a "siblingID" pointing at the other's id, and set "isReverseSibling": true on the reversed one. Easier route: import one-way cards and flip the two-way switch in the app.
  • reviewLogs exists so the app's own backups can carry study history. For a hand-built file, leave it [].

Gotchas

  • Importing the same ids twice updates in place. That's how the app's own backups restore. Keep your generated ids if you want re-imports to update the same deck; generate fresh ones to create a separate deck.
  • Validate your JSON before sending it over. A stray comma is the most common failure, and the app can only tell you the file didn't parse.
  • The format is versioned, and version 1 files will keep importing in future app versions. If the format ever changes, this page changes with it.

Built something on top of this — a converter, an exporter from another tool? We'd like to see it: support@flickdeck.page.