Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Admin API Reference

Hammerhead has a few internal “admin APIs” that can be used to manage the server.

Authentication

The admin API is authenticated by supplying a bearer token like with any other Matrix request, with the additional requirement that the token points at an account that has the administrator flag. The first account created is automatically given admin at the time of registration, however you can manually give any subsequent account admin by either:

  • Running UPDATE accounts SET admin=true WHERE localpart='foo'; via psql (localpart is the bit of the user ID between @ and :).
  • With an existing admin account, call update user with {"admin": true}

Pre-shared token authentication

Some endpoints, like create user, allow you to authenticate with the static “pre-shared token” defined in the configuration file: registration.admin_pre_shared_secret. The intent is that you can create the first account without needing to enable registration, and to allow automation without needing to delegate admin to yet another account (which increases your attack surface). The PSK may do more in the future.

Not all endpoints support this method of authentication. Those that do will state so explicitly.

Versioning

The admin API is locally versioned. Any time a breaking change is made to the functionality of a route, the version is increased. This does mean, however, that there can be any number of API versions active at once, such as /_hammerhead/v0/foo, /_hammerhead/v1/bar, and /_hammerhead/v9007199254740991/hello-world.

Consumers should always use the latest version, however removals will always be included in release notes, and historical versions will be kept around for as long as it is sensible to.

Important

Until v0.1.0 is released, this versioning is not respected.

Endpoints

Get Uptime

GET /_hammerhead/v0/uptime

Authentication: None.

Fetches the epoch from when the server started. “Started” refers to the unix timestamp at when the HTTP listeners were started, a.k.a. when the server became ready, not when the process itself started.

Response body:

{
    "started_at": 1775928780089
}
KeyTypeDescription
started_atnumberThe time the server started, in unix milliseconds

Get Version

GET /_hammerhead/v0/version

Authentication: None.

Returns full version metadata, including build date, commit hash, tagged version, OS architecture, etc. This is most useful for debugging and preparing issues, as it is very comprehensive.

Response body:

{
    "build_date": 1775928757000,
    "commit_hash": "c6a5ea0",
    "dirty": true,
    "full": "v0.0.1-dev+gc6a5ea0+dirty+d2026.04.11T17.32.37Z+go1.26.0@linux/amd64",
    "go_version": "go1.26.0",
    "latest_tag": "v0.0.0",
    "os_arch": "linux/amd64",
    "short": "v0.0.1-dev+gc6a5ea0",
    "tagged_version": ""
}
KeyTypeDescription
build_datenumberThe unix timestamp (milliseconds) when the running binary was compiled
commit_hashstringThe short commit hash the running binary was compiled with
dirtybooleanWhether the working tree was dirty when the running binary was compiled (uncommitted changes)
fullstringThe full version string. Used for issue reporting, as it combines all useful info into one string
go_versionstringThe Go compiler version the running binary was compiled with
latest_tagstringThe latest git tag available at the time of compile
os_archstringThe architecture the binary was compiled for (OS/ARCH)
shortstringThe short version string. Used in the server’s User-Agents
tagged_versionstringThe tag the commit_hash points at, if available. Otherwise, an empty string

Make PDU

POST /_hammerhead/v0/admin/make-pdu

Authentication: Admin account.

Creates a PDU with the given input. You currently can’t do anything with this, as the send-pdu counterpart has been temporarily removed.

If the room_version is omitted or empty, it will be fetched from the database. If a room version cannot be found, 404 / M_NOT_FOUND is returned.

room_id, sender, and content are the only required keys in the pdu object. Anything else required will be calculated on-demand. However, anything included in the base pdu will not be modified, meaning you can specify (for example) custom auth_events, prev_events, depth, and origin_server_ts values.

The event’s calculated event ID will be included under unsigned.

Request body:

{
    "dont_hash": true,
    "dont_sign": true,
    "room_version": "11",
    "pdu": {
        "content": {

        },
        "room_id": "!...",
        "sender": "@..."
    }
}
KeyTypeDescription
dont_hashboolean (default: false)If true, hashing and signing will not be performed on the PDU
dont_signboolean (default: false)If true, hashing will be performed, but signing will not
room_versionstring (optional)If creating a PDU for an unknown room, you can manually specify the version of the room. If omitted, the room’s version will be fetched from the database.
pduobjectThe base PDU object

