Create a user.
Session-only, admin role. There is no self-registration — this
instance is invite-only by design. The account is created with the user
role; promote it with PATCH /admin/users/{id}.
Request body required
application/json
object
display_name string | null
password string | null
Optional. Without one the account cannot sign in until an admin sets a
password with PATCH /admin/users/{id}, but its token works at once.
Responses
application/json
object
user_id integer · int64 required
token string required
A submit token for the new account. Shown once, never again.
400 Malformed or rejected input.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
403 Authenticated, but not permitted. Either the token lacks the required
scope, or the endpoint needs the admin role. Deliberately not a 401 —
re-authenticating will not help.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 403,
"error": "This token does not have the 'write' scope"
}
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
Mint a token.
Session-only. The secret is in this response and nowhere else.
Name it per device — that is what makes revocation meaningful when a
phone is lost. default_chain_id is rung 3 of the chain ladder: one
token per app makes attribution zero-configuration on the client side.
Request body required
application/json
object
user_id integer | null · int64
Whose token. Defaults to the caller; anyone else's is admin-only, since
minting a token for another account is taking it over.
name string required
Name it per device — that is what makes revocation meaningful when a
phone is lost.
scopes string
Space- or comma-separated: submit, read, write, or all. None
implies another.
default_chain_id integer | null · int64
The chain every listen submitted with this token is attributed to,
unless the listen carries better evidence — rung 3 of the chain ladder.
One token per app makes attribution zero-configuration on the client
side. Must be one of the owner's chains.
imports_are_live boolean
Whether this client's listen_type: "import" submissions are live
listens rather than a backfill. Some players (fooyin) label every
scrobble import; without this their listens are stored but never
forwarded to Last.fm / ListenBrainz. Even when set, only listens from
the last 24h are forwarded, so a genuine history import from the same
client stays local.
Responses
application/json
object
token string required
The secret. In this response and nowhere else.
user_id integer · int64 required
400 An empty name, or a chain that is not the owner's.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
403 A token for another user, and the caller is not an admin.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
POST /admin/tokens/{id}/rotate # Session
Re-issue a token in place.
Session-only. Same id, name, scopes and chain; new secret, returned once.
Parameters
id path required
The token's id, not its secret.
Responses
application/json
object
token string required
The new secret, returned once. The old one stops working now.
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
404 No such resource, or it belongs to another user.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
Engine, sink and source health for this instance.
Session-only, admin role — instance-wide, and the source rows are not scoped
to the caller. One request rather than several because the Operations screen
polls it while open.
tick.behind is the number that matters: a tick taking longer than its own
interval_secs means source polling and the deck are that late.
sinks[].skipping_for is set while a service's breaker is open and it is
being skipped outright. Neither credentials nor source URLs are returned.
Responses
application/json
object
Engine, sink and source health. Times are Unix seconds.
uptime_secs integer · int64 required
tick object required
interval_secs integer · int64 required
The configured interval — the denominator for everything else here.
ticks integer · int64 required
last_started integer | null · int64
last_duration_ms integer · int64 required
longest_ms integer · int64 required
Worst tick since the process started.
overruns integer · int64 required
Ticks that ran longer than the configured interval.
deferred_listens integer · int64 required
Listens the flush left for the next tick because it ran out of budget.
Nonzero means forwarding is behind.
behind boolean required
The last tick took longer than its own interval: source polling and
the deck are that late. The number that matters.
sinks array required
each item object
One forwarding service's circuit breaker.
failures integer · int32 required
Consecutive failures since the last success.
total_failures integer · int64 required
Since the process started.
total_successes integer · int64 required
last_ok integer | null · int64
last_failure integer | null · int64
last_error string | null
The most recent error, verbatim.
skipping_for integer | null · int64
Seconds left while the breaker is open and the service is being
skipped outright.
sources array required
Every user's sources — instance-wide, with no credentials or URLs.
each item object
One source's liveness, with nothing secret in it.
id integer · int64 required
user_id integer · int64 required
poll_interval_secs integer | null · int64
last_polled_at integer | null · int64
logs_enabled boolean required
File logging is on, so /admin/logs has something to show.
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
403 Authenticated, but not permitted. Either the token lacks the required
scope, or the endpoint needs the admin role. Deliberately not a 401 —
re-authenticating will not help.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 403,
"error": "This token does not have the 'write' scope"
}
The tail of a log file, filtered.
Session-only, admin role. A log carries usernames, source labels and request
paths, so this is deliberately unreachable with an API token — the endpoint
does not accept one, so scopes never enter into it.
level means "this and worse", so warn includes errors. matched is what
the filter found against shown, so a capped tail cannot be mistaken for
the whole answer.
Parameters
lines query
Newest N matching lines. Default 300, at most 2000.
level query
error | warn | info | debug, meaning "this and worse".
q query
Substring filter, applied after the level.
file query
One of the names from /admin/logs/files; anything else is a 404.
Defaults to the one being written.
Responses
application/json
object
The newest matching lines of one log file.
files array required
Every file there is, newest first.
matched integer required
How many lines the filter found…
shown integer required
…against how many are shown.
truncated boolean required
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
403 Authenticated, but not permitted. Either the token lacks the required
scope, or the endpoint needs the admin role. Deliberately not a 401 —
re-authenticating will not help.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 403,
"error": "This token does not have the 'write' scope"
}
404 File logging is off, or no such file.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
Drain and stop, so a supervisor restarts the process.
Session-only, admin role. In-flight requests drain and the engine finishes
its current tick before the process exits, so this cannot cut a write in
half — it is gentler than the kill an operator reaches for otherwise.
The process exits non-zero (75, EX_TEMPFAIL) deliberately: the
supplied systemd unit is Restart=on-failure, so a clean exit would stop
the service instead of restarting it. On a host with nothing supervising the
process, this is a stop.
Responses
202 Accepted; the drain has started.
application/json
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
403 Authenticated, but not permitted. Either the token lacks the required
scope, or the endpoint needs the admin role. Deliberately not a 401 —
re-authenticating will not help.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 403,
"error": "This token does not have the 'write' scope"
}
POST /admin/everynoise/scrape # Session
Start a scrape.
Session-only, admin role. Runs when an admin asks and never on a
schedule — Every Noise has been frozen since December 2023, so there
is nothing to sync.
Tier 1 is one HTTP request and returns every genre with coordinates
and colour; genre-level features cost that alone. Tier 2 walks ~6,300
genre pages at 1 req/s — about two hours — for exemplar artists and
the adjacency graph. Build on tier 1 first.
A fetch that parses to zero genres is treated as a failure rather
than written, or a changed page would silently empty the table. Resume
is per page, written in the same transaction as the page's contents, so
a crash can never mark a page done with nothing in it. A second
concurrent start is refused.
Request body required
application/json
object
tier string required
map (one request) or pages (~6,300, about two hours). pages
needs the map first.
Responses
application/json
object
tier string required
map is the genre map, one request; pages is every genre's page,
~6,300 requests at one a second.
mappages
400 No admin contact set, or an unknown tier.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
403 Authenticated, but not permitted. Either the token lacks the required
scope, or the endpoint needs the admin role. Deliberately not a 401 —
re-authenticating will not help.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 403,
"error": "This token does not have the 'write' scope"
}
409 A scrape is already running, or pages was asked for before the map.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
POST /admin/everynoise/cancel # Session
Cancel a running scrape.
Session-only, admin role. Progress already written is kept — resume is per
page.
Responses
application/json
object
status string required
saved, stopping or cleared.
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
403 Authenticated, but not permitted. Either the token lacks the required
scope, or the endpoint needs the admin role. Deliberately not a 401 —
re-authenticating will not help.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 403,
"error": "This token does not have the 'write' scope"
}