# TypeShelf — Full Reference > TypeShelf is an iOS app plus a custom system keyboard for storing reusable text snippets ("replies") and inserting them in any app that accepts text. Replies support dynamic "magic variables" (date, time, datetime, clipboard, random choice, cursor position) and are organized with color-coded tags. The collection can be exported and imported as JSON, which lets an AI agent generate a ready-to-import library of replies for a user. This document is the complete reference: feature overview, the exact JSON import/export schema, and the full magic-variable syntax. Site: https://cannedreplies.app Guide: https://cannedreplies.app/guide/ Machine-readable summary: https://cannedreplies.app/llms.txt --- ## 1. What the app is TypeShelf saves users from typing the same things over and over. A user builds a library of "replies" (snippets) in the main app, then inserts any of them from a custom iOS keyboard while typing in Mail, Messages, Slack, browsers, or any other app. Common uses: customer-support answers, sales follow-ups, email templates and signatures, scheduling messages, addresses, and frequently used phrases. The most relevant capability for AI agents: **the entire reply collection can be imported from a JSON file** (Settings → Import Collection). An agent can therefore generate a complete, organized library of replies on a user's behalf and hand them a `.json` file to import. ## 2. Core concepts - **Reply** — one reusable snippet. Has a short `title` (the keyboard button label) and a `text` body (what gets inserted). The body can contain magic variables and line breaks. - **Magic variable** — a `%%NAME%%` placeholder inside `text` replaced with a live value at insertion time (current date, clipboard contents, a random pick, etc.). - **Tag** — a color-coded group used to organize and filter replies (e.g. "Customer Support"). A reply can belong to one or more tags. - **Collection** — the full set of replies + tags, the unit that is exported/imported. ## 3. Feature overview ### Main app - Reply library: create, edit, search, reorder (drag & drop), and bulk-manage text and image replies. - Color-coded tags for organization and filtering. - Magic variables for dynamic content. - iCloud sync across the user's Apple devices (private, in the user's own iCloud). - Usage statistics: how often each reply is used and estimated time saved. - Typing speed test (WPM, accuracy). - Data management: export to JSON, import from JSON, bulk import from CSV with preview. ### Keyboard extension - Custom iOS keyboard usable in any app that accepts text input. - Two-column reply grid, color filtering, image-to-clipboard support, haptic feedback. - Magic-variable processing at insertion time, smart cursor placement, undo of the last insertion. ### Localization - 25+ interface languages, RTL support, locale-aware date/time formatting. ## 4. JSON import/export format The import accepts two shapes. Prefer the **wrapper format** because it carries tags. ### 4.1 Wrapper format (recommended) ```json { "version": "1.2", "tags": [ { "id": 1, "title": "Customer Support", "color_index": 5 }, { "id": 2, "title": "Sales", "color_index": 2 } ], "replies": [ { "title": "Greeting", "text": "Hi there! Thanks for reaching out. How can I help you today?", "type": "text", "group": 1, "sorting_key": 0 } ] } ``` ### 4.2 Flat array format (replies only, no tags) ```json [ { "title": "Thank you", "text": "Thanks so much — really appreciate it!", "type": "text" }, { "title": "Let me check", "text": "Let me look into that and get back to you shortly." } ] ``` ### 4.3 Reply object fields | Field | Type | Required | Notes | |-------|------|----------|-------| | `title` | string | yes | Short label shown on the keyboard button. | | `text` | string | yes | The inserted content. May contain magic variables and `\n` newlines. | | `type` | `"text"` \| `"image"` | no | Defaults to `"text"`. Generate only `"text"`; image replies cannot be created from JSON. | | `group` | integer | no | A single tag's `id`. Associates the reply with one tag. | | `tag_ids` | integer[] | no | Multiple tag ids. Takes precedence over `group` when both are present. | | `sorting_key` | integer | no | Display order, ascending. Defaults to 0; use incrementing values to control order. | | `id`, `created_at`, `updated_at` | — | no | Appear in exports; optional and ignorable on import. | ### 4.4 Tag object fields | Field | Type | Required | Notes | |-------|------|----------|-------| | `id` | integer | yes | Referenced by replies' `group` / `tag_ids`. Unique within the file. | | `title` | string | yes | Tag name. On import, matched to existing tags by title (case-insensitive); created if no match. | | `color_index` | integer | yes | Tag color, 0–19 (palette below). | ### 4.5 `color_index` palette | # | Color | # | Color | # | Color | # | Color | |---|-------|---|-------|---|-------|---|-------| | 0 | maroon | 5 | blue | 10 | mint | 15 | lavender | | 1 | yellow | 6 | green | 11 | brown | 16 | gold | | 2 | purple | 7 | teal | 12 | cyan | 17 | navy | | 3 | orange | 8 | pink | 13 | lime | 18 | rose | | 4 | red | 9 | indigo | 14 | coral | 19 | gray | ### 4.6 Import behavior notes - Tags are matched to the user's existing tags by **case-insensitive title**; an unmatched tag is created. - Imported replies are appended; `sorting_key` controls ordering among them. - On export the app writes `version` (currently `1.2`) and an `exportDate`, includes only `text` replies (image replies are excluded), and includes only the tags referenced by exported replies. ## 5. Magic variables (complete syntax) Placeholders written `%%NAME%%` inside a reply's `text`, replaced with live values at insertion. **Case-insensitive.** | Variable | Result | |----------|--------| | `%%DATE%%` | Current date, device locale, medium style. | | `%%TIME%%` | Current time as `HH:mm`. | | `%%DATETIME%%` | Current date + time (locale medium date, short time). | | `%%CLIPBOARD%%` | Current clipboard text. Removed if the clipboard is empty/inaccessible. | | `%%CURSOR%%` | Marks where the cursor lands after insertion. Inserts nothing. | | `%%RANDOM:a\|b\|c%%` | One option picked at random from the `\|`-separated list. | ### 5.1 Custom date formats `%%DATE:format%%` where `format` is a Unicode/`DateFormatter` pattern. - `%%DATE:yyyy-MM-dd%%` → `2026-06-01` - `%%DATE:EEEE%%` → `Monday` - `%%DATE:MMM d%%` → `Jun 1` - `%%DATE:d MMMM yyyy%%` → `1 June 2026` `%%TIME%%` and `%%DATETIME%%` do **not** accept custom formats — only offsets. ### 5.2 Offsets Add a **single space**, then a sign (`+`/`-`) immediately followed by a digit, then one or more unit chunks. Units (case-insensitive): `W` weeks, `D` days, `H` hours, `M` minutes, `S` seconds. Combine freely. - `%%DATE +1d%%` → tomorrow - `%%DATE +1w%%` → one week ahead - `%%DATE -3d%%` → three days ago - `%%TIME +2h30m%%` → 2h30m from now - `%%DATETIME -1d%%` → yesterday, date + time - `%%DATE:yyyy-MM-dd +10d%%` → ten days ahead in ISO format ### 5.3 Syntax rules (generate exactly this way) - Offsets are space-separated and start with a sign + digit: `%%DATE +1d%%` ✅. A colon means *format*, never an offset: `%%DATE:+1d%%` ❌. - Natural-language dates are not parsed: `%%DATE:next Monday%%` ❌. - `%%TIME%%` has no format variant: `%%TIME:2pm%%` ❌ (would be left as literal text). - `%%RANDOM%%` requires the colon and at least one option: `%%RANDOM:yes|no%%`. ## 6. Full ready-to-import example ```json { "version": "1.2", "tags": [ { "id": 1, "title": "Customer Support", "color_index": 5 }, { "id": 2, "title": "Sales", "color_index": 2 }, { "id": 3, "title": "Scheduling", "color_index": 6 } ], "replies": [ { "title": "Ticket received", "text": "Thanks for contacting us. Your request was received and assigned ticket #%%RANDOM:4820|5913|6047%%. We'll reply within 24 hours.", "type": "text", "group": 1, "sorting_key": 0 }, { "title": "Escalation", "text": "I've escalated your case to our senior team. Expect an update by %%DATE +1d%%. Thanks for your patience.", "type": "text", "group": 1, "sorting_key": 1 }, { "title": "Need more info", "text": "Thanks for the details. To resolve this faster, could you share:\n\n1. Your account email\n2. A screenshot of the issue\n3. Your device/browser\n\nThat lets us investigate right away.", "type": "text", "group": 1, "sorting_key": 2 }, { "title": "Pricing overview", "text": "Here's a quick breakdown:\n\n- Starter: $9/mo (up to 3 users)\n- Pro: $29/mo (up to 15 users)\n- Enterprise: custom\n\nWant a demo to see which fits best?", "type": "text", "group": 2, "sorting_key": 3 }, { "title": "Propose meeting", "text": "Would %%DATE:EEEE, MMM d +2d%% at 2:00 PM work for a quick call? %%CURSOR%%", "type": "text", "group": 3, "sorting_key": 4 } ] } ``` ## 7. Guidance for AI agents generating replies - Output a **single valid JSON document** in the wrapper format — no surrounding prose inside the file. - Use `type: "text"` only; image replies are not importable from JSON. - Keep `title` short (the keyboard button label); put the full content in `text`. - Use `\n` for line breaks. - Group related replies with tags via `group` (single) or `tag_ids` (multiple); pick sensible `color_index` values. - Use incrementing `sorting_key` values for ordering. - Use magic variables only where dynamic content genuinely helps, and follow the exact syntax in section 5. ## 8. Links - Home: https://cannedreplies.app/ - Guide: https://cannedreplies.app/guide/ - Privacy Policy: https://cannedreplies.app/privacy/ - Terms & Conditions: https://cannedreplies.app/terms/ - Concise AI summary: https://cannedreplies.app/llms.txt