Response body:

201 Created:

{
    "auth_events": ["$..."],
    "content": {

    },
    "depth": 1,
    "origin_server_ts": 123456789,
    "prev_events": ["$..."],
    "room_id": "!...",
    "sender": "@...",
    "unsigned": {
        "event_id": "$..."
    }
}

Refer to the “event format” section of the specified room_version’s documentation: https://spec.matrix.org/v1.18/rooms/. This endpoint always successfully responds with 201 Created.

Errors:

  • 400 / M_BAD_JSON: The request body is missing the pdu field, or pdu is missing some required keys.
  • 403 / M_FORBIDDEN: You did not authenticate, or are not a server administrator.
  • 404 / M_NOT_FOUND: room_version, auth_events, prev_events, or depth were not supplied, and the server could not fetch required data from the database to automatically populate these fields.
  • 500 / M_UNKNOWN: Hashing, signing, or event ID calculation failed.

Reload Configuration

POST /_hammerhead/v0/admin/reload-config

Authentication: Admin account.

Immediately reloads the server configuration by re-reading the configuration path, and clears internal caches.

Warning

Configuration reloading is not comprehensive, and you should usually restart the server entirely instead.

While Hammerhead components generally all refer directly to the same configuration reference variable, some values are disassociated from the configuration while initialising (such as cache sizes), meaning reloading the configuration may not update those values.

Furthermore, not all caches are cleared. Hammerhead has a LOT of moving parts, clearing EVERYTHING is not feasible.

Request body: None (ignored).

Response body (200 OK): JSON representation of the freshly loaded configuration file.

Errors:

  • 403 / M_FORBIDDEN: You forgot to authenticate, or are not a server administrator.
  • 500 / M_UNKNOWN: There was an issue reloading the configuration. Typically, the result of an invalid configuration, or when the configuration is no longer found at the initial path.

Delete Media

DELETE /_hammerhead/v0/admin/media/{origin}/{media_id}

Authentication: Admin account.

Deletes a specific piece of media. If origin is the current server, it can be shortened to _. Media that does not exist always returns a successful response (unless the database returns an error).

Request body: None (ignored)

Response body:

200 OK:

{
    "ok": 1,
    "fail": 0
}
KeyTypeDescription
oknumberThe number of media files that were successfully deleted (in this case, always 1)
failnumberThe number of media files that could not be deleted (in this case, always 0)

Errors:

  • 400 / M_MISSING_PARAM: origin or media_id were missing or empty.
  • 403 / M_FORBIDDEN: You forgot to authenticate, or are not a server administrator.
  • 500 / M_UNKNOWN: The server was unable to delete the media (database error, or the file system returned an error other than “file does not exist”, such as a permission error).

Purge Media

POST /_hammerhead/v0/admin/media/purge

Authentication: Admin account.

Purges media from the repository matching the specified criteria. This is irreversible and uninterruptible. Make no mistakes.

When multiple criteria are specified, they create an OR condition, not an AND. For example, providing all_remote_media=true and all_caches=true will delete all media that originated on another homeserver, OR is a cache file. This is no different for origin and user_id - if you supply all_remote_media=true and origin="_", this will delete media that is from another homeserver OR belongs to this homeserver.

Definitions

Remote media: Media that was uploaded to other Matrix homeservers, and was retrieved over federation.

Local media: Media that was uploaded directly to this homeserver.

Sparse media: Local media that was asynchronously created, but never had any content uploaded to it.

External media: Media cached from non-Matrix servers, such as URL preview images. Not associated with any user.

Tip

You typically do not need to manually clean up sparse media, as there is a clean-up task that runs every few hours, and one that also runs on server startup.

Caution

Media deletion is permanent and indiscriminate. As media is not currently related to events, there is no way for the server to know if a media file is a sticker, profile picture, custom emoji, room avatar, or just a normal attachment. It also has no way of knowing if the media has ever been used. Purging media will almost always have some undesirable collateral, so you should always think thrice before deleting local media. Deleting remote media is less bad, since the media can just be fetched again over federation, but only if the original server is still online.

