Using the built-in Notes URL scheme

Tested on macOS Ventura.


I just built a script that uses the built-in Apple Notes URL scheme, so I can replace the default script for getting Apple Notes URLs.

Here’s how the script works:

  1. It gets the selected note’s ID and title.
  2. It splits up the ID at each / character which is a Core Data URL that looks like this: “x-coredata://A3B24705-E414-46EB-AF67-6E70FCCA3CE3/ICNote/p2405”.
  3. We have the last part, p2405 in this case. The script removes the p from the string, so we got the internal database ID of the selected note.
  4. We can run a quick SQLite query to get the real identifier from the internal database of Notes at ~/Library/Group Containers/group.com.apple.notes/NoteStore.sqlite.
    • It is stored in the ZICCLOUDSYNCINGOBJECT as the ZIDENTIFIER column.
    • Hook needs to have full disk access to make this work.
  5. Having the identifier from the database, we can return the final URL scheme in the form of notes://showNote?identifier=EAE6848C-3556-407B-9CA0-A9C9E7CC26BD.

This URL sadly works only on macOS. iOS uses the mobilenotes protocol, so the same URL would be mobilenotes://showNote?identifier=EAE6848C-3556-407B-9CA0-A9C9E7CC26BD.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

tell application "Notes"
	set theNoteID to «class seld» of (selection as record)
	set theNoteName to name of note id theNoteID
end tell

set oldDelimiters to text item delimiters
set text item delimiters to "/"
set theTextItems to text items of theNoteID
set text item delimiters to oldDelimiters
set theIDPart to last item of theTextItems
set theIDPartCharacters to characters of theIDPart
set theIDPartCharacters to items 2 through -1 of theIDPartCharacters
set {oldDelimiters, text item delimiters} to {text item delimiters, ""}
set theNoteID to theIDPartCharacters as text
set text item delimiters to oldDelimiters
set theNoteIdentifier to do shell script "/usr/bin/sqlite3 ~/Library/Group\\ Containers/group.com.apple.notes/NoteStore.sqlite \"SELECT ZIDENTIFIER from ZICCLOUDSYNCINGOBJECT WHERE Z_PK = '" & theNoteID & "'\""
set theNoteURL to "notes://showNote?identifier=" & theNoteIdentifier

return "[" & theNoteName & "](" & theNoteURL & ")"

To replace the built-in script, just open Hook settings, go to Scripts, find Notes on the left and replace the Get Address script.


I found the inner workings of the Apple Notes URL scheme here.

7 Likes

Dude! This awesome! Thank you so much!

1 Like

Thanks much for this, @zsbenke lovely input. thanks to your insights, we currently intend to extend the hook://notes scheme so that it includes the ID. That way one can get the best of both worlds and use the links on Mac, iPhone and iPad. (who knows, maybe Apple TV one day :wink: ).

Also by using the notes:// scheme, there will be extra robustness as we will also include the datestamp. So if the trick you describe ceases to work (Apple could make their DB opaque), then the links will still work.

I’m hoping we can get this into Hookmark 5.1. We have quite a bit in the pipeline as always (5.1, 5.2, Hookmark Mobile release, and beyond …)

1 Like

I’ve wanted to be able to open folders in notes by AppleScript for a while…so I came up with this based on your code….haven’t integrated with Hookmark yet….but I’d love to see this as an option.

Also, the way I get the object properties may seem convoluted….but I did a bunch of testing and grabbing the properties first seems to prevent the loading and reloading of a mutable object on every access and really speeds things up.

tell application "Notes"
	set selNoteProps to properties of (item 1 of (selection as list))
	set selFolderProps to properties of container of selNoteProps
	set selNote to {name:name of selNoteProps, id:id of selNoteProps}
	set selFolder to {name:name of selFolderProps, id:id of selFolderProps}
	
	display dialog "Link to the note \"" & name of selNote & "\" or the folder \"" & name of selFolder & "?\"" with title "Choose your link" buttons {"Note", "Folder"}
	
	set theResult to the button returned of the result
	
	if theResult is "Note" then
		set theUUID to getUUID(id of selNote) of me
		set theURL to "applenotes:note/" & theUUID
	else
		set theUUID to getUUID(id of selFolder) of me
		set theURL to "applenotes:folder/" & theUUID
	end if
	
	set the clipboard to theURL	
end tell

on getUUID(theNoteID)
	set oldDelimiters to text item delimiters
	set text item delimiters to "/"
	set theTextItems to text items of theNoteID
	set text item delimiters to oldDelimiters
	set theIDPart to last item of theTextItems
	set theIDPartCharacters to characters of theIDPart
	set theIDPartCharacters to items 2 through -1 of theIDPartCharacters
	set {oldDelimiters, text item delimiters} to {text item delimiters, ""}
	set theNoteID to theIDPartCharacters as text
	set text item delimiters to oldDelimiters
	set theNoteIdentifier to do shell script "/usr/bin/sqlite3 ~/Library/Group\\ Containers/group.com.apple.notes/NoteStore.sqlite \"SELECT ZIDENTIFIER from ZICCLOUDSYNCINGOBJECT WHERE Z_PK = '" & theNoteID & "'\""
	return theNoteIdentifier
end getUUID