diff --git a/keyserver/addons/rust-node-addon/rust-binding-types.js b/keyserver/addons/rust-node-addon/rust-binding-types.js
--- a/keyserver/addons/rust-node-addon/rust-binding-types.js
+++ b/keyserver/addons/rust-node-addon/rust-binding-types.js
@@ -21,7 +21,7 @@
     password: string,
     signedIdentityKeysBlob: SignedIdentityKeysBlob,
   ) => Promise<boolean>,
-  +addReservedUsername: (message: string, signature: string) => Promise<void>,
+  +addReservedUsernames: (message: string, signature: string) => Promise<void>,
   +removeReservedUsername: (
     message: string,
     signature: string,
diff --git a/keyserver/addons/rust-node-addon/src/identity_client/add_reserved_username.rs b/keyserver/addons/rust-node-addon/src/identity_client/add_reserved_usernames.rs
rename from keyserver/addons/rust-node-addon/src/identity_client/add_reserved_username.rs
rename to keyserver/addons/rust-node-addon/src/identity_client/add_reserved_usernames.rs
--- a/keyserver/addons/rust-node-addon/src/identity_client/add_reserved_username.rs
+++ b/keyserver/addons/rust-node-addon/src/identity_client/add_reserved_usernames.rs
@@ -10,11 +10,11 @@
   let channel = get_identity_service_channel().await?;
   let mut identity_client = IdentityClientServiceClient::new(channel);
 
-  let add_reserved_username_request =
-    AddReservedUsernameRequest { message, signature };
+  let add_reserved_usernames_request =
+    AddReservedUsernamesRequest { message, signature };
 
   identity_client
-    .add_reserved_username(add_reserved_username_request)
+    .add_reserved_usernames(add_reserved_usernames_request)
     .await
     .map_err(|e| Error::new(Status::GenericFailure, e.to_string()))?
     .into_inner();
diff --git a/keyserver/addons/rust-node-addon/src/identity_client/mod.rs b/keyserver/addons/rust-node-addon/src/identity_client/mod.rs
--- a/keyserver/addons/rust-node-addon/src/identity_client/mod.rs
+++ b/keyserver/addons/rust-node-addon/src/identity_client/mod.rs
@@ -1,4 +1,4 @@
-pub mod add_reserved_username;
+pub mod add_reserved_usernames;
 pub mod register_user;
 pub mod remove_reserved_username;
 pub mod identity_client {
@@ -7,7 +7,7 @@
 
 use identity_client::identity_client_service_client::IdentityClientServiceClient;
 use identity_client::{
-  AddReservedUsernameRequest, DeviceKeyUpload, IdentityKeyInfo,
+  AddReservedUsernamesRequest, DeviceKeyUpload, IdentityKeyInfo,
   RegistrationFinishRequest, RegistrationStartRequest,
   RemoveReservedUsernameRequest,
 };
diff --git a/services/identity/src/client_service.rs b/services/identity/src/client_service.rs
--- a/services/identity/src/client_service.rs
+++ b/services/identity/src/client_service.rs
@@ -6,14 +6,16 @@
 
 use crate::{
   client_service::client_proto::{
-    DeleteUserRequest, Empty, GenerateNonceResponse, InboundKeysForUserRequest,
+    AddReservedUsernamesRequest, DeleteUserRequest, Empty,
+    GenerateNonceResponse, InboundKeysForUserRequest,
     InboundKeysForUserResponse, OpaqueLoginFinishRequest,
     OpaqueLoginFinishResponse, OpaqueLoginStartRequest,
     OpaqueLoginStartResponse, OutboundKeysForUserRequest,
     OutboundKeysForUserResponse, OutboundKeyserverResponse,
     RefreshUserPreKeysRequest, RegistrationFinishRequest,
     RegistrationFinishResponse, RegistrationStartRequest,
-    RegistrationStartResponse, UpdateUserPasswordFinishRequest,
+    RegistrationStartResponse, RemoveReservedUsernameRequest,
+    ReservedRegistrationStartRequest, UpdateUserPasswordFinishRequest,
     UpdateUserPasswordStartRequest, UpdateUserPasswordStartResponse,
     UploadOneTimeKeysRequest, VerifyUserAccessTokenRequest,
     VerifyUserAccessTokenResponse, WalletLoginRequest, WalletLoginResponse,
@@ -23,7 +25,7 @@
   id::generate_uuid,
   nonce::generate_nonce_data,
   reserved_users::{
-    validate_add_reserved_username_message,
+    validate_add_reserved_usernames_message,
     validate_remove_reserved_username_message,
     validate_signed_account_ownership_message,
   },
@@ -41,11 +43,6 @@
 use tonic::Response;
 use tracing::{debug, error};
 
-use self::client_proto::{
-  AddReservedUsernameRequest, RemoveReservedUsernameRequest,
-  ReservedRegistrationStartRequest,
-};
-
 #[derive(Clone)]
 pub enum WorkflowInProgress {
   Registration(UserRegistrationInfo),
@@ -819,20 +816,33 @@
     Ok(response)
   }
 
-  async fn add_reserved_username(
+  async fn add_reserved_usernames(
     &self,
-    request: tonic::Request<AddReservedUsernameRequest>,
+    request: tonic::Request<AddReservedUsernamesRequest>,
   ) -> Result<tonic::Response<Empty>, tonic::Status> {
     let message = request.into_inner();
 
-    let username = validate_add_reserved_username_message(
+    let usernames = validate_add_reserved_usernames_message(
       &message.message,
       &message.signature,
     )?;
 
+    let mut filtered_usernames = Vec::new();
+
+    for username in usernames {
+      if !self
+        .client
+        .username_taken(username.clone())
+        .await
+        .map_err(handle_db_error)?
+      {
+        filtered_usernames.push(username);
+      }
+    }
+
     self
       .client
-      .add_username_to_reserved_usernames_table(username)
+      .add_usernames_to_reserved_usernames_table(filtered_usernames)
       .await
       .map_err(handle_db_error)?;
 
diff --git a/services/identity/src/database.rs b/services/identity/src/database.rs
--- a/services/identity/src/database.rs
+++ b/services/identity/src/database.rs
@@ -5,7 +5,7 @@
 use std::sync::Arc;
 
 use aws_config::SdkConfig;
-use aws_sdk_dynamodb::model::AttributeValue;
+use aws_sdk_dynamodb::model::{AttributeValue, PutRequest, WriteRequest};
 use aws_sdk_dynamodb::output::{
   DeleteItemOutput, GetItemOutput, PutItemOutput, QueryOutput,
 };
@@ -665,22 +665,58 @@
       .map_err(|e| Error::AwsSdk(e.into()))
   }
 
-  pub async fn add_username_to_reserved_usernames_table(
+  pub async fn add_usernames_to_reserved_usernames_table(
     &self,
-    username: String,
-  ) -> Result<PutItemOutput, Error> {
-    let item = HashMap::from([(
-      RESERVED_USERNAMES_TABLE_PARTITION_KEY.to_string(),
-      AttributeValue::S(username),
-    )]);
-    self
-      .client
-      .put_item()
-      .table_name(RESERVED_USERNAMES_TABLE)
-      .set_item(Some(item))
-      .send()
-      .await
-      .map_err(|e| Error::AwsSdk(e.into()))
+    usernames: Vec<String>,
+  ) -> Result<(), Error> {
+    let mut write_requests = vec![];
+
+    for username in usernames {
+      let item: HashMap<String, AttributeValue> = vec![(
+        RESERVED_USERNAMES_TABLE_PARTITION_KEY.to_string(),
+        AttributeValue::S(username),
+      )]
+      .into_iter()
+      .collect();
+
+      let write_request = WriteRequest::builder()
+        .put_request(PutRequest::builder().set_item(Some(item)).build())
+        .build();
+
+      write_requests.push(write_request);
+    }
+
+    loop {
+      let output = self
+        .client
+        .batch_write_item()
+        .request_items(RESERVED_USERNAMES_TABLE, write_requests)
+        .send()
+        .await
+        .map_err(|e| Error::AwsSdk(e.into()))?;
+
+      let unprocessed_items_map = match output.unprocessed_items() {
+        Some(map) => map,
+        None => break,
+      };
+
+      let unprocessed_requests =
+        match unprocessed_items_map.get(RESERVED_USERNAMES_TABLE) {
+          Some(requests) => requests,
+          None => break,
+        };
+
+      info!(
+        "{} unprocessed items, retrying...",
+        unprocessed_requests.len()
+      );
+
+      write_requests = unprocessed_requests.to_vec();
+    }
+
+    info!("Batch write item to reserved usernames table succeeded");
+
+    Ok(())
   }
 
   pub async fn delete_username_from_reserved_usernames_table(
diff --git a/services/identity/src/reserved_users.rs b/services/identity/src/reserved_users.rs
--- a/services/identity/src/reserved_users.rs
+++ b/services/identity/src/reserved_users.rs
@@ -9,22 +9,25 @@
 
 #[derive(Deserialize)]
 #[serde(rename_all = "camelCase")]
-struct ReservedUsernameMessage {
+struct Message<T> {
   statement: String,
-  username: String,
+  payload: T,
   issued_at: String,
 }
 
-fn validate_message(
+fn validate_and_decode_message<T: serde::de::DeserializeOwned>(
   keyserver_message: &str,
   keyserver_signature: &str,
-  statement: &[u8],
-) -> Result<ReservedUsernameMessage, Status> {
-  let deserialized_message: ReservedUsernameMessage =
+  expected_statement: &[u8],
+) -> Result<Message<T>, Status> {
+  let deserialized_message: Message<T> =
     serde_json::from_str(keyserver_message)
       .map_err(|_| Status::invalid_argument("message format invalid"))?;
 
-  if !constant_time_eq(deserialized_message.statement.as_bytes(), statement) {
+  if !constant_time_eq(
+    deserialized_message.statement.as_bytes(),
+    expected_statement,
+  ) {
     return Err(Status::invalid_argument("message invalid"));
   }
 
@@ -69,41 +72,41 @@
   keyserver_message: &str,
   keyserver_signature: &str,
 ) -> Result<(), Status> {
-  let deserialized_message = validate_message(
+  let deserialized_message = validate_and_decode_message::<String>(
     keyserver_message,
     keyserver_signature,
     b"This user is the owner of the following username",
   )?;
 
-  if deserialized_message.username != username {
+  if deserialized_message.payload != username {
     return Err(Status::invalid_argument("message invalid"));
   }
 
   Ok(())
 }
 
-pub fn validate_add_reserved_username_message(
+pub fn validate_add_reserved_usernames_message(
   keyserver_message: &str,
   keyserver_signature: &str,
-) -> Result<String, Status> {
-  let deserialized_message = validate_message(
+) -> Result<Vec<String>, Status> {
+  let deserialized_message = validate_and_decode_message::<Vec<String>>(
     keyserver_message,
     keyserver_signature,
-    b"Add the following username to reserved list",
+    b"Add the following usernames to reserved list",
   )?;
 
-  Ok(deserialized_message.username)
+  Ok(deserialized_message.payload)
 }
 
 pub fn validate_remove_reserved_username_message(
   keyserver_message: &str,
   keyserver_signature: &str,
 ) -> Result<String, Status> {
-  let deserialized_message = validate_message(
+  let deserialized_message = validate_and_decode_message::<String>(
     keyserver_message,
     keyserver_signature,
     b"Remove the following username from reserved list",
   )?;
 
-  Ok(deserialized_message.username)
+  Ok(deserialized_message.payload)
 }
diff --git a/shared/protos/identity_client.proto b/shared/protos/identity_client.proto
--- a/shared/protos/identity_client.proto
+++ b/shared/protos/identity_client.proto
@@ -71,7 +71,7 @@
 
   // Called by Ashoat's keyserver to add usernames to the Identity service's
   // reserved list
-  rpc AddReservedUsername(AddReservedUsernameRequest) returns (Empty) {}
+  rpc AddReservedUsernames(AddReservedUsernamesRequest) returns (Empty) {}
   // Called by Ashoat's keyserver to remove usernames from the Identity
   // service's reserved list
   rpc RemoveReservedUsername(RemoveReservedUsernameRequest) returns (Empty) {}
@@ -340,9 +340,9 @@
   bool tokenValid = 1;
 }
 
-// AddReservedUsername
+// AddReservedUsernames
 
-message AddReservedUsernameRequest {
+message AddReservedUsernamesRequest {
   // Message from Ashoat's keyserver containing the username to be added
   string message = 1;
   // Above message signed with Ashoat's keyserver's signing ed25519 key