The user filter will only work for local media, or for remote media retrieved from another Hammerhead server. Regular remote media does not include metadata about who created it, but Hammerhead transmits that information with non-specced metadata.

This operation uses limited concurrency to delete entries in parallel. Read more about how limited concurrency works at Configuration Reference § GOMAXPROCS. This is a blocking operation, if there is a lot of work to do, your request may time out.

Request body:

{
    "all_remote_media": true,
    "all_local_media": true,
    "all_caches": true,
    "all_sparse": true,
    "origin": "_",
    "user": "@baduser:example.com"
}
KeyTypeDescription
all_remote_mediaboolean (default: false)If true, all remote media (media from other Matrix homeservers) is deleted
all_local_mediaboolean (default: false)If true, all local media is deleted
all_cachesboolean (default: false)If true, all cached media is deleted
all_sparseboolean (default: false)If true, all “sparse” media entries are deleted
originstring (default: empty)If not empty, delete all media that originates from the specified server
userstring (default: empty)If not empty, delete all media that was uploaded by the specified user

Response body:

200 OK:

{
    "ok": 420,
    "fail": 69
}
KeyTypeDescription
oknumberThe number of media files that were successfully deleted
failnumberThe number of media files that could not be deleted

Errors:

  • 400 / M_NOT_JSON: The request body was not valid JSON.
  • 403 / M_FORBIDDEN: You forgot to authenticate, or are not a server administrator.
  • 500 / M_UNKNOWN: There was an error querying the database to fetch eligible media entries.

Delete Room

DELETE /_hammerhead/v0/admin/rooms/{room_id}

Authentication: Admin account.

Deletes a room from the database. First tries to remove all local members (leave, decline pending invites, rescind pending knocks), then tries to delete as much data associated with the room as possible.

If force is not true, and there is an error during any stage of the operation, it aborts immediately. Actual data deletion is performed in a transaction, meaning if it fails, no data is deleted. However, membership changes are not transactional, and are always committed immediately, non-atomically.

Tip

You typically do not need to delete rooms to reclaim space from abandoned rooms. When all local members leave a room, it is automatically deleted, and as such does not need to be manually “cleaned up”.

Request body:

{
    "force": true
}
KeyTypeDescription
forceboolean (default: false)If true, errors during evacuation and deletion are ignored where possible

Response body (200 OK): Empty object (literally {}).

Errors:

  • 400 / M_NOT_JSON: The request body was not valid JSON.
  • 403 / M_FORBIDDEN: You forgot to authenticate, or are not an administrator.
  • 429 / M_LIMIT_EXCEEDED: There is already a room delete operation in progress (cannot have more than one at a time).
  • 500 / M_UNKNOWN: There was an unrecoverable error while evacuating or deleting the room.

Create User

POST /_hammerhead/v0/admin/users/create

Authentication: Admin account or pre-shared token.

Creates a new account with the specified pre-filled criteria.

The localpart must be a valid Matrix localpart, see the specification. On the other hand, password is not subject to the same validation that would normally be applied during registration, so low-entropy/too short passwords can be set here. It is recommended you don’t do that, though.

If password is omitted, the created user will not be able to log in (but admins can still create access tokens for them, so the account isn’t useless).

Request body:

{
    "localpart": "username.here",
    "password": "$ecureP4sswordH3re!"
}
KeyTypeDescription
localpartstringThe user’s localpart (the part of the user ID between @ and :)
lockedboolean (default: false)If true, the account will be locked upon creation
suspendedboolean (default: false)If true, the account will be suspended upon creation
passwordstring (optional)The account’s desired password

Response body: 201 Created:

{
    "account_id": 2
}
KeyTypeDescription
account_idnumberThe numberic ID of this account (in the database)

Errors:

  • 400 / M_NOT_JSON: The request body was not valid JSON.
  • 400 / M_INVALID_USERNAME: localpart is empty or invalid.
  • 400 / M_USER_IN_USE: localpart is already in use, or is reserved for another reason (e.g. service account).
  • 403 / M_FORBIDDEN: You forgot to authenticate, or are not a server administrator.
  • 403 / M_FORBIDDEN: You did not provide an administrator access token, and shared-secret token authentication is disabled, or the provided token did not match.

Deactivate Account

