diff --git a/src/dns/dns_driver.rs b/src/dns/dns_driver.rs
index 402826d..f006109 100644
--- a/src/dns/dns_driver.rs
+++ b/src/dns/dns_driver.rs
@@ -11,8 +11,9 @@ use domain::tsig::{Algorithm, Key, KeyName};
 use domain::net::client::request::{self, RequestMessage, RequestMessageMulti, SendRequest, SendRequestMulti};
 use tokio::net::TcpStream;
 
-use crate::resources::record;
+use crate::resources::dns::internal;
 use crate::proto;
+use crate::errors::Error;
 use super::{RecordDriver, ZoneDriver, DnsDriverError};
 
 use async_trait::async_trait;
@@ -127,7 +128,7 @@ impl ZoneDriver for DnsDriver {
 impl RecordDriver for DnsDriver {
     /// ------------- AXFR -------------
 
-    async fn get_records(&self, zone: &str) -> Result<Vec<record::Record>, DnsDriverError> {
+    async fn get_records(&self, zone: &str) -> Result<internal::RecordList, DnsDriverError> {
         let mut msg = MessageBuilder::new_vec();
         msg.header_mut().set_ad(true);
 
@@ -172,7 +173,7 @@ impl RecordDriver for DnsDriver {
         // AXFR response ends with SOA, we remove it so it is not doubled in the response.
         records.pop();
 
-        Ok(records)
+        Ok(internal::RecordList { records })
     }
 
     /// ------------- Dynamic Update - RFC 2136 -------------
@@ -221,7 +222,7 @@ impl RecordDriver for DnsDriver {
     ///   zone     rrset    rr       Add to an RRset
 
 
-    async fn add_records(&self, zone: &str, new_records: &[record::DnsRecordImpl]) -> Result<(), DnsDriverError> {
+    async fn add_records(&self, zone: &str, new_records: internal::RecordList) -> Result<(), DnsDriverError> {
         let mut msg = MessageBuilder::new_vec();
         msg.header_mut().set_opcode(Opcode::UPDATE);
 
@@ -233,7 +234,8 @@ impl RecordDriver for DnsDriver {
 
         let mut msg = msg.authority();
 
-        for record in new_records {
+        for record in new_records.records {
+            let record = proto::dns::RecordImpl::try_from(record)?;
             msg.push(record)?;
         }
 
@@ -294,6 +296,12 @@ impl From<wire::ParseError> for DnsDriverError {
     }
 }
 
+impl From<Error> for DnsDriverError {
+    fn from(value: Error) -> Self {
+        DnsDriverError::OperationError { reason: Box::new(value) }
+    }
+}
+
 /*
 use trust_dns_proto::DnsHandle;
 use trust_dns_client::client::ClientHandle;
diff --git a/src/dns/mod.rs b/src/dns/mod.rs
index 1db0d65..ff379e0 100644
--- a/src/dns/mod.rs
+++ b/src/dns/mod.rs
@@ -4,7 +4,7 @@ use std::sync::Arc;
 
 use async_trait::async_trait;
 
-use crate::resources::record;
+use crate::resources::dns::internal;
 
 pub type BoxedZoneDriver = Arc<dyn ZoneDriver>;
 pub type BoxedRecordDriver = Arc<dyn RecordDriver>;
@@ -25,8 +25,8 @@ pub trait ZoneDriver: Send + Sync {
 
 #[async_trait]
 pub trait RecordDriver: Send + Sync {
-    async fn get_records(&self, zone: &str) -> Result<Vec<record::Record>, DnsDriverError>;
-    async fn add_records(&self, zone: &str, new_records: &[record::DnsRecordImpl]) -> Result<(), DnsDriverError>;
+    async fn get_records(&self, zone: &str) -> Result<internal::RecordList, DnsDriverError>;
+    async fn add_records(&self, zone: &str, new_records: internal::RecordList) -> Result<(), DnsDriverError>;
     //async fn update_records(&mut self, zone: dns::Name, class: dns::DNSClass, old_records: Vec<dns::Record>, new_records: Vec<dns::Record>) -> ConnectorResult<()>;
     //async fn delete_records(&mut self, zone: dns::Name, class: dns::DNSClass, records: Vec<dns::Record>) -> ConnectorResult<()>;
 
diff --git a/src/errors.rs b/src/errors.rs
index d2b475f..c50b555 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -8,7 +8,8 @@ use serde::{Serialize, Serializer};
 use serde_json::{Value, json};
 
 use crate::dns::DnsDriverError;
-use crate::resources::record::{RecordError, RecordParseError};
+use crate::resources::dns::external::rdata::RDataValidationError;
+use crate::resources::dns::external::record::{RecordError, RecordValidationError};
 use crate::resources::zone::ZoneError;
 use crate::validation::{DomainValidationError, TxtParseError};
 use crate::template::TemplateError;
@@ -157,6 +158,9 @@ impl fmt::Display for Error {
     }
 }
 
+
+impl std::error::Error for Error {}
+
 impl IntoResponse for Error {
     fn into_response(self) -> Response {
         if let Some(status) = self.status {
@@ -319,41 +323,46 @@ impl From<DnsDriverError> for Error {
     }
 }
 
-impl From<RecordParseError> for Error {
-    fn from(value: RecordParseError) -> Self {
+impl From<RDataValidationError> for Error {
+    fn from(value: RDataValidationError) -> Self {
         match value {
-            RecordParseError::Ip4Address { input } => {
+            RDataValidationError::Ip4Address { input } => {
                 Error::new("record:parse:ip4", "The following IPv4 address {input} is invalid. IPv4 addresses should have four numbers, each between 0 and 255, separated by dots.")
                     .with_details(json!({
                         "input": input
                     }))
             },
-            RecordParseError::Ip6Address { input } => {
+            RDataValidationError::Ip6Address { input } => {
                 Error::new("record:parse:ip6", "The following IPv4 address {input} is invalid. IPv6 addresses should have eight groups of four hexadecimal digit separated by colons. Leftmost zeros in a group can be omitted, sequence of zeros can be shorted by a double colons.")
                     .with_details(json!({
                         "input": input
                     }))
             },
-            RecordParseError::RDataUnknown { input, field, rtype } => {
-                Error::new("record:parse:rdata_unknown", "Unknown error while parsing record rdata field")
-                    .with_details(json!({
-                        "input": input,
-                        "field": field,
-                        "rtype": rtype,
-                    }))
-            },
-            RecordParseError::NameUnknown { input } => {
-                Error::new("record:parse:name_unknown", "Unknown error while parsing record name")
-                    .with_details(json!({
-                        "input": input
-                    }))
-            },
-            RecordParseError::NotInZone { name, zone } => {
+        }
+    }
+}
+
+impl From<RecordValidationError> for Error {
+    fn from(value: RecordValidationError) -> Self {
+        match value {
+            RecordValidationError::NotInZone { name, zone } => {
                 Error::new("record:parse:not_in_zone", "The domain name {name} is not in the current zone ({zone})")
                     .with_details(json!({
                         "name": name,
                         "zone": zone
                     }))
+            },
+        }
+    }
+}
+
+impl From<RecordError > for Error {
+    fn from(value: RecordError) -> Self {
+        match value {
+            RecordError::Validation { suberrors } => {
+                Error::new("record:validation", "Error while validating input records")
+                    .with_suberrors(suberrors)
+                    .with_status(StatusCode::BAD_REQUEST)
             }
         }
     }
@@ -377,18 +386,6 @@ impl From<TemplateError> for Error {
     }
 }
 
-impl From<RecordError > for Error {
-    fn from(value: RecordError) -> Self {
-        match value {
-            RecordError::Validation { suberrors } => {
-                Error::new("record:validation", "Error while validating input records")
-                    .with_suberrors(suberrors)
-                    .with_status(StatusCode::BAD_REQUEST)
-            }
-        }
-    }
-}
-
 impl From<ProtoDnsError> for Error {
     fn from(value: ProtoDnsError) -> Self {
         match value {
@@ -400,6 +397,12 @@ impl From<ProtoDnsError> for Error {
                         "rtype": rtype,
                     }))
             },
+            ProtoDnsError::NameParseError { input } => {
+                Error::new("proto:dns:name_unknown", "Unknown error while parsing name")
+                    .with_details(json!({
+                        "input": input
+                    }))
+            }
         }
     }
 }
diff --git a/src/proto/dns.rs b/src/proto/dns.rs
index bc64b95..13b4037 100644
--- a/src/proto/dns.rs
+++ b/src/proto/dns.rs
@@ -2,15 +2,17 @@ use std::fmt::Write;
 
 use domain::base::rdata::ComposeRecordData;
 use domain::base::wire::{Composer, ParseError};
-use domain::base::{Name, ParseRecordData, ParsedName, RecordData, Rtype, ToName, Ttl};
+use domain::base::{iana::Class, Name, ParseRecordData, ParsedName, RecordData, Record, Rtype, ToName, Ttl};
 use domain::rdata;
 use domain::dep::octseq::{Parser, Octets};
 
 use crate::resources::dns::internal;
 use crate::errors::Error;
 
+#[derive(Debug)]
 pub enum ProtoDnsError {
     RDataUnknown { input: String, field: String, rtype: String },
+    NameParseError { input: String }
 }
 
 /* --------- A --------- */
@@ -423,3 +425,58 @@ impl<Name: ToName, Octs: AsRef<[u8]>> ComposeRecordData for ParsedRData<Name, Oc
         }
     }
 }
+
+/* --------- Records  --------- */
+
+pub(crate) type RecordImpl = Record<
+    Name<Vec<u8>>,
+    ParsedRData<Name<Vec<u8>>,Vec<u8>>
+>;
+
+
+impl<Name: ToString, Oct: AsRef<[u8]>> From<Record<Name, ParsedRData<Name, Oct>>> for internal::Record {
+    fn from(value: Record<Name, ParsedRData<Name, Oct>>) -> Self {
+        internal::Record {
+            name: internal::Name::new(value.owner().to_string()),
+            ttl: value.ttl().as_secs(),
+            rdata: internal::RData::from(value.into_data()),
+        }
+    }
+}
+
+impl TryFrom<internal::Record> for RecordImpl {
+    type Error = Error;
+
+    fn try_from(value: internal::Record) -> Result<Self, Self::Error> {
+        let owner = value.name.to_string();
+        let owner = owner.parse::<Name<_>>().map_err(|e|  {
+            Error::from(ProtoDnsError::NameParseError {
+                input: owner
+            }).with_cause(&e.to_string())
+        })?;
+
+        let ttl = Ttl::from_secs(value.ttl);
+        let data = value.rdata.try_into()?;
+        Ok(Record::new(owner, Class::IN, ttl, data))
+
+    }
+}
+
+
+pub struct RecordList {
+    pub records: Vec<RecordImpl>
+}
+
+impl TryFrom<internal::RecordList> for RecordList {
+    type Error = Error;
+
+    fn try_from(value: internal::RecordList) -> Result<Self, Self::Error> {
+        let mut records = Vec::with_capacity(value.records.len());
+
+        for record in value.records.into_iter() {
+            records.push(record.try_into()?)
+        }
+
+        Ok(RecordList { records })
+    }
+}
diff --git a/src/resources/dns/external/mod.rs b/src/resources/dns/external/mod.rs
index f567899..55840a5 100644
--- a/src/resources/dns/external/mod.rs
+++ b/src/resources/dns/external/mod.rs
@@ -1,2 +1,5 @@
 pub mod rdata;
+pub mod record;
+
 pub use rdata::*;
+pub use record::*;
diff --git a/src/resources/dns/external/rdata.rs b/src/resources/dns/external/rdata.rs
index ec00eb1..2202364 100644
--- a/src/resources/dns/external/rdata.rs
+++ b/src/resources/dns/external/rdata.rs
@@ -1,16 +1,20 @@
 use std::fmt::Write;
 use std::net::{Ipv4Addr, Ipv6Addr};
 
-use domain::base::{Rtype, scan::Symbol};
+use domain::base::scan::Symbol;
 use serde::{Deserialize, Serialize};
 
 use crate::errors::Error;
 use crate::validation;
 
 use crate::macros::{append_errors, push_error};
-use crate::resources::record::RecordParseError;
 use crate::resources::dns::internal;
 
+pub enum RDataValidationError {
+    Ip4Address { input: String },
+    Ip6Address { input: String },
+}
+
 /// Type used to serialize / deserialize resource records data to response / request
 ///
 #[derive(Debug, Deserialize, Serialize)]
@@ -34,20 +38,6 @@ pub enum RData {
 }
 
 impl RData {
-    pub fn rtype(&self) -> Rtype {
-        match self {
-            RData::A(_) => Rtype::A,
-            RData::Aaaa(_) => Rtype::AAAA,
-            RData::Cname(_) => Rtype::CNAME,
-            RData::Mx(_) => Rtype::MX,
-            RData::Ns(_) => Rtype::NS,
-            RData::Ptr(_) => Rtype::PTR,
-            RData::Soa(_) => Rtype::SOA,
-            RData::Srv(_) => Rtype::SRV,
-            RData::Txt(_) => Rtype::TXT,
-        }
-    }
-
     pub fn validate(self) -> Result<internal::RData, Vec<Error>> {
         let rdata = match self {
             RData::A(data) => internal::RData::A(data.validate()?),
@@ -101,7 +91,7 @@ impl A {
         let mut errors = Vec::new();
 
         let address = push_error!(self.address.parse::<Ipv4Addr>().map_err(|e| {
-            Error::from(RecordParseError::Ip4Address { input: self.address })
+            Error::from(RDataValidationError::Ip4Address { input: self.address })
                 .with_cause(&e.to_string())
                 .with_path("/address")
         }), errors);
@@ -137,7 +127,7 @@ impl Aaaa {
 
         // TODO: replace with custom validation
         let address = push_error!(self.address.parse::<Ipv6Addr>().map_err(|e| {
-            Error::from(RecordParseError::Ip6Address { input: self.address })
+            Error::from(RDataValidationError::Ip6Address { input: self.address })
                 .with_cause(&e.to_string())
                 .with_path("/address")
         }), errors);
diff --git a/src/resources/dns/internal/mod.rs b/src/resources/dns/internal/mod.rs
index 3d9a147..5e3d450 100644
--- a/src/resources/dns/internal/mod.rs
+++ b/src/resources/dns/internal/mod.rs
@@ -1,3 +1,7 @@
 pub mod rdata;
+pub mod record;
+pub mod base;
 
 pub use rdata::*;
+pub use record::*;
+pub use base::*;
diff --git a/src/resources/dns/internal/rdata.rs b/src/resources/dns/internal/rdata.rs
index 552575e..add8c19 100644
--- a/src/resources/dns/internal/rdata.rs
+++ b/src/resources/dns/internal/rdata.rs
@@ -1,6 +1,8 @@
 use std::net::{Ipv4Addr, Ipv6Addr};
-use std::fmt;
 
+use super::{Name, Rtype};
+
+#[derive(Clone)]
 pub enum RData {
     A(A),
     Aaaa(Aaaa),
@@ -13,49 +15,54 @@ pub enum RData {
     Txt(Txt),
 }
 
-pub struct Name {
-    name: String
-}
-
-impl Name {
-    pub fn new(name: String) -> Self {
-        Name {
-            name,
+impl RData {
+    pub fn rtype(&self) -> Rtype {
+        match self {
+            RData::A(_) => Rtype::A,
+            RData::Aaaa(_) => Rtype::Aaaa,
+            RData::Cname(_) => Rtype::Cname,
+            RData::Mx(_) => Rtype::Mx,
+            RData::Ns(_) => Rtype::Ns,
+            RData::Ptr(_) => Rtype::Ptr,
+            RData::Soa(_) => Rtype::Soa,
+            RData::Srv(_) => Rtype::Srv,
+            RData::Txt(_) => Rtype::Txt,
         }
     }
 }
 
-impl fmt::Display for Name {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.name)
-    }
-}
-
+#[derive(Clone)]
 pub struct A {
     pub address: Ipv4Addr,
 }
 
+#[derive(Clone)]
 pub struct Aaaa {
     pub address: Ipv6Addr,
 }
 
+#[derive(Clone)]
 pub struct Cname {
     pub target: Name,
 }
 
+#[derive(Clone)]
 pub struct Mx {
     pub preference: u16,
     pub mail_exchanger: Name,
 }
 
+#[derive(Clone)]
 pub struct Ns {
     pub target: Name,
 }
 
+#[derive(Clone)]
 pub struct Ptr {
     pub target: Name,
 }
 
+#[derive(Clone)]
 pub struct Soa {
     pub primary_server: Name,
     pub maintainer: Name,
@@ -66,6 +73,7 @@ pub struct Soa {
     pub serial: u32,
 }
 
+#[derive(Clone)]
 pub struct Srv {
     pub server: Name,
     pub port: u16,
@@ -73,6 +81,7 @@ pub struct Srv {
     pub weight: u16,
 }
 
+#[derive(Clone)]
 pub struct Txt {
     pub text: Vec<u8>,
 }
diff --git a/src/resources/mod.rs b/src/resources/mod.rs
index fb7d88e..9cc3a32 100644
--- a/src/resources/mod.rs
+++ b/src/resources/mod.rs
@@ -20,6 +20,4 @@ pub use zone::{Zone, AddZoneMemberRequest, CreateZoneRequest};
 */
 
 pub mod zone;
-//pub mod rdata;
-pub mod record;
 pub mod dns;
diff --git a/src/resources/record/mod.rs b/src/resources/record/mod.rs
deleted file mode 100644
index 9b29821..0000000
--- a/src/resources/record/mod.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-use serde::{Deserialize, Serialize};
-
-use domain::base::{iana::Class, Name, Record as DnsRecord, Ttl};
-
-use crate::{errors::Error, validation};
-use crate::macros::{append_errors, push_error};
-
-use crate::resources::dns::external;
-use crate::resources::dns::internal;
-use crate::proto;
-
-pub enum RecordParseError {
-    Ip4Address { input: String },
-    Ip6Address { input: String },
-    RDataUnknown { input: String, field: String, rtype: String },
-    NameUnknown { input: String },
-    NotInZone { name: String, zone: String },
-}
-
-pub enum RecordError {
-    Validation { suberrors: Vec<Error> },
-}
-
-pub(crate) type DnsRecordImpl = DnsRecord<
-    Name<Vec<u8>>,
-    proto::dns::ParsedRData<Name<Vec<u8>>,Vec<u8>>
->;
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Record {
-    pub name: String,
-    pub ttl: u32,
-    #[serde(flatten)]
-    pub rdata: external::RData
-}
-
-// TODO: Proto
-impl<Name: ToString, Oct: AsRef<[u8]>> From<DnsRecord<Name, proto::dns::ParsedRData<Name, Oct>>> for Record {
-    fn from(value: DnsRecord<Name, proto::dns::ParsedRData<Name, Oct>>) -> Self {
-        Record {
-            name: value.owner().to_string(),
-            ttl: value.ttl().as_secs(),
-            rdata: internal::RData::from(value.into_data()).into(),
-        }
-    }
-}
-
-impl Record {
-    fn convert(self, zone_name: &Name<Vec<u8>>) -> Result<DnsRecordImpl, Vec<Error>> {
-        let mut errors = Vec::new();
-
-        let name = push_error!(validation::normalize_domain(&self.name), errors, "/name");
-
-        let name = name.and_then(|name| push_error!(name.parse::<Name<_>>().map_err(|e| {
-            Error::from(RecordParseError::NameUnknown {
-                input: self.name.clone()
-            }).with_cause(&e.to_string())
-        }), errors, "/name"));
-
-        let name = name.and_then(|name| {
-            if !name.ends_with(zone_name) {
-                errors.push(
-                    Error::from(RecordParseError::NotInZone { name: self.name, zone: zone_name.to_string() })
-                        .with_path("/name")
-                );
-                None
-            } else {
-                Some(name)
-            }
-        });
-
-        let ttl = Ttl::from_secs(self.ttl);
-        let rdata = append_errors!(self.rdata.validate(), errors, "/rdata");
-
-
-        if errors.is_empty() {
-            // TODO: Split this in proto / external
-            let rdata = proto::dns::ParsedRData::try_from(rdata.unwrap()).unwrap();
-            Ok(DnsRecord::new(name.unwrap(), Class::IN, ttl, rdata))
-        } else {
-            Err(errors)
-        }
-    }
-}
-
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct RecordList(pub Vec<Record>);
-
-impl RecordList {
-    fn convert(self, zone_name: &Name<Vec<u8>>) -> Result<Vec<DnsRecordImpl>, Vec<Error>> {
-        let mut errors = Vec::new();
-        let mut records = Vec::new();
-
-        for (index, record) in self.0.into_iter().enumerate() {
-            let record = append_errors!(record.convert(zone_name), errors, &format!("/{index}"));
-
-            if let Some(record) = record {
-                records.push(record)
-            }
-        }
-
-        if errors.is_empty() {
-            Ok(records)
-        } else {
-            Err(errors)
-        }
-    }
-}
-
-#[derive(Debug,Deserialize)]
-pub struct AddRecordsRequest {
-    pub new_records: RecordList
-}
-
-pub struct AddRecords {
-    pub new_records: Vec<DnsRecordImpl>
-}
-
-impl AddRecordsRequest {
-    pub fn validate(self, zone_name: &str) -> Result<AddRecords, Error> {
-        let zone_name: Name<Vec<u8>> = zone_name.parse().expect("zone name is assumed to be valid");
-
-        let mut errors = Vec::new();
-        let records = append_errors!(self.new_records.convert(&zone_name), errors, "/new_records");
-
-        if errors.is_empty() {
-            Ok(AddRecords {
-                new_records: records.unwrap(),
-            })
-        } else {
-            Err(Error::from(RecordError::Validation { suberrors: errors }))
-        }
-    }
-}
diff --git a/src/resources/zone.rs b/src/resources/zone.rs
index ced2977..3552789 100644
--- a/src/resources/zone.rs
+++ b/src/resources/zone.rs
@@ -7,7 +7,7 @@ use crate::database::{BoxedDb, sqlite::SqliteDB};
 use crate::dns::{BoxedZoneDriver, BoxedRecordDriver, DnsDriverError};
 use crate::errors::Error;
 use crate::macros::push_error;
-use crate::resources::record::RecordList;
+use crate::resources::dns::internal::RecordList;
 use crate::validation;
 
 pub enum ZoneError {
@@ -45,13 +45,9 @@ impl Zone {
         let zone = db.get_zone_by_name(zone_name).await?;
         let mut records = record_driver.get_records(&zone.name).await?;
 
-        records.sort_by(|r1, r2| {
-            let key1 = (&r1.name, r1.rdata.rtype());
-            let key2 = (&r2.name, r2.rdata.rtype());
-            key1.cmp(&key2)
-        });
+        records.sort();
 
-        Ok(RecordList(records))
+        Ok(records)
     }
 }
 
diff --git a/src/routes/api/zones.rs b/src/routes/api/zones.rs
index 5dc0739..84c260c 100644
--- a/src/routes/api/zones.rs
+++ b/src/routes/api/zones.rs
@@ -4,7 +4,8 @@ use axum::Json;
 use crate::AppState;
 use crate::errors::Error;
 use crate::resources::zone::{CreateZoneRequest, Zone};
-use crate::resources::record::{AddRecordsRequest, Record, RecordList};
+use crate::resources::dns::external;
+use crate::resources::dns::internal;
 
 
 pub async fn create_zone(
@@ -18,26 +19,26 @@ pub async fn create_zone(
 pub async fn get_zone_records(
     Path(zone_name): Path<String>,
     State(app): State<AppState>,
-) -> Result<Json<RecordList>, Error>
+) -> Result<Json<external::RecordList>, Error>
 {
-    Zone::get_records(&zone_name, app.db, app.records).await.map(Json)
+    Zone::get_records(&zone_name, app.db, app.records)
+        .await
+        .map(|records| Json(records.into()))
 }
 
 pub async fn create_zone_records(
     Path(zone_name): Path<String>,
     State(app): State<AppState>,
-    Json(add_records): Json<AddRecordsRequest>,
-) -> Result<Json<Vec<Record>>, Error>
+    Json(add_records): Json<external::AddRecords>,
+) -> Result<Json<external::RecordList>, Error>
 {
 
     let zone = app.db.get_zone_by_name(&zone_name).await?;
-    let add_records = add_records.validate(&zone.name)?;
-    app.records.add_records(&zone.name, &add_records.new_records).await?;
-    let records = add_records.new_records.into_iter()
-        .map(|r| r.into())
-        .collect();
+    let name = internal::Name::new(zone.name.clone());
+    let add_records = add_records.validate(&name)?;
+    app.records.add_records(&zone.name, add_records.new_records.clone()).await?;
 
-    Ok(Json(records))
+    Ok(Json(add_records.new_records.into()))
 }
 
 /*
diff --git a/src/routes/ui/zones.rs b/src/routes/ui/zones.rs
index 6f8dd0a..962d844 100644
--- a/src/routes/ui/zones.rs
+++ b/src/routes/ui/zones.rs
@@ -5,7 +5,7 @@ use crate::AppState;
 use crate::errors::Error;
 use crate::resources::zone::Zone;
 use crate::template::Template;
-
+use crate::resources::dns::external;
 
 pub async fn get_zone_records_page(
     Path(zone_name): Path<String>,
@@ -20,7 +20,7 @@ pub async fn get_zone_records_page(
         app.template_engine,
         json!({
             "current_zone": zone_name,
-            "records": records,
+            "records": external::RecordList::from(records),
         })
     ))
 }