MojoPad has a local API, and anything running as you on this Mac can use it. A Shortcut that files a note into today’s journal. A shell script that pulls every page of a kind into a report. A Keyboard Maestro macro that searches your wiki by meaning from any app. Another program you wrote, reading a page by name. None of it needs an AI, an account, or the internet.
Not to be confused with the mojo API, which is a different thing with
a similar name. That one runs inside a page, in scriptlets and plugins, and is
described under Scripting. This one is spoken to from outside MojoPad, over a
connection on your own machine. If you are writing something that is not a MojoPad page, this
is the one you want.
The same door an assistant uses is open to anything else you run. When
Let agents reach the wiki that’s open right now is on, MojoPad answers a small
set of requests on your own machine — so a shell script, a Shortcut, Keyboard Maestro, or
any app you write can read from and add to the wiki you have open. Nothing on the internet
can: the door is on 127.0.0.1, it needs a key, and it refuses anything that
looks like it came from a web page.
Finding it. Each time MojoPad starts it writes
~/.mojopad/bridge.json — the port it is listening on and a fresh key,
readable only by you. A script reads that file and puts the key in an
Authorization: Bearer header. The key changes every launch, so nothing can
hold one from last week.
PORT=$(python3 -c "import json;print(json.load(open('$HOME/.mojopad/bridge.json'))['port'])")
TOK=$(python3 -c "import json;print(json.load(open('$HOME/.mojopad/bridge.json'))['token'])")
curl -s -H "Authorization: Bearer $TOK" http://127.0.0.1:$PORT/info
Everything below is POST with a JSON body, except /info.
ref means a page’s name or its id.
| Ask | Body | Answers with |
|---|---|---|
/info | — | the app version, the contract version, and which wiki is open |
/pages | {"limit": 200, "offset": 0} | every page’s name, id, type and when it changed |
/page | {"ref": "Lend Lease"} | that page’s words, tags and other names |
/props | {"ref": "Lend Lease"} | its typed properties, each with where its value came from |
/search | {"query": "…", "k": 6} | pages ranked by meaning |
/ask | {"question": "…"} | an answer from your local model, with its sources |
/add ✎ | {"title": "…", "content": "…"} | makes a page |
/append ✎ | {"ref": "…", "text": "…"} | adds to the end of a page |
/today ✎ | {"text": "…"} | adds to today’s note |
/move ✎ | {"ref": "…", "folder": "Reading/Papers"} | moves a page that already exists into a folder |
/folders | {} | the folders in the open wiki — their names, their nesting, and the text to file into one by. Never which pages are in them. |
/identify ✎ | {"ref": "…", "value": "…"} | records a paper’s number on a page as a draft, asked about every time |
The five marked ✎ change your wiki, and they are refused unless Allow agents to modify wikis you already have is also on. Reading is never affected by that switch.
Moving is the narrowest of them. It changes which folder holds a page and not one word of the page — it does not read what is on it, and it cannot make a page or remove one. A name that answers to nothing is refused rather than turned into a new page, so an assistant that misremembers a title gets told so. The question you are asked names the page by the name your wiki spells it — an assistant often holds a paper’s number rather than its title — and the folder the page will really land in.
And it hands back nothing about where the page was. Nothing an assistant can ask over this connection returns which folder holds a page, and a move is not a way around that: it answers with where the page is now and no more. Which papers you keep under which folder is the shape of your thinking, and it is not something a run of moves should be able to read off its own replies.
The way back covers the whole run. An assistant sorting a shelf moves in a loop, so the moves that arrive together are collected and one offer to put all of them back is raised when they stop — every page to the position it held, along with any folder the run invented to hold them and nothing has been put in since. It carries no timer, because you were not the one at the keyboard.
Event: or Plugin: page from outside — their words are
the code, and an Event: Open page runs every time the document is opened.The contract has its own version. /info reports api
alongside the app version. The app version moves every release; the api number
moves only when the shape of a request or an answer changes, so a script can tell the
difference between MojoPad being newer and MojoPad being different.