Talk to a Semurg node over plain HTTP: addresses, the token, answers and errors
12 October 2026 · Semurg · 4 min read · Markdown
Verified: every command below ran on a fresh node of r11.78.20261012.bc0d94f (how)
Every Semurg node answers plain HTTP. There is no client library you must use and no account to create: a node, its address and its API token are the whole setup. This post covers the rules every REST call follows. The other connections (the MCP endpoint, the WebSocket, the client libraries) are covered in their own posts and answer the same questions the same way, because each capability has one implementation behind every door.
The address
A node listens on http://localhost:4000 on the machine it runs on, and only there, until you decide otherwise. The routes fall into two families:
- Public routes that say whether the node is up and which release it runs:
/api/healthand/api/version. They need no token. - Everything else: your data, memory, queries, the node's status, on routes whose path begins with v1, such as
/v1/query. Every one of them needs the token.
The full list of routes, with every operation, is in the API reference, which is generated from the code of the published release.
The token
The installer prints the node's API token when it finishes, and keeps it on the node in /etc/semurg/semurg.env; a Docker node prints it in its log and keeps it in the container. Send it with every call to a v1 route, in either header:
Authorization: Bearer <token>x-api-key: <token>
Without it, such a call is refused with 401; with it, the same call goes through:
export SEMURG_API_TOKEN=<your token>
curl -si http://localhost:4000/v1/whoami | head -1
curl -si -H "Authorization: Bearer $SEMURG_API_TOKEN" http://localhost:4000/v1/whoami | head -1Expected: the first call answers HTTP 401, the second HTTP 200
A refusal tells you what to do
A refused call answers JSON with "ok": false, an error that says what was wrong and, where there is more to say, a detail. The 401 goes further, because the usual question is not "which header?" but "where is my token?": it says what your request carried, how to send the token, and where to find it.
export SEMURG_API_TOKEN=<your token>
curl -s http://localhost:4000/v1/whoamiExpected: a refusal that says what the request carried, how to send the token, and where to find it
If your shell variable is empty, the 401 says that too: an empty token is the most common mistake, and the answer names it rather than pretending the header was missing. On a node exposed to other machines, the answer no longer says where the token file is; it tells a stranger to ask the operator.
The other statuses follow the same pattern: 422 for a call whose body is wrong, naming the field; 404 for something that is not there; 409 for a request that cannot be completed as asked; 413 for a call past a size bound; 501 for a capability this release does not carry; 503 for something that could not be read or done right now, safe to retry.
An answer
A successful call answers JSON with "ok": true and the fields of that operation. With the token, whoami says who the node thinks you are:
export SEMURG_API_TOKEN=<your token>
curl -s -H "Authorization: Bearer $SEMURG_API_TOKEN" http://localhost:4000/v1/whoamiExpected: an authenticated answer naming your scope of access
One route for every kind of data
Most reads and writes of data go through one route, POST /v1/query, with an op naming the operation and the operation's own fields beside it. An unknown op is refused with the nearest real one suggested and the full list of operations, so a typo answers with its own fix. The memory calls have their own routes, such as /v1/memory/recall, and the node's own state is at /v1/status and on the cluster routes, such as /v1/cluster/util.
Behind your own proxy
To reach a node from other machines, put it behind your own TLS proxy and use the proxy's https address; every path stays the same. A node without a token that is reachable from other machines serves callers there only a small public sandbox, never your data, so set a token before you expose a node that holds anything private.
Next
- Look after your node: health, version, status and utilisation.
- Give your agent memory over MCP.
- Objects, streams, search and places: the data operations on
/v1/query.
How these commands were checked
Our test farm ran each of the 3 command blocks marked runnable, exactly as written, with the answer checked against the expectation printed under it, against a fresh node of the published release r11.78.20261012.bc0d94f. A post is published only when every one of them passed there.