diff --git a/trifid-api/src/main.rs b/trifid-api/src/main.rs
index 56797de..2be2a84 100644
--- a/trifid-api/src/main.rs
+++ b/trifid-api/src/main.rs
@@ -92,6 +92,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
             .service(routes::v1::roles::create_role_request)
             .service(routes::v1::roles::get_roles)
             .service(routes::v1::roles::get_role)
+            .service(routes::v1::roles::delete_role)
     }).bind(CONFIG.server.bind)?.run().await?;
 
     Ok(())
diff --git a/trifid-api/src/routes/v1/roles.rs b/trifid-api/src/routes/v1/roles.rs
index 33aa3d8..daa0277 100644
--- a/trifid-api/src/routes/v1/roles.rs
+++ b/trifid-api/src/routes/v1/roles.rs
@@ -25,6 +25,10 @@
 //#GET /v1/roles/{role_id} t+parity:full t+type:documented t+status:done
 // This endpoint has full parity with the original API. It has been recreated from the original API documentation.
 // This endpoint is considered done. No major features should be added or removed, unless it fixes bugs.
+//
+//#DELETE /v1/roles/{role_id} t+parity:full t+type:documented t+status:done
+// This endpoint has full parity with the original API. It has been recreated from the original API documentation.
+// This endpoint is considered done. No major features should be added or removed, unless it fixes bugs.
 
 use std::time::{SystemTime, UNIX_EPOCH};
 use actix_web::{get, HttpRequest, HttpResponse, post};
@@ -41,6 +45,7 @@ use trifid_api_entities::entity::firewall_rule;
 use trifid_api_entities::entity::role;
 use crate::cursor::Cursor;
 use crate::tokens::random_id;
+use actix_web::delete;
 
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct CreateRoleRequest {
@@ -653,4 +658,98 @@ pub struct GetRoleResponse {
     pub metadata: GetRoleResponseMetadata
 }
 #[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct GetRoleResponseMetadata {}
\ No newline at end of file
+pub struct GetRoleResponseMetadata {}
+
+#[delete("/v1/roles/{role_id}")]
+pub async fn delete_role(net: Path<String>, req_info: HttpRequest, db: Data<AppState>) -> HttpResponse {
+    // For this endpoint, you either need to be a fully authenticated user OR a token with roles:delete
+    let session_info = enforce_2fa(&req_info, &db.conn).await.unwrap_or(TokenInfo::NotPresent);
+    let api_token_info = enforce_api_token(&req_info, &["roles:delete"], &db.conn).await.unwrap_or(TokenInfo::NotPresent);
+
+    // If neither are present, throw an error
+    if matches!(session_info, TokenInfo::NotPresent) && matches!(api_token_info, TokenInfo::NotPresent) {
+        return HttpResponse::Unauthorized().json(APIErrorsResponse {
+            errors: vec![
+                APIError {
+                    code: "ERR_UNAUTHORIZED".to_string(),
+                    message: "This endpoint requires either a fully authenticated user or a token with the roles:delete scope".to_string(),
+                    path: None,
+                }
+            ],
+        })
+    }
+
+    // If both are present, throw an error
+    if matches!(session_info, TokenInfo::AuthToken(_)) && matches!(api_token_info, TokenInfo::ApiToken(_)) {
+        return HttpResponse::BadRequest().json(APIErrorsResponse {
+            errors: vec![
+                APIError {
+                    code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(),
+                    message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(),
+                    path: None
+                }
+            ],
+        })
+    }
+
+    let role: Option<role::Model> = match role::Entity::find().filter(role::Column::Id.eq(net.into_inner())).one(&db.conn).await {
+        Ok(r) => r,
+        Err(e) => {
+            error!("database error: {}", e);
+            return HttpResponse::InternalServerError().json(APIErrorsResponse {
+                errors: vec![
+                    APIError {
+                        code: "ERR_DB_ERROR".to_string(),
+                        message: "There was an error performing the database request, please try again later.".to_string(),
+                        path: None,
+                    }
+                ],
+            });
+        }
+    };
+
+    if let Some(role) = role {
+        match role.delete(&db.conn).await {
+            Ok(_) => (),
+            Err(e) => {
+                error!("database error: {}", e);
+                return HttpResponse::InternalServerError().json(APIErrorsResponse {
+                    errors: vec![
+                        APIError {
+                            code: "ERR_DB_ERROR".to_string(),
+                            message: "There was an error performing the database request, please try again later.".to_string(),
+                            path: None,
+                        }
+                    ],
+                });
+            }
+        };
+
+        HttpResponse::Ok().json(RoleDeleteResponse {
+            data: RoleDeleteResponseData {},
+            metadata: RoleDeleteResponseMetadata {},
+        })
+    } else {
+        HttpResponse::NotFound().json(APIErrorsResponse {
+            errors: vec![
+                APIError {
+                    code: "ERR_MISSING_ROLE".to_string(),
+                    message: "Role does not exist".to_string(),
+                    path: None,
+                }
+            ],
+        })
+    }
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct RoleDeleteResponse {
+    data: RoleDeleteResponseData,
+    metadata: RoleDeleteResponseMetadata
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct RoleDeleteResponseData {}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct RoleDeleteResponseMetadata {}
\ No newline at end of file