diff --git a/src/client_server.rs b/src/client_server.rs
index e5a6df8d..16bf73d5 100644
--- a/src/client_server.rs
+++ b/src/client_server.rs
@@ -477,13 +477,15 @@ pub fn deactivate_route(
         };
 
         db.rooms.append_pdu(
-            room_id.clone(),
-            sender_id.clone(),
-            EventType::RoomMember,
-            serde_json::to_value(event).expect("event is valid, we just created it"),
-            None,
-            Some(sender_id.to_string()),
-            None,
+            PduBuilder {
+                room_id: room_id.clone(),
+                sender: sender_id.clone(),
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(event).expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(sender_id.to_string()),
+                redacts: None,
+            },
             &db.globals,
         )?;
     }
@@ -662,30 +664,32 @@ pub fn set_displayname_route(
     for room_id in db.rooms.rooms_joined(&sender_id) {
         let room_id = room_id?;
         db.rooms.append_pdu(
-            room_id.clone(),
-            sender_id.clone(),
-            EventType::RoomMember,
-            serde_json::to_value(ruma::events::room::member::MemberEventContent {
-                displayname: body.displayname.clone(),
-                ..serde_json::from_value::<Raw<_>>(
-                    db.rooms
-                        .room_state_get(&room_id, &EventType::RoomMember, &sender_id.to_string())?
-                        .ok_or_else(|| {
-                            Error::bad_database(
-                                "Tried to send displayname update for user not in the room.",
-                            )
-                        })?
-                        .content
-                        .clone(),
-                )
-                .expect("from_value::<Raw<..>> can never fail")
-                .deserialize()
-                .map_err(|_| Error::bad_database("Database contains invalid PDU."))?
-            })
-            .expect("event is valid, we just created it"),
-            None,
-            Some(sender_id.to_string()),
-            None,
+            PduBuilder {
+                room_id: room_id.clone(),
+                sender: sender_id.clone(),
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(ruma::events::room::member::MemberEventContent {
+                    displayname: body.displayname.clone(),
+                    ..serde_json::from_value::<Raw<_>>(
+                        db.rooms
+                            .room_state_get(&room_id, &EventType::RoomMember, &sender_id.to_string())?
+                            .ok_or_else(|| {
+                                Error::bad_database(
+                                    "Tried to send displayname update for user not in the room.",
+                                )
+                            })?
+                            .content
+                            .clone(),
+                    )
+                    .expect("from_value::<Raw<..>> can never fail")
+                    .deserialize()
+                    .map_err(|_| Error::bad_database("Database contains invalid PDU."))?
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(sender_id.to_string()),
+                redacts: None,
+            },
             &db.globals,
         )?;
 
@@ -758,30 +762,32 @@ pub fn set_avatar_url_route(
     for room_id in db.rooms.rooms_joined(&sender_id) {
         let room_id = room_id?;
         db.rooms.append_pdu(
-            room_id.clone(),
-            sender_id.clone(),
-            EventType::RoomMember,
-            serde_json::to_value(ruma::events::room::member::MemberEventContent {
-                avatar_url: body.avatar_url.clone(),
-                ..serde_json::from_value::<Raw<_>>(
-                    db.rooms
-                        .room_state_get(&room_id, &EventType::RoomMember, &sender_id.to_string())?
-                        .ok_or_else(|| {
-                            Error::bad_database(
-                                "Tried to send avatar url update for user not in the room.",
-                            )
-                        })?
-                        .content
-                        .clone(),
-                )
-                .expect("from_value::<Raw<..>> can never fail")
-                .deserialize()
-                .map_err(|_| Error::bad_database("Database contains invalid PDU."))?
-            })
-            .expect("event is valid, we just created it"),
-            None,
-            Some(sender_id.to_string()),
-            None,
+            PduBuilder {
+                room_id: room_id.clone(),
+                sender: sender_id.clone(),
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(ruma::events::room::member::MemberEventContent {
+                    avatar_url: body.avatar_url.clone(),
+                    ..serde_json::from_value::<Raw<_>>(
+                        db.rooms
+                            .room_state_get(&room_id, &EventType::RoomMember, &sender_id.to_string())?
+                            .ok_or_else(|| {
+                                Error::bad_database(
+                                    "Tried to send avatar url update for user not in the room.",
+                                )
+                            })?
+                            .content
+                            .clone(),
+                    )
+                    .expect("from_value::<Raw<..>> can never fail")
+                    .deserialize()
+                    .map_err(|_| Error::bad_database("Database contains invalid PDU."))?
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(sender_id.to_string()),
+                redacts: None,
+            },
             &db.globals,
         )?;
 
@@ -1294,32 +1300,36 @@ pub fn create_room_route(
 
     // 1. The room create event
     db.rooms.append_pdu(
-        room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomCreate,
-        serde_json::to_value(content).expect("event is valid, we just created it"),
-        None,
-        Some("".to_owned()),
-        None,
+        PduBuilder {
+            room_id: room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomCreate,
+            content: serde_json::to_value(content).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
     // 2. Let the room creator join
     db.rooms.append_pdu(
-        room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomMember,
-        serde_json::to_value(member::MemberEventContent {
-            membership: member::MembershipState::Join,
-            displayname: db.users.displayname(&sender_id)?,
-            avatar_url: db.users.avatar_url(&sender_id)?,
-            is_direct: body.is_direct,
-            third_party_invite: None,
-        })
-        .expect("event is valid, we just created it"),
-        None,
-        Some(sender_id.to_string()),
-        None,
+        PduBuilder {
+            room_id: room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(member::MemberEventContent {
+                membership: member::MembershipState::Join,
+                displayname: db.users.displayname(&sender_id)?,
+                avatar_url: db.users.avatar_url(&sender_id)?,
+                is_direct: body.is_direct,
+                third_party_invite: None,
+            })
+            .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(sender_id.to_string()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
@@ -1359,72 +1369,82 @@ pub fn create_room_route(
         .expect("event is valid, we just created it")
     };
     db.rooms.append_pdu(
-        room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomPowerLevels,
-        power_levels_content,
-        None,
-        Some("".to_owned()),
-        None,
+        PduBuilder {
+            room_id: room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomPowerLevels,
+            content: power_levels_content,
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
     // 4. Events set by preset
     // 4.1 Join Rules
     db.rooms.append_pdu(
-        room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomJoinRules,
-        match preset {
-            create_room::RoomPreset::PublicChat => serde_json::to_value(
-                join_rules::JoinRulesEventContent::new(join_rules::JoinRule::Public),
-            )
-            .expect("event is valid, we just created it"),
-            // according to spec "invite" is the default
-            _ => serde_json::to_value(join_rules::JoinRulesEventContent::new(
-                join_rules::JoinRule::Invite,
-            ))
-            .expect("event is valid, we just created it"),
+        PduBuilder {
+            room_id: room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomJoinRules,
+            content: match preset {
+                create_room::RoomPreset::PublicChat => serde_json::to_value(
+                    join_rules::JoinRulesEventContent::new(join_rules::JoinRule::Public),
+                )
+                .expect("event is valid, we just created it"),
+                // according to spec "invite" is the default
+                _ => serde_json::to_value(join_rules::JoinRulesEventContent::new(
+                    join_rules::JoinRule::Invite,
+                ))
+                .expect("event is valid, we just created it"),
+            },
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
         },
-        None,
-        Some("".to_owned()),
-        None,
         &db.globals,
     )?;
 
     // 4.2 History Visibility
     db.rooms.append_pdu(
-        room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomHistoryVisibility,
-        serde_json::to_value(history_visibility::HistoryVisibilityEventContent::new(
-            history_visibility::HistoryVisibility::Shared,
-        ))
-        .expect("event is valid, we just created it"),
-        None,
-        Some("".to_owned()),
-        None,
+        PduBuilder {
+            room_id: room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomHistoryVisibility,
+            content: serde_json::to_value(history_visibility::HistoryVisibilityEventContent::new(
+                history_visibility::HistoryVisibility::Shared,
+            ))
+            .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
     // 4.3 Guest Access
     db.rooms.append_pdu(
-        room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomGuestAccess,
-        match preset {
-            create_room::RoomPreset::PublicChat => serde_json::to_value(
-                guest_access::GuestAccessEventContent::new(guest_access::GuestAccess::Forbidden),
-            )
-            .expect("event is valid, we just created it"),
-            _ => serde_json::to_value(guest_access::GuestAccessEventContent::new(
-                guest_access::GuestAccess::CanJoin,
-            ))
-            .expect("event is valid, we just created it"),
+        PduBuilder {
+            room_id: room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomGuestAccess,
+            content: match preset {
+                create_room::RoomPreset::PublicChat => {
+                    serde_json::to_value(guest_access::GuestAccessEventContent::new(
+                        guest_access::GuestAccess::Forbidden,
+                    ))
+                    .expect("event is valid, we just created it")
+                }
+                _ => serde_json::to_value(guest_access::GuestAccessEventContent::new(
+                    guest_access::GuestAccess::CanJoin,
+                ))
+                .expect("event is valid, we just created it"),
+            },
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
         },
-        None,
-        Some("".to_owned()),
-        None,
         &db.globals,
     )?;
 
@@ -1441,15 +1461,17 @@ pub fn create_room_route(
         }
 
         db.rooms.append_pdu(
-            room_id.clone(),
-            sender_id.clone(),
-            event_type.clone(),
-            serde_json::from_str(content.get()).map_err(|_| {
-                Error::BadRequest(ErrorKind::BadJson, "Invalid initial_state content.")
-            })?,
-            None,
-            state_key.clone(),
-            None,
+            PduBuilder {
+                room_id: room_id.clone(),
+                sender: sender_id.clone(),
+                event_type: event_type.clone(),
+                content: serde_json::from_str(content.get()).map_err(|_| {
+                    Error::BadRequest(ErrorKind::BadJson, "Invalid initial_state content.")
+                })?,
+                unsigned: None,
+                state_key: state_key.clone(),
+                redacts: None,
+            },
             &db.globals,
         )?;
     }
@@ -1457,33 +1479,38 @@ pub fn create_room_route(
     // 6. Events implied by name and topic
     if let Some(name) = &body.name {
         db.rooms.append_pdu(
-            room_id.clone(),
-            sender_id.clone(),
-            EventType::RoomName,
-            serde_json::to_value(
-                name::NameEventContent::new(name.clone())
-                    .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Name is invalid."))?,
-            )
-            .expect("event is valid, we just created it"),
-            None,
-            Some("".to_owned()),
-            None,
+            PduBuilder {
+                room_id: room_id.clone(),
+                sender: sender_id.clone(),
+                event_type: EventType::RoomName,
+                content: serde_json::to_value(
+                    name::NameEventContent::new(name.clone()).map_err(|_| {
+                        Error::BadRequest(ErrorKind::InvalidParam, "Name is invalid.")
+                    })?,
+                )
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
             &db.globals,
         )?;
     }
 
     if let Some(topic) = &body.topic {
         db.rooms.append_pdu(
-            room_id.clone(),
-            sender_id.clone(),
-            EventType::RoomTopic,
-            serde_json::to_value(topic::TopicEventContent {
-                topic: topic.clone(),
-            })
-            .expect("event is valid, we just created it"),
-            None,
-            Some("".to_owned()),
-            None,
+            PduBuilder {
+                room_id: room_id.clone(),
+                sender: sender_id.clone(),
+                event_type: EventType::RoomTopic,
+                content: serde_json::to_value(topic::TopicEventContent {
+                    topic: topic.clone(),
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
             &db.globals,
         )?;
     }
@@ -1491,20 +1518,22 @@ pub fn create_room_route(
     // 7. Events implied by invite (and TODO: invite_3pid)
     for user in &body.invite {
         db.rooms.append_pdu(
-            room_id.clone(),
-            sender_id.clone(),
-            EventType::RoomMember,
-            serde_json::to_value(member::MemberEventContent {
-                membership: member::MembershipState::Invite,
-                displayname: db.users.displayname(&user)?,
-                avatar_url: db.users.avatar_url(&user)?,
-                is_direct: body.is_direct,
-                third_party_invite: None,
-            })
-            .expect("event is valid, we just created it"),
-            None,
-            Some(user.to_string()),
-            None,
+            PduBuilder {
+                room_id: room_id.clone(),
+                sender: sender_id.clone(),
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(member::MemberEventContent {
+                    membership: member::MembershipState::Invite,
+                    displayname: db.users.displayname(&user)?,
+                    avatar_url: db.users.avatar_url(&user)?,
+                    is_direct: body.is_direct,
+                    third_party_invite: None,
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(user.to_string()),
+                redacts: None,
+            },
             &db.globals,
         )?;
     }
@@ -1552,16 +1581,18 @@ pub fn redact_event_route(
     let sender_id = body.sender_id.as_ref().expect("user is authenticated");
 
     let event_id = db.rooms.append_pdu(
-        body.room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomRedaction,
-        serde_json::to_value(redaction::RedactionEventContent {
-            reason: body.reason.clone(),
-        })
-        .expect("event is valid, we just created it"),
-        None,
-        None,
-        Some(body.event_id.clone()),
+        PduBuilder {
+            room_id: body.room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomRedaction,
+            content: serde_json::to_value(redaction::RedactionEventContent {
+                reason: body.reason.clone(),
+            })
+            .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: None,
+            redacts: Some(body.event_id.clone()),
+        },
         &db.globals,
     )?;
 
@@ -1647,13 +1678,15 @@ pub fn join_room_by_id_route(
     };
 
     db.rooms.append_pdu(
-        body.room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomMember,
-        serde_json::to_value(event).expect("event is valid, we just created it"),
-        None,
-        Some(sender_id.to_string()),
-        None,
+        PduBuilder {
+            room_id: body.room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(sender_id.to_string()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
@@ -1724,13 +1757,15 @@ pub fn leave_room_route(
     event.membership = member::MembershipState::Leave;
 
     db.rooms.append_pdu(
-        body.room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomMember,
-        serde_json::to_value(event).expect("event is valid, we just created it"),
-        None,
-        Some(sender_id.to_string()),
-        None,
+        PduBuilder {
+            room_id: body.room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(sender_id.to_string()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
@@ -1768,13 +1803,15 @@ pub fn kick_user_route(
     // TODO: reason
 
     db.rooms.append_pdu(
-        body.room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomMember,
-        serde_json::to_value(event).expect("event is valid, we just created it"),
-        None,
-        Some(body.user_id.to_string()),
-        None,
+        PduBuilder {
+            room_id: body.room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(body.user_id.to_string()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
@@ -1859,13 +1896,15 @@ pub fn ban_user_route(
         )?;
 
     db.rooms.append_pdu(
-        body.room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomMember,
-        serde_json::to_value(event).expect("event is valid, we just created it"),
-        None,
-        Some(body.user_id.to_string()),
-        None,
+        PduBuilder {
+            room_id: body.room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(body.user_id.to_string()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
@@ -1902,13 +1941,15 @@ pub fn unban_user_route(
     event.membership = ruma::events::room::member::MembershipState::Leave;
 
     db.rooms.append_pdu(
-        body.room_id.clone(),
-        sender_id.clone(),
-        EventType::RoomMember,
-        serde_json::to_value(event).expect("event is valid, we just created it"),
-        None,
-        Some(body.user_id.to_string()),
-        None,
+        PduBuilder {
+            room_id: body.room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(body.user_id.to_string()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
@@ -1942,20 +1983,22 @@ pub fn invite_user_route(
 
     if let invite_user::InvitationRecipient::UserId { user_id } = &body.recipient {
         db.rooms.append_pdu(
-            body.room_id.clone(),
-            sender_id.clone(),
-            EventType::RoomMember,
-            serde_json::to_value(member::MemberEventContent {
-                membership: member::MembershipState::Invite,
-                displayname: db.users.displayname(&user_id)?,
-                avatar_url: db.users.avatar_url(&user_id)?,
-                is_direct: None,
-                third_party_invite: None,
-            })
-            .expect("event is valid, we just created it"),
-            None,
-            Some(user_id.to_string()),
-            None,
+            PduBuilder {
+                room_id: body.room_id.clone(),
+                sender: sender_id.clone(),
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(member::MemberEventContent {
+                    membership: member::MembershipState::Invite,
+                    displayname: db.users.displayname(&user_id)?,
+                    avatar_url: db.users.avatar_url(&user_id)?,
+                    is_direct: None,
+                    third_party_invite: None,
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(user_id.to_string()),
+                redacts: None,
+            },
             &db.globals,
         )?;
 
@@ -2327,18 +2370,20 @@ pub fn create_message_event_route(
     unsigned.insert("transaction_id".to_owned(), body.txn_id.clone().into());
 
     let event_id = db.rooms.append_pdu(
-        body.room_id.clone(),
-        sender_id.clone(),
-        body.event_type.clone(),
-        serde_json::from_str(
-            body.json_body
-                .ok_or(Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?
-                .get(),
-        )
-        .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
-        Some(unsigned),
-        None,
-        None,
+        PduBuilder {
+            room_id: body.room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: body.event_type.clone(),
+            content: serde_json::from_str(
+                body.json_body
+                    .ok_or(Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?
+                    .get(),
+            )
+            .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
+            unsigned: Some(unsigned),
+            state_key: None,
+            redacts: None,
+        },
         &db.globals,
     )?;
 
@@ -2395,13 +2440,15 @@ pub fn create_state_event_for_key_route(
     }
 
     let event_id = db.rooms.append_pdu(
-        body.room_id.clone(),
-        sender_id.clone(),
-        body.event_type.clone(),
-        content,
-        None,
-        Some(body.state_key.clone()),
-        None,
+        PduBuilder {
+            room_id: body.room_id.clone(),
+            sender: sender_id.clone(),
+            event_type: body.event_type.clone(),
+            content,
+            unsigned: None,
+            state_key: Some(body.state_key.clone()),
+            redacts: None,
+        },
         &db.globals,
     )?;
 
@@ -2696,7 +2743,7 @@ pub async fn sync_events_route(
                     // Filter for possible heroes
                     .filter_map(|u| u)
                 {
-                    if heroes.contains(&hero) || hero == sender_id.to_string() {
+                    if heroes.contains(&hero) || hero == sender_id.as_str() {
                         continue;
                     }
 
@@ -2796,7 +2843,7 @@ pub async fn sync_events_route(
                 notification_count,
             },
             timeline: sync_events::Timeline {
-                limited: false || joined_since_last_sync,
+                limited: joined_since_last_sync,
                 prev_batch,
                 events: room_events,
             },
@@ -2984,7 +3031,7 @@ pub async fn sync_events_route(
     {
         // Hang a few seconds so requests are not spammed
         // Stop hanging if new info arrives
-        let mut duration = body.timeout.unwrap_or(Duration::default());
+        let mut duration = body.timeout.unwrap_or_default();
         if duration.as_secs() > 30 {
             duration = Duration::from_secs(30);
         }
diff --git a/src/database.rs b/src/database.rs
index 536978ef..e3d003c3 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -125,7 +125,7 @@ impl Database {
         })
     }
 
-    pub async fn watch(&self, user_id: &UserId, device_id: &DeviceId) -> () {
+    pub async fn watch(&self, user_id: &UserId, device_id: &DeviceId) {
         let mut userid_prefix = user_id.to_string().as_bytes().to_vec();
         userid_prefix.push(0xff);
         let mut userdeviceid_prefix = userid_prefix.clone();
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index ae0d0849..9ff11b89 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -2,7 +2,7 @@ mod edus;
 
 pub use edus::RoomEdus;
 
-use crate::{utils, Error, PduEvent, Result};
+use crate::{pdu::PduBuilder, utils, Error, PduEvent, Result};
 use log::error;
 use ruma::{
     api::client::error::ErrorKind,
@@ -250,18 +250,21 @@ impl Rooms {
     }
 
     /// Creates a new persisted data unit and adds it to a room.
-    #[allow(clippy::too_many_arguments, clippy::blocks_in_if_conditions)]
+    #[allow(clippy::blocks_in_if_conditions)]
     pub fn append_pdu(
         &self,
-        room_id: RoomId,
-        sender: UserId,
-        event_type: EventType,
-        content: serde_json::Value,
-        unsigned: Option<serde_json::Map<String, serde_json::Value>>,
-        state_key: Option<String>,
-        redacts: Option<EventId>,
+        pdu_builder: PduBuilder,
         globals: &super::globals::Globals,
     ) -> Result<EventId> {
+        let PduBuilder {
+            room_id,
+            sender,
+            event_type,
+            content,
+            unsigned,
+            state_key,
+            redacts,
+        } = pdu_builder;
         // TODO: Make sure this isn't called twice in parallel
         let prev_events = self.get_pdu_leaves(&room_id)?;
 
@@ -623,7 +626,7 @@ impl Rooms {
         let mut first_pdu_id = prefix.clone();
         first_pdu_id.extend_from_slice(&(since + 1).to_be_bytes());
 
-        let mut last_pdu_id = prefix.clone();
+        let mut last_pdu_id = prefix;
         last_pdu_id.extend_from_slice(&u64::MAX.to_be_bytes());
 
         let user_id = user_id.clone();
diff --git a/src/pdu.rs b/src/pdu.rs
index c1492970..b689a3ee 100644
--- a/src/pdu.rs
+++ b/src/pdu.rs
@@ -175,3 +175,15 @@ impl PduEvent {
         serde_json::from_value(json).expect("Raw::from_value always works")
     }
 }
+
+/// Build the start of a PDU in order to add it to the `Database`.
+#[derive(Debug)]
+pub struct PduBuilder {
+    pub room_id: RoomId,
+    pub sender: UserId,
+    pub event_type: EventType,
+    pub content: serde_json::Value,
+    pub unsigned: Option<serde_json::Map<String, serde_json::Value>>,
+    pub state_key: Option<String>,
+    pub redacts: Option<EventId>,
+}