POST /_hammerhead/v0/admin/users/{user_id}/deactivate
  • user_id: Fully qualified user ID (@foo:bar.example), or localpart (foo).

Authentication: Admin account or pre-shared token.

Immediately deactivates an account. If the account is already deactivated, makes no change.

Performs the following steps:

  1. Mark account as deactivated (but not erased). Prevents further use of account immediately.
  2. Remove account’s administrator flag.
  3. If redact is true, issue redactions for every event the target ever sent.
  4. Mark account as deactivated again, this time respecting the GDPR erased flag.
  5. Deletes all profile data the target set.
  6. Deletes all account data the target set.
  7. Removes all devices (sessions) the target had.
  8. Then, with limited concurrency, for each room the target was a member of:
    • If the target was invited to the room, reject the invite
    • If the target was knocking on the room, rescind the join request
    • If the target was joined to the room, leave the room

This is a blocking operation, your request may time out.

Request body:

{
    "erase": false,
    "redact": false
}
KeyTypeDescription
eraseboolean (default: false)If true, mark the account as GDPR erased
redactboolean (default: false)If true, issue redactions for every event sent by the user.

Response body (200 OK): Empty object (literally {}).

Response body (304 Not Modified): No data.

Errors:

  • 400 / M_NOT_JSON: Request body was not valid JSON.
  • 403 / M_FORBIDDEN: You forgot to authenticate, or are not a server administrator.
  • 403 / M_FORBIDDEN: You did not provide an administrator access token, and shared-secret token authentication is disabled, or the provided token did not match.
  • 404 / M_NOT_FOUND: The requested user does not exist or does not belong to this server.
  • 500 / M_UNKNOWN: An unrecoverable error was encountered during deactivation.

List Users

GET /_hammerhead/v0/admin/users

Authentication: Admin account.

Lists all users registered on this homeserver. Currently, filtering, sorting, and backwards pagination are not supported. Results are ordered newest → oldest.

Query Parameters:

ParameterTypeDescription
sincenumberThe end token of the previous page (from next_batch).
limitnumber (default: 1)The maximum nmber of results to return. Cannot be less than 1, cannot be more than 1000.

Response body:

200 OK:

{
    "chunk": [
        {
            "entry_id": 1,
            "localpart": "foo",
            "admin": true,
            "locked": false,
            "suspended": false,
            "deactivated": false,
            "erased": false,
            "created_at": 1775660510816
        }
    ],
    "next_batch": 1
}
KeyTypeDescription
chunkArray[Account] (optional)An array of up to {limit} accounts
next_batchnumber (optional)The next batch token to pass to since, if there are potentially more results

Account:

KeyTypeDescription
entry_idnumberThe numeric database ID of this account
localpartstringThe localpart of this account
adminbooleanThe administrator status of this account
lockedbooleanWhether this account is locked
suspendedbooleanWhether this account is suspended
deactivatedbooleanWhether his account has been deactivated
erasedbooleanIn combination with deactivated, whether this account is GDPR-erased
created_atnumberUnix timestamp (in milliseconds) when this account was registered

Errors:

  • 400 / M_INVALID_PARAM: since or limit were not numbers.
  • 403 / M_FORBIDDEN: You forgot to authenticate, or are not a server administrator.

Update User

PATCH /_hammerhead/v0/admin/users/{user_id}
  • user_id: Fully qualified user ID (@foo:bar.example), or localpart (foo).

Authentication: Admin account or pre-shared token.

Updates any of the provided attributes on the account. If an attribute is not provided in the request body, no change is made to the relevant account attribute. As such, all request keys are optional and may be omitted.

Request body:

{
    "password": "foobar",
    "admin": true
}
KeyTypeDescription
passwordstringThe new password for this user
adminbooleanSets the administrator flag for this user

Response body (200 OK): Empty object (literally {}).

Errors:

  • 400 / M_NOT_JSON: The request body was not valid JSON.
  • 403 / M_FORBIDDEN: You forgot to authenticate, or are not a server administrator.
  • 403 / M_FORBIDDEN: You did not provide an administrator access token, and shared-secret token authentication is disabled, or the provided token did not match.
  • 404 / M_NOT_FOUND: The requested user does not exist or does not belong to this server.