diff --git a/include/mtx/requests.hpp b/include/mtx/requests.hpp
index aa727ba840e6123a1da86244e70900221cd03d1e..29e3d6f8c59830430f67f647e47011b65ae19bd3 100644
--- a/include/mtx/requests.hpp
+++ b/include/mtx/requests.hpp
@@ -254,5 +254,48 @@ struct KeySignaturesUpload
 
 void
 to_json(json &obj, const KeySignaturesUpload &req);
+
+struct PusherData {
+        //! Required if `kind` is http. The URL to use to send notifications to.
+        //! MUST be an HTTPS URL with a path of /_matrix/push/v1/notify.
+        std::string url;
+        //! The format to send notifications in to Push Gateways if the kind is http.
+        //! The details about what fields the homeserver should send to the push gateway are
+        //! defined in the Push Gateway Specification. Currently the only format available is
+        //! 'event_id_only'.
+        std::string format;
+};
+
+void
+to_json(json &obj, const PusherData &data);
+
+//! Request payload for the `POST /_matrix/client/r0/pushers/set` endpoint.
+struct SetPusher {
+        //! Required. Unique identifier for this pusher.
+        std::string pushkey;
+        //! Required. The kind of pusher to configure. "http" makes a pusher that sends HTTP pokes.
+        //! "email" makes a pusher that emails the user with unread notifications.
+        //! null deletes the pusher.
+        std::string kind;
+        //! Required. This is a reverse-DNS style identifier for the application.
+        //! If the `kind` is "email", this is "m.email".
+        std::string app_id;
+        //! Required. A string that will allow the user to identify what application owns this pusher.
+        std::string app_display_name;
+        //! Required. A string that will allow the user to identify what device owns this pusher.
+        std::string device_display_name;
+        //! Determines which set of device specific rules this pusher executes.
+        std::string profile_tag;
+        //! Required. The preferred language for receiving notifications.
+        std::string lang;
+        //! Required. Data for the pusher implementation (for example, if `kind` is `http`, includes
+        //! the URL to push to).
+        PusherData data;
+        //! If true, add another pusher instead of updating an existing one.
+        bool append = false;
+};
+
+void
+to_json(json &obj, const SetPusher &req);
 } // namespace requests
 } // namespace mtx
diff --git a/include/mtxclient/http/client.hpp b/include/mtxclient/http/client.hpp
index 745f33e9cdd5608b0e4f727aabe6e6cc3c73c7a2..67987f807801a137939c326b5b8e18c93a41e3bd 100644
--- a/include/mtxclient/http/client.hpp
+++ b/include/mtxclient/http/client.hpp
@@ -49,6 +49,8 @@ struct ClaimKeys;
 struct UploadKeys;
 struct PublicRoomVisibility;
 struct PublicRooms;
+struct PushersData;
+struct SetPushers;
 }
 namespace responses {
 struct AvatarUrl;
@@ -558,6 +560,10 @@ public:
         //! Gets any TURN server URIs and authentication credentials
         void get_turn_server(Callback<mtx::responses::TurnServer> cb);
 
+        //! Sets, updates, or deletes a pusher
+        void set_pusher(const mtx::requests::SetPusher &req,
+                        Callback<mtx::responses::Empty> cb);
+
 private:
         template<class Request, class Response>
         void post(const std::string &endpoint,
diff --git a/lib/http/client.cpp b/lib/http/client.cpp
index 8595e53e087cc4c1daca0064cc397f9b5b1d6353..8b8c7aa4e8a670230362323954e6879389a60fac 100644
--- a/lib/http/client.cpp
+++ b/lib/http/client.cpp
@@ -1266,6 +1266,12 @@ Client::get_turn_server(Callback<mtx::responses::TurnServer> cb)
                                              RequestErr err) { cb(res, err); });
 }
 
+void
+Client::set_pusher(const mtx::requests::SetPusher &req, Callback<mtx::responses::Empty> cb)
+{
+        post<mtx::requests::SetPusher, mtx::responses::Empty>("/client/r0/pushers/set", req, cb);
+}
+
 // Template instantiations for the various send functions
 
 #define MTXCLIENT_SEND_STATE_EVENT(Content)                                                        \
diff --git a/lib/structs/requests.cpp b/lib/structs/requests.cpp
index 819791ee79faf482efc77fa828154d22da29a4d3..f23d67db5cef97b4b8c108f16957f46c6be82a61 100644
--- a/lib/structs/requests.cpp
+++ b/lib/structs/requests.cpp
@@ -187,5 +187,33 @@ to_json(json &obj, const KeySignaturesUpload &req)
                           std::visit([](const auto &e) { return json(e); }, keyVar);
 }
 
+
+void
+to_json(json &obj, const PusherData &data)
+{
+        if (!data.url.empty()) {
+                obj["url"] = data.url;
+        }
+        if (!data.format.empty()) {
+                obj["format"] = data.format;
+        }
+}
+
+void
+to_json(json &obj, const SetPusher &req)
+{
+        obj["pushkey"] = req.pushkey;
+        obj["kind"] = req.kind;
+        obj["app_id"] = req.app_id;
+        obj["app_display_name"] = req.app_display_name;
+        obj["device_display_name"] = req.device_display_name;
+        if (!req.profile_tag.empty()) {
+                obj["profile_tag"] = req.profile_tag;
+        }
+        obj["lang"] = req.lang;
+        obj["data"] = req.data;
+        obj["append"] = req.append;
+}
+
 } // namespace requests
 } // namespace mtx