This problem is now resolved for me. In case anyone else has the same symptoms, here’s what I found / did.
I’m on a Mac. I’m running Roon core locally.
I had installed the roon-extension-http-api into /Users/me/roon/roon-extension-http-api/
After firing up node to run the extension, a file called config.json appeared in this directory, which (confusingly to me, at least) seems to be written by Roon itself, not by the extension. It contains the magic token.
Subsequently restarting the extension and/or Roon worked fine - the token persisted and the extension was remembered by Roon.
In order to launch the extension automatically on boot, I had created a Mac launch agent. This didn’t “remember” extension authorisations at first, because (it transpired) Roon couldn’t find the config.json file. Adding a WorkingDirectory key to the launch agent plist fixes the issue.
Now sending GET requests to localhost:3001 allows me to control Roon. I am using Alfred to bind hotkeys to curl requests for volume up, volume down and play/pause, which now work globally (ie, no matter what app has focus).
My plist for the service is below.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
Thanks again to Ben for pointing me in the right direction.