diff --git a/trifid-api/src/routes/v1/hosts.rs b/trifid-api/src/routes/v1/hosts.rs
index ec14f2e..59d96b7 100644
--- a/trifid-api/src/routes/v1/hosts.rs
+++ b/trifid-api/src/routes/v1/hosts.rs
@@ -195,7 +195,7 @@ pub async fn get_hosts(
                 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
+                    path: None,
                 }
             ],
         });
@@ -238,7 +238,7 @@ pub async fn get_hosts(
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -417,7 +417,7 @@ pub async fn get_hosts(
                 match (Cursor {
                     page: cursor.page - 1,
                 })
-                .try_into()
+                    .try_into()
                 {
                     Ok(r) => Some(r),
                     Err(_) => None,
@@ -429,7 +429,7 @@ pub async fn get_hosts(
                 match (Cursor {
                     page: cursor.page + 1,
                 })
-                .try_into()
+                    .try_into()
                 {
                     Ok(r) => Some(r),
                     Err(_) => None,
@@ -515,7 +515,7 @@ pub async fn create_hosts_request(
                 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
+                    path: None,
                 }
             ],
         });
@@ -558,7 +558,7 @@ pub async fn create_hosts_request(
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -639,7 +639,7 @@ pub async fn create_hosts_request(
             errors: vec![APIError {
                 code: "ERR_INVALID_VALUE".to_string(),
                 message: "lighthouse hosts must specify a static listen port".to_string(),
-                path: Some("listenPort".to_string())
+                path: Some("listenPort".to_string()),
             }]
         });
     } else if req.listen_port == 0 && req.is_relay {
@@ -647,7 +647,7 @@ pub async fn create_hosts_request(
             errors: vec![APIError {
                 code: "ERR_INVALID_VALUE".to_string(),
                 message: "relay hosts must specify a static listen port".to_string(),
-                path: Some("listenPort".to_string())
+                path: Some("listenPort".to_string()),
             }]
         });
     }
@@ -857,6 +857,7 @@ pub struct GetHostResponse {
     pub data: HostResponse,
     pub metadata: GetHostResponseMetadata,
 }
+
 #[derive(Serialize, Deserialize)]
 pub struct GetHostResponseMetadata {}
 
@@ -894,7 +895,7 @@ pub async fn get_host(id: Path<String>, req_info: HttpRequest, db: Data<AppState
                 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
+                    path: None,
                 }
             ],
         });
@@ -937,7 +938,7 @@ pub async fn get_host(id: Path<String>, req_info: HttpRequest, db: Data<AppState
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -1006,11 +1007,11 @@ pub async fn get_host(id: Path<String>, req_info: HttpRequest, db: Data<AppState
                 errors: vec![APIError {
                     code: "ERR_NOT_FOUND".to_string(),
                     message:
-                        "resource not found"
-                            .to_string(),
+                    "resource not found"
+                        .to_string(),
                     path: None,
                 }],
-            })
+            });
         }
     };
 
@@ -1086,8 +1087,10 @@ pub struct DeleteHostResponse {
     pub data: DeleteHostData,
     pub metadata: DeleteHostMetadata,
 }
+
 #[derive(Serialize, Deserialize)]
 pub struct DeleteHostData {}
+
 #[derive(Serialize, Deserialize)]
 pub struct DeleteHostMetadata {}
 
@@ -1129,7 +1132,7 @@ pub async fn delete_host(
                 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
+                    path: None,
                 }
             ],
         });
@@ -1172,7 +1175,7 @@ pub async fn delete_host(
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -1241,11 +1244,11 @@ pub async fn delete_host(
                 errors: vec![APIError {
                     code: "ERR_NOT_FOUND".to_string(),
                     message:
-                        "resource not found"
-                            .to_string(),
+                    "resource not found"
+                        .to_string(),
                     path: None,
                 }],
-            })
+            });
         }
     };
 
