Axon API (0.1.0)

Download OpenAPI specification:

License: LicenseRef-axon

Read API for a personal Matrix state layer. Account-scoped resources nest under /v1/accounts/{account_id}; /v1/rooms is the cross-account aggregate.

accounts

The Matrix accounts this Axon manages

List the accounts this Axon manages, oldest first — the **client-visible** set: `active` and `deactivated`.

Logged-out (deactivated) accounts are included so a client that has lost the account_id can still discover one to offer re-login (the login verb both produces and reactivates them). The transient deleting teardown state is excluded — a row mid-removal isn't something to act on — but any account, in any state, can still be read by id via [get_account].

Authorizations:
bearer_token

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Adopt an existing Matrix access token as a runtime account, then return the resulting active account — the runtime replacement for the retired `sync.account.access_token` boot-provisioning path: restore a session axon didn't mint itself (issued by another client, or an SSO-only account with no password) without a fresh login. Idempotent by Matrix `username`, exactly like [`login`]: a new identity mints a row, a logged-out (`deactivated`) account is reactivated using its *stored* endpoint (the request's `homeserver_url` is only consulted for a new identity), and an already-`active` account is returned unchanged (the token isn't consulted). An account mid-deletion (`deleting`) is a `409`.

Unlike login, no homeserver call confirms the token before this returns — session restore is a purely local SDK operation — so the token (and, if the homeserver reports one, the device id) is validated with a whoami round-trip before the account is activated; a mismatched or revoked token is a 401, same as login's rejected-credential case.

Secret-bearing; gated by the bearer-token auth layer like every /v1/ route (M7b, ADR 0029).

Authorizations:
bearer_token
Request Body schema: application/json
required
access_token
required
string

The existing Matrix access token to adopt.

device_id
required
string

The device ID the access token belongs to.

homeserver_url
required
string

Homeserver base URL, e.g. https://matrix.example.org. Required: unlike [LoginRequest], there is no MXID-based discovery for a token.

username
required
string

Full Matrix user ID, e.g. @alice:example.org.

Responses

Request samples

Content type
application/json
{
  • "access_token": "string",
  • "device_id": "string",
  • "homeserver_url": "string",
  • "username": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Add or reactivate a Matrix account at runtime, then return the resulting active account. Idempotent by Matrix `username`: a new identity is minted, a logged-out (`deactivated`) account is reactivated using its stored endpoint, and an already-`active` account is returned **unchanged** (a no-op — the desired end state already holds, so the password isn't consulted and nothing is touched). An account mid-deletion (`deleting`) is a `409`. `username` must be a full Matrix user ID (a malformed one is a `400`); bad credentials are a `401`. The password is used once and never stored.

homeserver_url is optional: when omitted, the server discovers the canonical homeserver from the user ID's server name (.well-known/matrix/client, falling back to the server name itself) — so clients need only username + password. The URL is a connection endpoint; the Matrix ID keys identity. A failed discovery is a 502. The username domain is then checked against the homeserver's own declared server name (best-effort): a user ID written with the homeserver's hostname (@adam:matrix.example.org) is a 400 whose message suggests the ID they almost certainly meant (@adam:example.org), rather than a misleading 401 — and never a silent login as a different identity.

Secret-bearing; gated by the bearer-token auth layer like every /v1/ route (M7b, ADR 0029).

Authorizations:
bearer_token
Request Body schema: application/json
required
homeserver_url
string or null

Homeserver base URL, e.g. https://matrix.example.org. Optional: when omitted, the server discovers it from the user ID's server name via .well-known/matrix/client (falling back to https://<server name>). Supply it explicitly to skip discovery (e.g. http://localhost:8008 for a local dev homeserver).

password
required
string

Account password. Consumed once at login; never persisted.

username
required
string

Full Matrix user ID, e.g. @alice:example.org. A user ID written with the homeserver's hostname as its domain (@alice:matrix.example.org) is rejected with a 400 whose message suggests the canonical spelling.

Responses

Request samples

Content type
application/json
{
  • "homeserver_url": "string",
  • "password": "string",
  • "username": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Read a single account by id, in whatever lifecycle state it is — unlike the list, a direct by-id read is not filtered to `active` (so a client can poll an account it knows and watch it transition). An unknown id is a 404.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Permanently delete an account and every trace of it: stops syncing it, invalidates its device token upstream, removes its on-disk SDK store, and drops its row (cascading away its archived events, room state, and account data). Returns `204 No Content` — the resource is gone, so there is nothing to return.

Unlike [logout] this is not reversible: re-adding the same Matrix account later is a fresh login with a new account_id. Idempotent and crash-safe — a delete of an account already mid-teardown resumes it, and an interrupted delete is finished by the next boot's reconcile. An unknown id is a 404. A 409 means the account's sync task has not finished shutting down; retry shortly. This is durable by construction — nothing auto-provisions accounts, so a deleted account never comes back except through an explicit login/import call.

Destructive; gated by the bearer-token auth layer like every /v1/ route (M7b, ADR 0029).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Log a Matrix account out, then return the resulting (now `deactivated`) account. Stops syncing it, invalidates its device token upstream (best-effort — an unreachable homeserver never fails the logout), and moves it to a logged-out state, **retaining all of its data** (archive, search, media) so a later login reactivates the same `account_id`. Idempotent: logging out an already-logged-out account is a `200` no-op. An account mid-deletion (`deleting`) is a `409`; an unknown id is a `404`.

Secret-bearing / destructive; gated by the bearer-token auth layer like every /v1/ route (M7b, ADR 0029).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Acquire E2EE keys for an **active** account from its Secure-Storage (4S) recovery key, then return the account with its `verified` flag re-derived. One SDK call imports the account's megolm key backup + cross-signing keys — self-verifying axon's device with no interactive partner — and any already-stored UTDs the keys unlock are back-filled. The recovery key is used once and never persisted.

A 200 means the keys were imported (the recovery succeeded); the returned verified reflects the freshly-derived cross-signing state, which is true in the normal case but is a derived observation, not a guaranteed postcondition (e.g. a partial Secure-Backup that imports the megolm key but not the cross-signing keys would import successfully yet stay unverified).

The account must be active: a logged-out (deactivated) account is a 409 (log in first), as is one mid-deletion (deleting). A wrong/rotated key, or an account that never set up Secure Backup, is a 400 (a readable error, not a silent permanent UTD). An unknown id is a 404.

Secret-bearing; gated by the bearer-token auth layer like every /v1/ route (M7b, ADR 0029).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Request Body schema: application/json
required
recovery_key
required
string

The account's Secure-Storage (4S) recovery key.

Responses

Request samples

Content type
application/json
{
  • "recovery_key": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Explicitly retry every pending UTD for an active account. The default startup policy attempts each stored UTD once, then waits for fresh room-key arrivals; this endpoint is the authenticated operator escape hatch for rows whose keys are already in the crypto store or whose initial startup attempt predated key acquisition.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

rooms

Rooms and their timelines

Bundle of small "what kind of room is this" reads: join rule, history visibility, guest access, and encryption algorithm (issue #404, ADR 0084). Each is an independent singleton `room_state` lookup — fanned out concurrently rather than four sequential round trips, since none depends on another's result. An unknown room or unset state reads as `null` for that field, not a 404.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

List the current members of a room — the resolved `m.room.member` state, one entry per user. Useful for seeding a display-name map without requiring membership events to appear in the loaded timeline. An unknown `account_id`/`room_id` yields an empty list (200), not a 404.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

List the room's currently pinned messages (`m.room.pinned_events`), resolved and hydrated exactly like any other timeline read (redaction masking, edit collapse, reaction tally) rather than a raw event-id list (issue #404, ADR 0084). Order follows the pinned list's own array order. An unknown `account_id`/`room_id`, or a room with no pinned state, yields an empty list (200), not a 404.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

List a space's children (`m.space.child`, MSC1772-ordered: `order` string ascending, absent sorts last, then `origin_ts`, then `room_id`), each enriched with the child room's own cached name/avatar/room_type where Axon knows that room too (issue #404, ADR 0084). An unknown `account_id`/ `room_id`, or a room with no children, yields an empty list (200), not a 404. The store caps this at a fixed row count against a room with a pathological number of children (`SPACE_HIERARCHY_CAP` in `axon-store::spaces`).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id of the space

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

List the spaces a room belongs to (`m.space.parent`) — the reverse lookup of [`space_children`]. An unknown `account_id`/`room_id`, or a room with no parents, yields an empty list (200), not a 404. Same row cap as `space_children`.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

List the threads in a room (M8): one summary per distinct `m.thread` root, most-recently-active first, each with its reply count and latest reply. The root events themselves are read via the single-event endpoint; a thread's members are paged via [`thread_timeline`]. An unknown `account_id`/`room_id` (or a room with no threads) yields an empty list (200), not a 404. The list is hard-capped in the store against a room with a pathological thread count.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Read a thread's timeline (M8): the `m.thread` members whose root is `root_id`, newest first, with the same cursor pagination as the room timeline. The thread root itself is not included (fetch it via the single-event endpoint). Reactions and edits on thread members are aggregated onto their rows like any other timeline read. An unknown thread yields an empty page (200); a malformed cursor is a 400.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

root_id
required
string

Matrix event id of the thread root

query Parameters
cursor
string

Opaque cursor from a previous page's next_cursor; omit for the newest page.

limit
integer <int64>

Page size (default 50, max 200).

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Read a room's timeline, newest first, with cursor pagination.

An unknown account_id/room_id yields an empty page (200), not a 404 — an empty timeline and a non-existent room are indistinguishable here and an empty page is the natural answer.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

query Parameters
cursor
string

Opaque cursor from a previous page's next_cursor; omit for the newest page.

limit
integer <int64>

Page size (default 50, max 200).

at_ts
integer <int64>

Jump to a specific point in time: Unix milliseconds. Returns the page of events at or before this timestamp. Mutually exclusive with cursor; supplying both is a 400.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Where a tombstoned room's replacement lives, and/or where this room was upgraded from (issue #404, ADR 0084). The two reads are independent, so they're fanned out concurrently rather than run one after the other. An unknown room, or one with neither event, reads as all-`null` (200), not a 404. Deliberately not folded into `RoomDto` — see [`RoomUpgradeDto`] for why.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

List rooms across accounts, most-recent-activity first.

Authorizations:
bearer_token
query Parameters
account_id
string <uuid>

Narrow the list to a single account. Omit for all accounts.

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

search

Full-text search across the index

Search across the index, BM25-ranked when a full-text query is present, paginated.

503 when search is disabled (search.enabled = false). A missing/empty q is allowed only with at least one narrowing filter (account_id, room_id, sender, from, or to); an unbounded empty query or malformed cursor is a 400. Results are the resolved read-API event view (latest edited body, redaction-masked) plus each hit's score.

Authorizations:
bearer_token
query Parameters
q
string

The full-text query string. Parsed against message bodies; all terms are required (AND). May be omitted when at least one narrowing filter is present.

account_id
string <uuid>

Restrict to one account. Omit to search across all accounts.

room_id
string

Restrict to one room.

sender
string

Restrict to senders whose Matrix user id contains this substring, case-insensitively.

from
integer <int64>

Inclusive lower bound on origin_server_ts, Unix milliseconds.

to
integer <int64>

Inclusive upper bound on origin_server_ts, Unix milliseconds.

limit
integer <int64>

Page size (default 50, max 200).

cursor
string

Opaque cursor from a previous page's next_cursor; omit for the first page.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

status

Server health and backfill status

Report backfill status: the disk-space valve state (live free space, whether paused) plus per-account backfill progress; per-account sync-service status; and the running build's identity.

Authorizations:
bearer_token

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

events

Individual events

Read a single event by `(account_id, event_id)`. Redacted events come back masked (`content`/`body` null, `redacted` true); an unknown id is a 404.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

event_id
required
string

Matrix event id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

The forensic edit history of an event (M8): every `m.replace` targeting it, oldest first, *unfiltered* by the resolution rules — including edits from a non-sender or an incompatible `msgtype` — for clients that want to show or audit the edit trail. The collapsed timeline (and `EventDto.edited` / `edit_count`) is the resolved view; this is the raw provenance. An event with no edits (or an unknown event) yields an empty array, not a 404. The result is hard-capped in the store against a pathological edit count.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

event_id
required
string

Matrix event id

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Per-emoji reaction tally for an event (M8), resolved over the event's reactions on disk regardless of where the original message falls in the timeline window — the fix for the dropped-reaction bug (issue #22). The body is a JSON object keyed by emoji: `{ "👍": { "count": 2, "me": true, "senders": [...], "my_event_ids": [...] } }`. `my_event_ids` carries the account user's own reaction event ids for the key — the ids a client redacts to withdraw the reaction, since the collapsed timeline no longer carries the raw `m.reaction` rows. An event with no reactions (or an unknown event) yields an empty object (`{ "data": {} }`), not a 404 — an absent event and one with no reactions are indistinguishable here and empty is the natural answer. Against a pathological event, the store hard-caps the tally at the oldest 1000 distinct `(sender, key)` pairs (deterministically by `(origin_ts, event_id)`), so an event beyond that bound reports those rather than literally every reaction.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

event_id
required
string

Matrix event id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Direct replies to an event (M8): events whose nested `m.relates_to.m.in_reply_to.event_id` is this event and which carry no `rel_type` (a plain reply, not a thread member). Oldest first; redaction-masked. An event with no replies (or an unknown event) yields an empty array, not a 404 — empty is the natural answer. The result is hard-capped in the store against a pathological room.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

event_id
required
string

Matrix event id

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Read the per-event verification bundle (M7c): the durable at-decrypt sender- trust snapshot plus live cross-signing evidence for the sender's device and identity. An unknown account/event is a 404; a logged-out account is a 409 (no live client to read evidence from). Gated by the bearer-token auth layer like every `/v1/` route.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

event_id
required
string

Matrix event id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

messages

Sending, editing, redacting, and reacting

Edit an existing message (sends an `m.replace`).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

event_id
required
string

Event id being edited

Request Body schema: application/json
required
body
required
string

The new plain-text message body (and formatting fallback).

format
string or null

Markup name for formatted_body — only org.matrix.custom.html. Must be paired with formatted_body.

formatted_body
string or null

The rendered HTML body. Must be paired with format.

Responses

Request samples

Content type
application/json
{
  • "body": "string",
  • "format": "string",
  • "formatted_body": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Redact an event, optionally with a reason (`?reason=`).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

event_id
required
string

Event id being redacted

query Parameters
reason
string

Optional human-readable reason recorded on the redaction.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

React to an event with an emoji/short key.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

event_id
required
string

Event id being reacted to

Request Body schema: application/json
required
key
required
string

The reaction key — typically an emoji.

Responses

Request samples

Content type
application/json
{
  • "key": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Send a plain-text message to a room.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
body
required
string

The plain-text message body (and formatting fallback).

format
string or null

Markup name for formatted_body — only org.matrix.custom.html. Must be paired with formatted_body.

formatted_body
string or null

The rendered HTML body. Must be paired with format.

reply_to
string or null

Send as a reply to this event id (m.in_reply_to). Optional.

thread_root
string or null

Send into this thread, identified by its root event id (rel_type: m.thread). Optional; when set with reply_to, the reply is scoped to the thread. Without reply_to, this is a thread member, not a reply.

Responses

Request samples

Content type
application/json
{
  • "body": "string",
  • "format": "string",
  • "formatted_body": "string",
  • "reply_to": "string",
  • "thread_root": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Send a staged media upload to a room.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
caption
string or null

Optional media caption. When absent, Axon uses the staged filename as the Matrix event body.

reply_to
string or null

Send as a reply to this event id (m.in_reply_to). Optional.

thread_root
string or null

Send into this thread, identified by its root event id (rel_type: m.thread). Optional; when set with reply_to, the reply is scoped to the thread. Without reply_to, this is a thread member, not a reply.

upload_id
required
string <uuid>

Server-issued staged upload id returned by POST …/media/uploads.

Responses

Request samples

Content type
application/json
{
  • "caption": "string",
  • "reply_to": "string",
  • "thread_root": "string",
  • "upload_id": "f2ef591b-135b-46fa-a604-3d4fda5bfbfb"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

ephemeral

Outbound read receipts and typing notices sent to the homeserver

Mark a room read: sets both the public read receipt and the private fully-read marker to `event_id` in one homeserver call.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
event_id
required
string

The event id to mark as read.

Responses

Request samples

Content type
application/json
{
  • "event_id": "string"
}

Response samples

Content type
application/json
{
  • "data": null
}

Set (or clear) this account's typing indicator in a room.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
typing
required
boolean

Whether the account is now typing in this room. Setting false clears an active typing indicator early instead of waiting for it to expire.

Responses

Request samples

Content type
application/json
{
  • "typing": true
}

Response samples

Content type
application/json
{
  • "data": null
}

membership

Existing-room membership: leave, forget, invite, kick, ban, unban

Ban a user from this room, optionally with a reason.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
reason
string or null

Optional human-readable reason recorded on the membership change.

user_id
required
string

The target user's Matrix id (@user:server).

Responses

Request samples

Content type
application/json
{
  • "reason": "string",
  • "user_id": "string"
}

Response samples

Content type
application/json
{
  • "data": null
}

Forget a left or banned-from room.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": null
}

Invite a user to this room.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
user_id
required
string

The invited user's Matrix id (@user:server).

Responses

Request samples

Content type
application/json
{
  • "user_id": "string"
}

Response samples

Content type
application/json
{
  • "data": null
}

Kick a user from this room, optionally with a reason.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
reason
string or null

Optional human-readable reason recorded on the membership change.

user_id
required
string

The target user's Matrix id (@user:server).

Responses

Request samples

Content type
application/json
{
  • "reason": "string",
  • "user_id": "string"
}

Response samples

Content type
application/json
{
  • "data": null
}

Leave this room.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": null
}

Unban a user from this room, optionally with a reason.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
reason
string or null

Optional human-readable reason recorded on the membership change.

user_id
required
string

The target user's Matrix id (@user:server).

Responses

Request samples

Content type
application/json
{
  • "reason": "string",
  • "user_id": "string"
}

Response samples

Content type
application/json
{
  • "data": null
}

room-entry

Joining, knocking on, and creating rooms

Create a new room.

Not idempotent: Matrix's createRoom endpoint has no idempotency key, so retrying this call after a client-side timeout can create a second room upstream if the first attempt actually succeeded after Axon's own timeout fired (see SdkGateway::create_room).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Request Body schema: application/json
required
encrypted
boolean

When true, an m.room.encryption event is included in the room's initial state, so it is encrypted from its first transaction rather than via a later, racier enable_encryption call.

invite
Array of strings

Users to invite on creation (@user:server).

is_direct
boolean

Whether to set the is_direct flag on the invites this creates.

name
string or null

Room name (m.room.name), if set.

null or RoomPresetDto (string)
public
boolean

Whether the room is published in the room directory. Defaults to false (private).

topic
string or null

Room topic (m.room.topic), if set.

Responses

Request samples

Content type
application/json
{
  • "encrypted": true,
  • "invite": [
    ],
  • "is_direct": true,
  • "name": "string",
  • "preset": { },
  • "public": true,
  • "topic": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Create a DM with another user.

Not idempotent: Matrix's createRoom endpoint has no idempotency key, so retrying this call after a client-side timeout can create a second DM room upstream if the first attempt actually succeeded after Axon's own timeout fired (see SdkGateway::create_dm). This route always creates a new room; it does not check for or reuse an existing DM with the target user.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Request Body schema: application/json
required
user_id
required
string

The other participant's Matrix id (@user:server).

Responses

Request samples

Content type
application/json
{
  • "user_id": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Join a room by id or alias.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Request Body schema: application/json
required
room_id_or_alias
required
string

The room id (!room:server) or alias (#room:server) to join.

server_names
Array of strings

Federation-resolution hints (ruma's via) for an alias/id this account's client has no direct path to.

Responses

Request samples

Content type
application/json
{
  • "room_id_or_alias": "string",
  • "server_names": [
    ]
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Knock on a room, optionally with a reason.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Request Body schema: application/json
required
reason
string or null

Optional human-readable reason shown to the room's members.

room_id_or_alias
required
string

The room id (!room:server) or alias (#room:server) to knock on.

server_names
Array of strings

Federation-resolution hints (ruma's via) for an alias/id this account's client has no direct path to.

Responses

Request samples

Content type
application/json
{
  • "reason": "string",
  • "room_id_or_alias": "string",
  • "server_names": [
    ]
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

room-settings

Room name, topic, avatar, and this account's tags on a room

Set this room's avatar from an already-staged upload.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
upload_id
required
string <uuid>

Server-issued staged upload id returned by POST …/media/uploads.

Responses

Request samples

Content type
application/json
{
  • "upload_id": "f2ef591b-135b-46fa-a604-3d4fda5bfbfb"
}

Response samples

Content type
application/json
{
  • "data": null
}

Clear this room's avatar.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": null
}

Set this room's name. An empty `name` clears it.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
name
required
string

Responses

Request samples

Content type
application/json
{
  • "name": "string"
}

Response samples

Content type
application/json
{
  • "data": null
}

Add or update a tag on this room (room account data, not a state event).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

tag
required
string

Tag wire form: m.favourite, m.lowpriority, m.server_notice, or a u.-prefixed custom tag

Request Body schema: application/json
required
order
number or null <double>

Optional sort order among this account's tagged rooms, in [0, 1] per the Matrix spec's m.tag convention. Rejected as 400 outside that range.

Responses

Request samples

Content type
application/json
{
  • "order": 0
}

Response samples

Content type
application/json
{
  • "data": null
}

Remove a tag from this room.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

tag
required
string

Tag wire form: m.favourite, m.lowpriority, m.server_notice, or a u.-prefixed custom tag

Responses

Response samples

Content type
application/json
{
  • "data": null
}

Set this room's topic. An empty `topic` clears it.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
topic
required
string

Responses

Request samples

Content type
application/json
{
  • "topic": "string"
}

Response samples

Content type
application/json
{
  • "data": null
}

power-levels

Role thresholds and per-user power levels for a room

Read this room's fully resolved power levels (defaults filled in).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Apply a power-level change: role thresholds and/or per-user levels, merged into one `m.room.power_levels` write.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

room_id
required
string

Matrix room id

Request Body schema: application/json
required
acknowledge_self_demotion
boolean

Bypasses the self-demotion guardrail: without this, a change that would drop the caller's own resolved power level below what's needed to send another m.room.power_levels event is rejected as 400, since that write would otherwise succeed and permanently strand the caller with no way to self-correct.

ban
integer or null <int64>
events_default
integer or null <int64>
invite
integer or null <int64>
kick
integer or null <int64>
redact
integer or null <int64>
state_default
integer or null <int64>
object

User id -> requested power level, merged into the room's existing users map. A user not present here keeps their current level.

users_default
integer or null <int64>

Responses

Request samples

Content type
application/json
{
  • "acknowledge_self_demotion": true,
  • "ban": 0,
  • "events_default": 0,
  • "invite": 0,
  • "kick": 0,
  • "redact": 0,
  • "state_default": 0,
  • "users": {
    },
  • "users_default": 0
}

Response samples

Content type
application/json
{
  • "data": null
}

account-actions

This account's own profile and ignore list, another user's profile, and public-room directory search

Search a homeserver's public-room directory.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

query Parameters
server
string

Search the directory of this server instead of the account's own homeserver.

search_term
string

Free-text filter over room name, topic, and canonical alias.

limit
integer <int32> >= 0

Maximum rooms to return in this page.

since
string

Pagination token from a previous page's next_batch.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Set this account's avatar from an already-staged upload.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Request Body schema: application/json
required
upload_id
required
string <uuid>

Server-issued staged upload id returned by POST …/media/uploads.

Responses

Request samples

Content type
application/json
{
  • "upload_id": "f2ef591b-135b-46fa-a604-3d4fda5bfbfb"
}

Response samples

Content type
application/json
{
  • "data": null
}

Clear this account's avatar.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Responses

Response samples

Content type
application/json
{
  • "data": null
}

Set this account's own display name. An empty `display_name` clears it.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Request Body schema: application/json
required
display_name
required
string

Responses

Request samples

Content type
application/json
{
  • "display_name": "string"
}

Response samples

Content type
application/json
{
  • "data": null
}

Add `user_id` to this account's ignore list.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

user_id
required
string

Target Matrix user id (@user:server)

Responses

Response samples

Content type
application/json
{
  • "data": null
}

Remove `user_id` from this account's ignore list.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

user_id
required
string

Target Matrix user id (@user:server)

Responses

Response samples

Content type
application/json
{
  • "data": null
}

Read a Matrix user's profile (display name + avatar). `user_id` may be this account's own user id or any other user's.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

user_id
required
string

Target Matrix user id (@user:server)

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

verification

Interactive SAS device verification

List the account's currently-tracked verification flows (live, plus recently-terminal ones still within their grace window).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Responses

Response samples

Content type
application/json
{
  • "data": [
    ]
}

Start a SAS verification — against one of the account's own trusted devices (`device_id`, self-verification) or against another user's identity (`user_id`, cross-user verification; ADR 0040) — returning the new flow's `flow_id`. The exchange then proceeds asynchronously: watch `verification.*` frames over `/v1/ws`, or poll `GET …/verify/{flow_id}`.

Trust-bearing; gated by the bearer-token auth layer like every /v1/ route (M7b, ADR 0029).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

Request Body schema: application/json
required
device_id
string or null

The device ID of the trusted device to verify against (self-verification).

user_id
string or null

The user ID of another user to verify (cross-user verification).

Responses

Request samples

Content type
application/json
{
  • "device_id": "string",
  • "user_id": "string"
}

Response samples

Content type
application/json
{
  • "data": {
    }
}

Read one verification flow's replayable state — the endpoint a reconnecting client reads to resume (current stage, target device, and the SAS emoji/decimals once they're available).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

flow_id
required
string

Verification transaction id

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Cancel the flow. Returns `204`. Idempotent — cancelling an already-terminal flow succeeds. Trust-bearing; gated by the bearer-token auth layer (M7b).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

flow_id
required
string

Verification transaction id

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Confirm that the SAS matches, sending this side's MAC. Returns `204` once the confirm is sent; completion (`verification.done`) arrives asynchronously. Idempotent. Trust-bearing; gated by the bearer-token auth layer (M7b).

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

flow_id
required
string

Verification transaction id

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

devices

Device-list / discovery, for the SAS verification picker

List a Matrix user's devices — the account's own by default, or an arbitrary user via `?user_id=`. Reads the SDK's local crypto-store cache on every call (kept current by the account's background sync, not by a homeserver call this endpoint makes itself); no device roster is persisted by axon. A `user_id` axon has never tracked (no shared encrypted room) returns `200` with an empty `devices` list, not an error.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

query Parameters
user_id
string

Matrix user id to list devices for. Omit to list the account's own devices (self-verification picker); supply to list another user's (cross-user picker, ADR 0040).

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

media

Authenticated MXC media proxy

Stage raw upload bytes for a later room-aware media send.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

query Parameters
kind
required
string (MediaUploadKindDto)
Enum: "image" "file"

Matrix media message kind the staged bytes are intended to become.

filename
required
string

Original filename. The handler normalizes path-like input down to the basename before storing it.

Request Body schema: application/octet-stream
required

Raw media bytes streamed to Axon's durable upload staging area

string

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Delete an unsent staged upload.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account id

upload_id
required
string <uuid>

Server-issued upload id

Responses

Response samples

Content type
application/json
{
  • "data": null
}

Proxy an `mxc://` download through the account's homeserver connection.

The server_name and media_id path segments form the mxc:// URI that was embedded in a Matrix event's primary or thumbnail media descriptor. The response body is the raw media bytes, streamed from the on-disk cache with range-request and conditional-GET support.

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account whose credentials are used for the download

server_name
required
string

Server-name component of the MXC URI (the part after mxc://)

media_id
required
string

Media-ID component of the MXC URI

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

Proxy a homeserver-generated thumbnail of an `mxc://` object through the account's homeserver connection.

Shares [get_media]'s event lookup (via [resolve_media_content]) and conditional-GET/range-serving machinery, but resolves a resized variant via [MediaProxy::get_thumbnail] instead of the full original, and corrects Content-Type by sniffing the returned bytes (see [correct_thumbnail_content_type]) since a homeserver-regenerated thumbnail may not match the original's declared mimetype. Encrypted media is rejected with 400 before the proxy is ever called — see [resolve_thumbnail_spec].

Authorizations:
bearer_token
path Parameters
account_id
required
string <uuid>

Axon account whose credentials are used for the download

server_name
required
string

Server-name component of the MXC URI (the part after mxc://)

media_id
required
string

Media-ID component of the MXC URI

query Parameters
width
required
integer <int32> >= 0

Desired width in pixels. Clamped to a fixed [min, max] range and snapped up to the nearest of a small set of standard sizes, rather than rejected — see routes::media::snap_thumbnail_dimension.

height
required
integer <int32> >= 0

Desired height in pixels. Same clamp-and-snap as width.

method
string (ThumbnailMethodDto)
Enum: "crop" "scale"

Resizing method; defaults to scale (the Matrix spec default) when omitted.

Responses

Response samples

Content type
application/json
{
  • "error": {
    }
}

device-state

Per-device client state: drafts, read markers

Read one namespace of per-device state: the last-write-wins **merged view across all the account's devices** (per key, the newest write wins; deleted keys are absent). A namespace never written is an empty map, not a `404`. The `device_id` names the calling device; the merge is account-wide.

Authorizations:
bearer_token
path Parameters
device_id
required
string <uuid>

The calling device (client-supplied UUID)

namespace
required
string

The state namespace, e.g. drafts

query Parameters
account_id
required
string <uuid>

The Axon account this state belongs to.

Responses

Response samples

Content type
application/json
{
  • "data": {
    }
}

Write per-device state: a **merge-upsert** of the supplied entries under the calling device. Only the keys present are touched; a `null` value deletes the key (a tombstone, so the deletion wins the cross-device merge). Last-write-wins on the server clock — the returned `updated_at` is the ordering all devices share. A successful write with at least one entry fans out a `device_state.changed` frame over `/v1/ws` carrying this `device_id`, which sibling devices apply and the originator ignores.

Authorizations:
bearer_token
path Parameters
device_id
required
string <uuid>

The writing device (client-supplied UUID; first PUT registers it)

namespace
required
string

The state namespace, e.g. drafts

query Parameters
account_id
required
string <uuid>

The Axon account this state belongs to.

Request Body schema: application/json
required
required
object

The entries to write, keyed by the client-chosen key. null deletes.

Responses

Request samples

Content type
application/json
{
  • "entries": {
    }
}

Response samples

Content type
application/json
{
  • "data": {
    }
}