Configuring Synapse Spam Checker Callbacks for Matrix Rooms

Running a federated chat community requires robust defenses against unwanted messaging, automated scripts, and abusive room activity. For administrators operating a Matrix homeserver, Synapse provides a dedicated module API that allows custom spam mitigation modules to intercept actions prior to distribution. By writing and loading a custom module, you can evaluate incoming room events submitted by local clients or received over federation and reject unauthorized payloads before they propagate. This setup guide explains how to register and configure these callbacks using the tools documented in the official Synapse spam checker callbacks documentation, complementing broader strategies discussed in our Synapse Matrix Homeserver Profile and our general forum spam prevention guide.
Step 1: Register Callbacks with the Module API
To enable custom spam checking within Synapse, your Python module must declare and register its callback functions during initialization. Synapse provides the register_spam_checker_callbacks method on its module API interface specifically for this purpose.
When Synapse boots and loads your module, your initialization code receives an instance of the module API. By passing your callback functions to register_spam_checker_callbacks, you instruct the homeserver engine to route specific actions—such as incoming message events, room joins, or invites—through your filtering methods before committing them to the room DAG or relaying them across the federation boundary.
Step 2: Implement check_event_for_spam
The primary callback for stopping abusive messages before they federate is check_event_for_spam. First introduced in Synapse v1.37.0, this asynchronous callback is triggered whenever an event is received from a client or arrives via federation. The signature of the method accepts an event object:
async def check_event_for_spam(event: "synapse.module_api.EventBase")
Inside this function, you examine the event payload. Because the callback receives the full event structure, your module can inspect the event content, sender, room identifier, and message type. If an event contains abusive text, disallowed URLs, or signs of flooding, your module can intercept the event on the spot.
Step 3: Return Appropriate Status and Error Codes
The return values for check_event_for_spam were updated in Synapse v1.60.0. In current releases, your callback must avoid deprecated booleans and strings, returning explicit constants from the module API:
- synapse.module_api.NOT_SPAM: Return this constant to permit the operation. Returning this value signals that your module found no policy violations, allowing Synapse to proceed.
- synapse.module_api.errors.Codes: Return an error code from this namespace to reject the operation. The official documentation notes that in case of doubt,
synapse.module_api.errors.Codes.FORBIDDENis a good error code to return.
While older versions permitted returning False to allow or True / raw strings to reject, these return formats are deprecated. Emitting synapse.module_api.errors.Codes.FORBIDDEN cleanly rejects the abusive submission without requiring custom client localization handling.
Step 4: Understand Module Execution Order and Precedence
Homeserver configurations often load several modules simultaneously. As detailed in the Synapse spam checker callbacks reference, Synapse enforces a strict sequence when processing spam checker callbacks:
- If multiple modules implement
check_event_for_spam, Synapse considers them in the order they are configured. - If a callback returns
synapse.module_api.NOT_SPAM, Synapse falls through to the next module's callback. - The value of the first callback that returns something other than
synapse.module_api.NOT_SPAMis immediately used to halt processing. - Once a rejection code is encountered, Synapse skips all subsequent module implementations for that event.
This sequential logic ensures that high-priority checks can stop abusive events immediately without incurring unnecessary computational overhead from downstream modules.
Step 5: Complement Message Checks with Room and Invite Callbacks
Stopping abusive text inside established rooms is only one layer of homeserver defense. Synapse supports several additional callbacks to catch unwanted actors before messages can even be sent:
- user_may_join_room: Introduced in v1.37.0 and updated in v1.61.0, this callback receives the user ID, room ID, and a boolean indicating whether a pending invite exists. It is not invoked for server administrators or during room creation.
- user_may_invite: Introduced in v1.37.0 and updated in v1.62.0, this evaluates invitations between users, checking sender and recipient Matrix IDs.
- federated_user_may_invite: Added in v1.133.0, this callback evaluates federated invites and receives the full event, including stripped state in the unsigned section. Synapse runs this before falling through to
user_may_invite. - user_may_send_3pid_invite: Introduced in v1.45.0 and updated in v1.62.0, this inspects local invitations addressed to third-party identifiers like email addresses.
- user_may_create_room: Updated in v1.62.0 and expanded in v1.132.0 with a
room_configdictionary argument representing the request body, allowing you to reject room creation attempts. - user_may_create_room_alias and user_may_publish_room: Updated in v1.62.0, these callbacks regulate whether rooms can receive public aliases or appear in the homeserver's public directory.
- user_may_send_state_event: Introduced in v1.132.0 as an experimental callback to inspect state event submissions prior to inclusion.
Sources
Documentation referenced in this guide:
Published October 31, 2026. No affiliate links on this page. Facts come from the sources linked above; check them for updates.