@@ -1303,8 +1306,8 @@ pub async fn delete_host(
                     errors: vec![APIError {
                         code: "ERR_DB_ERROR".to_string(),
                         message:
-                            "There was an error with the database query. Please try again later."
-                                .to_string(),
+                        "There was an error with the database query. Please try again later."
+                            .to_string(),
                         path: None,
                     }],
                 });
@@ -1329,7 +1332,7 @@ pub struct EditHostRequest {
     // t+features:extended_hosts
     pub ip: Option<Ipv4Addr>,
     // t+features:extended_hosts
-    pub role: Option<String>
+    pub role: Option<String>,
 }
 
 #[derive(Serialize, Deserialize)]
@@ -1386,7 +1389,7 @@ pub async fn edit_host(
                 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
+                    path: None,
                 }
             ],
         });
@@ -1429,7 +1432,7 @@ pub async fn edit_host(
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -1498,11 +1501,11 @@ pub async fn edit_host(
                 errors: vec![APIError {
                     code: "ERR_NOT_FOUND".to_string(),
                     message:
-                        "resource not found"
-                            .to_string(),
+                    "resource not found"
+                        .to_string(),
                     path: None,
                 }],
-            })
+            });
         }
     };
 
@@ -1545,8 +1548,8 @@ pub async fn edit_host(
                     errors: vec![APIError {
                         code: "ERR_DB_ERROR".to_string(),
                         message:
-                            "There was an error with the database query. Please try again later."
-                                .to_string(),
+                        "There was an error with the database query. Please try again later."
+                            .to_string(),
                         path: None,
                     }],
                 });
@@ -1713,7 +1716,7 @@ pub async fn block_host(
                 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
+                    path: None,
                 }
             ],
         });
@@ -1756,7 +1759,7 @@ pub async fn block_host(
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -1825,11 +1828,11 @@ pub async fn block_host(
                 errors: vec![APIError {
                     code: "ERR_NOT_FOUND".to_string(),
                     message:
-                        "resource not found"
-                            .to_string(),
+                    "resource not found"
+                        .to_string(),
                     path: None,
                 }],
-            })
+            });
         }
     };
 
@@ -1980,7 +1983,7 @@ pub async fn enroll_host(
                 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
+                    path: None,
                 }
             ],
         });
@@ -2023,7 +2026,7 @@ pub async fn enroll_host(
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -2092,11 +2095,11 @@ pub async fn enroll_host(
                 errors: vec![APIError {
                     code: "ERR_NOT_FOUND".to_string(),
                     message:
-                        "resource not found"
-                            .to_string(),
+                    "resource not found"
+                        .to_string(),
                     path: None,
                 }],
-            })
+            });
         }
     };
 
@@ -2198,7 +2201,7 @@ pub async fn create_host_and_enrollment_code(
                 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
+                    path: None,
                 }
             ],
         });
@@ -2224,8 +2227,8 @@ pub async fn create_host_and_enrollment_code(
                     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(),
+                                code: "ERR_MISSING_ORG".to_string(),
+                                message: "The server was unable to locate your organization, please try again later or report this to your administrator if it continues to fail.".to_string(),
                                 path: None,
                             }
                         ],
@@ -2241,7 +2244,7 @@ pub async fn create_host_and_enrollment_code(
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -2262,9 +2265,9 @@ pub async fn create_host_and_enrollment_code(
             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,
+                        code: "ERR_INVALID_REFERENCE".to_string(),
+                        message: "referenced value is invalid (perhaps it does not exist?)".to_string(),
+                        path: Some("networkID".to_string()),
                     }
                 ],
             });
@@ -2276,10 +2279,9 @@ pub async fn create_host_and_enrollment_code(
     } else {
         return HttpResponse::Unauthorized().json(APIErrorsResponse {
             errors: vec![APIError {
-                code: "ERR_NO_NET".to_string(),
-                message: "This user does not own any networks. Try using an API token instead."
-                    .to_string(),
-                path: None,
+                code: "ERR_INVALID_REFERENCE".to_string(),
+                message: "referenced value is invalid (perhaps it does not exist?)".to_string(),
+                path: Some("networkID".to_string()),
             }],
         });
     }
