--- name: apple-notes-data-handling description: 'Handle Apple Notes data formats: HTML body, attachments, and rich content. Trigger: "apple notes data handling". ' allowed-tools: Read, Write, Edit, Bash(osascript:*), Grep version: 1.6.0 license: MIT author: Jeremy Longshore tags: - saas - macos - apple-notes - automation compatibility: Designed for Claude Code --- # Apple Notes Data Handling ## Overview Apple Notes stores note content as a restricted subset of HTML internally. The `body()` property in JXA returns this HTML, which includes `
`, `

`-`

`, ``, ``, `
    `, `
  • `, and Apple-specific classes for checklists and tables. Attachments (images, PDFs, sketches, scans) are embedded as `` or object references but cannot be directly extracted via JXA — they require the `attachments()` property. Understanding these data formats is essential for building reliable import, export, and backup pipelines. ## Prerequisites - Written authorization for the accounts, folders, and note categories to be exported or transformed. - An encrypted destination outside a shared directory, a retention limit, and a tested restore path. - A conversion test corpus containing only synthetic notes; do not use the examples as proof that Apple Notes HTML is a stable public format. ## Instructions 1. Scope reads to named folders and minimize collected fields before invoking `osascript`. 2. Write exports to a pre-created, owner-only directory and validate permissions before any data is emitted. 3. Treat HTML and attachment metadata as untrusted content: sanitize before rendering, and never execute embedded links or markup. 4. Hash or redact identifiers in operational logs; record only counts and completion state. ## Note Body HTML Format ```html

    Title


    Paragraph text here.
    Bold text and italic text

    • List item 1
    • List item 2
    • Completed item
    • Incomplete item
    Cell 1Cell 2
    #project #important
    ``` ## Export All Notes to JSON ```bash #!/bin/bash # Full export with metadata — useful for backups and migration osascript -l JavaScript -e ' const Notes = Application("Notes"); const results = Notes.defaultAccount.notes().map(n => ({ id: n.id(), title: n.name(), body: n.body(), plaintext: n.plaintext(), folder: n.container().name(), created: n.creationDate().toISOString(), modified: n.modificationDate().toISOString(), attachmentCount: n.attachments().length, })); JSON.stringify(results, null, 2); ' > "$HOME/notes-export-$(date +%Y%m%d).json" ``` ## HTML to Markdown Converter ```typescript // src/data/html-to-markdown.ts function notesHtmlToMarkdown(html: string): string { return html .replace(/

    (.*?)<\/h1>/g, "# $1") .replace(/

    (.*?)<\/h2>/g, "## $1") .replace(/

    (.*?)<\/h3>/g, "### $1") .replace(/(.*?)<\/b>/g, "**$1**") .replace(/(.*?)<\/strong>/g, "**$1**") .replace(/(.*?)<\/i>/g, "*$1*") .replace(/(.*?)<\/em>/g, "*$1*") .replace(/
  • (.*?)<\/li>/g, "- [x] $1") .replace(/
  • (.*?)<\/li>/g, "- [ ] $1") .replace(//g, "\n") .replace(/
    /g, "").replace(/<\/div>/g, "\n") .replace(/<[^>]*>/g, "") .replace(/\n{3,}/g, "\n\n") .trim(); } ``` ## Attachment Handling ```bash # List all notes with attachments and their counts osascript -l JavaScript -e ' const Notes = Application("Notes"); Notes.defaultAccount.notes() .filter(n => n.attachments().length > 0) .map(n => n.name() + ": " + n.attachments().length + " attachments (" + n.attachments().map(a => a.name()).join(", ") + ")") .join("\n"); ' # Note: JXA cannot directly save attachment binary data. # For full attachment export, use Shortcuts: # shortcuts run "Export Note Attachments" --input-type text --input "Note Title" ``` ## Error Handling | Issue | Cause | Solution | |-------|-------|----------| | `body()` returns empty string | Note contains only attachments (no text) | Check `attachments().length`; use `plaintext()` as fallback | | HTML contains unexpected tags | Note created on iOS with unsupported formatting | Strip unknown tags; keep only known Apple Notes subset | | `plaintext()` truncated | Very large note body | Export via `body()` HTML instead; convert after | | Checklist state lost in export | Custom class not preserved in conversion | Map `class="done"` to `[x]` before stripping HTML | | Attachment names are generic | Auto-generated names like `Image.png` | Use note title + index for meaningful filenames | ## Output An export produces a scoped, encrypted artifact plus a separate receipt containing the folder scope, record count, checksum, and expiration—not note bodies, titles, or attachment names. A conversion produces Markdown only after the source has been sanitized and its unsupported constructs have been recorded for review. ## Examples For a migration rehearsal, export a synthetic test folder to an owner-only staging directory, convert it, validate the expected count and checksum, then securely remove the rehearsal artifact under the retention policy. For a live backup, use a job-owned encrypted volume and report only `completed: 42 records` to monitoring. ## Resources - [Mac Automation Scripting Guide](https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/) - [JXA Cookbook](https://github.com/JXA-Cookbook/JXA-Cookbook) - [Apple Notes File Format (reverse-engineered)](https://ciofecaforensics.com/2020/08/05/apple-notes-format/) ## Next Steps For migrating between note platforms, see `apple-notes-migration-deep-dive`. For backup automation, see `apple-notes-deploy-integration`.