@@ -2288,9 +2290,9 @@ pub async fn create_host_and_enrollment_code(
         return HttpResponse::Unauthorized().json(APIErrorsResponse {
             errors: vec![
                 APIError {
-                    code: "ERR_WRONG_NET".to_string(),
-                    message: "The network on the request does not match the network associated with this token or user.".to_string(),
-                    path: None
+                    code: "ERR_INVALID_REFERENCE".to_string(),
+                    message: "referenced value is invalid (perhaps it does not exist?)".to_string(),
+                    path: Some("networkID".to_string()),
                 }
             ],
         });
@@ -2299,8 +2301,8 @@ pub async fn create_host_and_enrollment_code(
     if req.is_lighthouse && req.is_relay {
         return HttpResponse::BadRequest().json(APIErrorsResponse {
             errors: vec![APIError {
-                code: "ERR_CANNOT_BE_RELAY_AND_LIGHTHOUSE".to_string(),
-                message: "A host cannot be a relay and a lighthouse at the same time.".to_string(),
+                code: "ERR_INVALID_VALUE".to_string(),
+                message: "lighthouse hosts must not also be relay hosts".to_string(),
                 path: None,
             }],
         });
@@ -2309,13 +2311,129 @@ pub async fn create_host_and_enrollment_code(
     if (req.is_lighthouse || req.is_relay) && req.static_addresses.is_empty() {
         return HttpResponse::BadRequest().json(APIErrorsResponse {
             errors: vec![APIError {
-                code: "ERR_NEEDS_STATIC_ADDR".to_string(),
-                message: "A relay or lighthouse requires at least one static address.".to_string(),
-                path: None,
+                code: "ERR_INVALID_VALUE".to_string(),
+                message: "lighthouse hosts must have at least one static ip address".to_string(),
+                path: Some("staticAddresses".to_string()),
             }],
         });
     }
 
+    if req.listen_port == 0 && req.is_lighthouse {
+        return HttpResponse::BadRequest().json(APIErrorsResponse {
+            errors: vec![APIError {
+                code: "ERR_INVALID_VALUE".to_string(),
+                message: "lighthouse hosts must specify a static listen port".to_string(),
+                path: Some("listenPort".to_string()),
+            }]
+        });
+    } else if req.listen_port == 0 && req.is_relay {
+        return HttpResponse::BadRequest().json(APIErrorsResponse {
+            errors: vec![APIError {
+                code: "ERR_INVALID_VALUE".to_string(),
+                message: "relay hosts must specify a static listen port".to_string(),
+                path: Some("listenPort".to_string()),
+            }]
+        });
+    }
+
+    if let Some(role) = &req.role_id {
+        let roles = match role::Entity::find()
+            .filter(role::Column::Id.eq(role))
+            .all(&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 validating the request. Please try again later.".to_string(),
+                            path: Some("role".to_string()),
+                        }
+                    ],
+                });
+            }
+        };
+
+        if roles.is_empty() {
+            return HttpResponse::BadRequest().json(APIErrorsResponse {
+                errors: vec![
+                    APIError {
+                        code: "ERR_INVALID_REFERENCE".to_string(),
+                        message: "referenced value is invalid (perhaps it does not exist?)".to_string(),
+                        path: Some("role".to_string()),
+                    }
+                ],
+            });
+        }
+    }
+
+    let matching_hostname = match host::Entity::find()
+        .filter(host::Column::Name.eq(&req.name))
+        .all(&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 validating the request. Please try again later.".to_string(),
+                        path: Some("name".to_string()),
+                    }
+                ],
+            });
+        }
+    };
+
+    if !matching_hostname.is_empty() {
+        return HttpResponse::BadRequest().json(APIErrorsResponse {
+            errors: vec![
+                APIError {
+                    code: "ERR_DUPLICATE_VALUE".to_string(),
+                    message: "value already exists".to_string(),
+                    path: Some("name".to_string()),
+                }
+            ],
+        });
+    }
+
+    let matching_ip = match host::Entity::find()
+        .filter(host::Column::Ip.eq(&req.ip_address.to_string()))
+        .all(&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 validating the request. Please try again later.".to_string(),
+                        path: Some("ipAddress".to_string()),
+                    }
+                ],
+            });
+        }
+    };
+
+    if !matching_ip.is_empty() {
+        return HttpResponse::BadRequest().json(APIErrorsResponse {
+            errors: vec![
+                APIError {
+                    code: "ERR_DUPLICATE_VALUE".to_string(),
+                    message: "value already exists".to_string(),
+                    path: Some("ipAddress".to_string()),
+                }
+            ],
+        });
+    }
+
     let new_host_model = host::Model {
         id: random_id("host"),
         name: req.name.clone(),
@@ -2447,18 +2565,18 @@ pub async fn create_host_and_enrollment_code(
 
 #[derive(Serialize, Deserialize)]
 pub struct HostConfigOverrideResponse {
-    pub data: HostConfigOverrideData
+    pub data: HostConfigOverrideData,
 }
 
 #[derive(Serialize, Deserialize)]
 pub struct HostConfigOverrideData {
-    pub overrides: Vec<HostConfigOverrideDataOverride>
+    pub overrides: Vec<HostConfigOverrideDataOverride>,
 }
 
 #[derive(Serialize, Deserialize)]
 pub struct HostConfigOverrideDataOverride {
     pub key: String,
-    pub value: HostConfigOverrideDataOverrideValue
+    pub value: HostConfigOverrideDataOverrideValue,
 }
 
 #[derive(Serialize, Deserialize)]
@@ -2466,7 +2584,7 @@ pub struct HostConfigOverrideDataOverride {
 pub enum HostConfigOverrideDataOverrideValue {
     Boolean(bool),
     Numeric(i64),
-    Other(String)
+    Other(String),
 }
 
 #[get("/v1/hosts/{host_id}/config-overrides")]
@@ -2503,7 +2621,7 @@ pub async fn get_host_overrides(id: Path<String>, req_info: HttpRequest, db: Dat
                 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
+                    path: None,
                 }
             ],
         });
@@ -2546,7 +2664,7 @@ pub async fn get_host_overrides(id: Path<String>, req_info: HttpRequest, db: Dat
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -2619,7 +2737,7 @@ pub async fn get_host_overrides(id: Path<String>, req_info: HttpRequest, db: Dat
                         .to_string(),
                     path: None,
                 }],
-            })
+            });
         }
     };
 
@@ -2673,7 +2791,7 @@ pub async fn get_host_overrides(id: Path<String>, req_info: HttpRequest, db: Dat
 
 #[derive(Serialize, Deserialize)]
 pub struct UpdateOverridesRequest {
-    pub overrides: Vec<HostConfigOverrideDataOverride>
+    pub overrides: Vec<HostConfigOverrideDataOverride>,
 }
 
 #[put("/v1/hosts/{host_id}/config-overrides")]
@@ -2710,7 +2828,7 @@ pub async fn update_host_overrides(id: Path<String>, req: Json<UpdateOverridesRe
                 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
+                    path: None,
                 }
             ],
         });
@@ -2753,7 +2871,7 @@ pub async fn update_host_overrides(id: Path<String>, req: Json<UpdateOverridesRe
                         APIError {
                             code: "ERR_NO_ORG".to_string(),
                             message: "This user does not own any organizations. Try using an API token instead.".to_string(),
-                            path: None
+                            path: None,
                         }
                     ],
                 });
@@ -2826,7 +2944,7 @@ pub async fn update_host_overrides(id: Path<String>, req: Json<UpdateOverridesRe
                         .to_string(),
                     path: None,
                 }],
-            })
+            });
         }
     };