diff --git a/.env.unix.example b/.env.unix.example
index b352e9a482a5f582dd36e11aa32700e81ebcd824..930e4f7ba4640f056e13aceb2d02dac45261cb7c 100644
--- a/.env.unix.example
+++ b/.env.unix.example
@@ -20,4 +20,9 @@ SHARED_FILESYSTEM=/tmp
 LOG_LEVEL=trace # error, warning, info, debug, trace
 DEFAULT_ROLES=ROLE_RESEARCHER
 SUPERUSERS=user1,user2
-ELASTIC_PASSWORD=elastic
\ No newline at end of file
+ELASTIC_PASSWORD=elastic
+DOI_URL=https://doi.org
+DATACITE_URL=https://api.datacite.org
+DATACITE_PREFIX=<prefix>
+DATACITE_USERNAME=<username>
+DATACITE_PASSWORD=<password>
\ No newline at end of file
diff --git a/.env.win.example b/.env.win.example
index cf27aca493364a3665bd177230f9028384a2413a..3c26a4505b85400196878fb5c2e7ccb8b750c1df 100644
--- a/.env.win.example
+++ b/.env.win.example
@@ -20,4 +20,9 @@ SHARED_FILESYSTEM=C:\tmp
 LOG_LEVEL=trace # error, warning, info, debug, trace
 DEFAULT_ROLES=ROLE_RESEARCHER
 SUPERUSERS=user1,user2
-ELASTIC_PASSWORD=elastic
\ No newline at end of file
+ELASTIC_PASSWORD=elastic
+DOI_URL=https://doi.org
+DATACITE_URL=https://api.datacite.org
+DATACITE_PREFIX=<prefix>
+DATACITE_USERNAME=<username>
+DATACITE_PASSWORD=<password>
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index cebd8bbc63ddab78c25b3db0b3d3e34f3519f545..dcecc1a35265d9e5d9c94e65edc5edd348986114 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,7 +3,7 @@ target/
 !.mvn/wrapper/maven-wrapper.jar
 !**/src/main/**/target/
 !**/src/test/**/target/
-dbrepo-*
+# dbrepo-*
 
 # Notebooks
 .jupyter/
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteBody.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteBody.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1c434179a00b9a1383e38a5c900f2894cb64249
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteBody.java
@@ -0,0 +1,15 @@
+package at.tuwien.datacite;
+
+import lombok.*;
+
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteBody<T> implements Serializable {
+
+    private DataCiteData<T> data;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteData.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteData.java
new file mode 100644
index 0000000000000000000000000000000000000000..84350e4ab9680e255590b9c9ec4b76705a3eeec9
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/DataCiteData.java
@@ -0,0 +1,20 @@
+package at.tuwien.datacite;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.*;
+
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DataCiteData<T> implements Serializable {
+
+    private String id;
+    private String type;
+    private T attributes;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteCreateDoi.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteCreateDoi.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a9d1c1b9f4801160debb9b64390c916b5708af8
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteCreateDoi.java
@@ -0,0 +1,44 @@
+package at.tuwien.datacite.doi;
+
+import lombok.*;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteCreateDoi implements Serializable {
+
+    private String url;
+
+    private String prefix;
+
+    private DataCiteDoiTypes types;
+
+    private DataCiteDoiEvent event;
+
+    private List<DataCiteDoiTitle> titles;
+
+    @NotBlank
+    private String publisher;
+
+    @NotNull
+    private Integer publicationYear;
+
+    private Integer publicationMonth;
+
+    private Integer publicationDay;
+
+    private String language;
+
+    private List<DataCiteDoiRights> rightsList;
+
+    private List<DataCiteDoiCreator> creators;
+
+    private List<DataCiteDoiRelatedIdentifier> relatedIdentifiers;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoi.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoi.java
new file mode 100644
index 0000000000000000000000000000000000000000..1890aae2fd324e075d4141e99f17c06f28b23ecc
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoi.java
@@ -0,0 +1,17 @@
+package at.tuwien.datacite.doi;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.*;
+
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DataCiteDoi implements Serializable {
+
+    private String doi;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreator.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..d42fdec34746836859fa18af7dfbf43f94b23a1a
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreator.java
@@ -0,0 +1,26 @@
+package at.tuwien.datacite.doi;
+
+import lombok.*;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteDoiCreator implements Serializable {
+
+    @NotBlank
+    private String name;
+
+    private String givenName;
+
+    private String familyName;
+
+    private List<DataCiteDoiCreatorAffiliation> affiliation;
+
+    private List<DataCiteDoiCreatorNameIdentifier> nameIdentifiers;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorAffiliation.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorAffiliation.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b598cb33bca49a287ac0918eee671a184c61532
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorAffiliation.java
@@ -0,0 +1,21 @@
+package at.tuwien.datacite.doi;
+
+import lombok.*;
+
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteDoiCreatorAffiliation implements Serializable {
+
+    private String affiliationIdentifier;
+
+    private String affiliationScheme;
+
+    private String name;
+
+    private String schemeUri;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorNameIdentifier.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorNameIdentifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe12496fc159244c20259685f7b7f4796fd0565c
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiCreatorNameIdentifier.java
@@ -0,0 +1,19 @@
+package at.tuwien.datacite.doi;
+
+import lombok.*;
+
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteDoiCreatorNameIdentifier implements Serializable {
+
+    private String schemeUri;
+
+    private String nameIdentifier;
+
+    private String nameIdentifierScheme;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiEvent.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..24925a4f08286c9c59533628cc21e7c0e26375bb
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiEvent.java
@@ -0,0 +1,33 @@
+package at.tuwien.datacite.doi;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+
+@Getter
+public enum DataCiteDoiEvent implements Serializable {
+
+    @JsonProperty("publish")
+    PUBLISH("publish"),
+
+    @JsonProperty("register")
+    REGISTER("register"),
+
+    @JsonProperty("hide")
+    HIDE("hide");
+
+    private final String name;
+
+    DataCiteDoiEvent(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String toString() {
+        return this.name;
+    }
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRelatedIdentifier.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRelatedIdentifier.java
new file mode 100644
index 0000000000000000000000000000000000000000..3f4a4211d9428cad2c9bf165d4e2793c6950cdef
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRelatedIdentifier.java
@@ -0,0 +1,21 @@
+package at.tuwien.datacite.doi;
+
+import lombok.*;
+
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteDoiRelatedIdentifier implements Serializable {
+
+    private String relatedIdentifier;
+
+    private String relatedIdentifierType;
+
+    private String relationType;
+
+    private String resourceTypeGeneral;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRights.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRights.java
new file mode 100644
index 0000000000000000000000000000000000000000..b48826fa13cc5be41eb8a0c0672a9a2d09acb220
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiRights.java
@@ -0,0 +1,19 @@
+package at.tuwien.datacite.doi;
+
+import lombok.*;
+
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteDoiRights implements Serializable {
+
+    private String rights;
+
+    private String rightsUri;
+
+    private String lang;
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTitle.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTitle.java
new file mode 100644
index 0000000000000000000000000000000000000000..c19a37036e1c07ce4dc7c6197b7939d183c6f97a
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTitle.java
@@ -0,0 +1,48 @@
+package at.tuwien.datacite.doi;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteDoiTitle implements Serializable {
+
+    @NotBlank
+    private String title;
+
+    private Type titleType;
+
+    private String lang;
+
+    public enum Type {
+
+        @JsonProperty("AlternativeTitle")
+        ALTERNATIVE_TITLE("AlternativeTitle"),
+
+        @JsonProperty("Subtitle")
+        SUBTITLE("Subtitle"),
+
+        @JsonProperty("TranslatedTitle")
+        TRANSLATED_TITLE("TranslatedTitle"),
+
+        @JsonProperty("Other")
+        OTHER("Other");
+
+        private final String name;
+
+        Type(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return this.name;
+        }
+    }
+}
diff --git a/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTypes.java b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTypes.java
new file mode 100644
index 0000000000000000000000000000000000000000..deb7d155f4df1c206fc6cbbfa4fc86d043541165
--- /dev/null
+++ b/dbrepo-identifier-service/api/src/main/java/at/tuwien/datacite/doi/DataCiteDoiTypes.java
@@ -0,0 +1,29 @@
+package at.tuwien.datacite.doi;
+
+import lombok.*;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@Data
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class DataCiteDoiTypes implements Serializable {
+
+    public static final DataCiteDoiTypes DATASET = DataCiteDoiTypes.builder().resourceTypeGeneral("Dataset").build();
+
+    @NotNull
+    private String resourceTypeGeneral;
+
+    private String resourceType;
+
+    private String schemaOrg;
+
+    private String bibtex;
+
+    private String citeproc;
+
+    private String ris;
+}
diff --git a/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
index 8826a455e6903339ac54f7cde0b9326feaf3b94e..4d9a5defbf76ef93e0b84b7737b8eca4d4b83f97 100644
--- a/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
+++ b/dbrepo-identifier-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
@@ -172,7 +172,7 @@ public class IdentifierEndpoint extends AbstractEndpoint {
     })
     public ResponseEntity<IdentifierDto> update(@NotNull @PathVariable("id") Long id,
                                                 @NotNull @Valid @RequestBody IdentifierDto data)
-            throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException {
+            throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException, IdentifierRequestException {
         log.debug("endpoint update identifier, id={}, data={}", id, data);
         final Identifier identifier = identifierService.update(id, data);
         return ResponseEntity.accepted()
@@ -195,7 +195,7 @@ public class IdentifierEndpoint extends AbstractEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<?> delete(@NotNull @PathVariable("id") Long id)
-            throws IdentifierNotFoundException {
+            throws IdentifierNotFoundException, NotAllowedException {
         log.debug("endpoint delete identifier, id={}", id);
         identifierService.delete(id);
         return ResponseEntity.accepted()
diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/application-doi.yml b/dbrepo-identifier-service/rest-service/src/main/resources/application-doi.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4a6687e0e807f7fbb2243078053bc4641a900368
--- /dev/null
+++ b/dbrepo-identifier-service/rest-service/src/main/resources/application-doi.yml
@@ -0,0 +1,6 @@
+fda:
+  datacite:
+    url: "${DATACITE_URL}"
+    prefix: "${DATACITE_PREFIX}"
+    username: "${DATACITE_USERNAME}"
+    password: "${DATACITE_PASSWORD}"
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_apa.txt b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_apa.txt
index 13cf643dae05b0e93e8c77d68a280b4d0f5260ad..8af282e645262dd7f8dfa8c17826e1d4c4fd43a6 100644
--- a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_apa.txt
+++ b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_apa.txt
@@ -1 +1 @@
-[# th:each="creator,idx: ${creators}"][# th:if="${idx.index} &gt; 0 and ${idx.index} != ${idx.size} - 1 and ${idx.index} != 0"], [/][# th:if="${idx.index} == ${idx.size} - 1 and ${idx.size} != 1"] & [/][[${creator.firstname.substring(0,1)}]]., [[${creator.lastname}]][/][# th:if="${creators.size()} &gt; 0"]. [/]([[${publicationYear}]]). [[${title}]]. [[${publisher}]]. [[${doi}]]
\ No newline at end of file
+[# th:each="creator,idx: ${creators}"][# th:if="${idx.index} &gt; 0 and ${idx.index} != ${idx.size} - 1 and ${idx.index} != 0"], [/][# th:if="${idx.index} == ${idx.size} - 1 and ${idx.size} != 1"] & [/][[${creator.firstname.substring(0,1)}]]., [[${creator.lastname}]][/][# th:if="${creators.size()} &gt; 0"]. [/]([[${publicationYear}]]). [[${title}]]. [[${publisher}]]. [[${identifierType} == 'doi' ? 'https://doi.org/' : '']][[${identifier}]]
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_bibtex.txt b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_bibtex.txt
index 3ffe19fef7193059855155a43c65df28de45059b..ad48a2bd59fb10d5e25acc9323288c6c16e0df49 100644
--- a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_bibtex.txt
+++ b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_bibtex.txt
@@ -1,7 +1,7 @@
 @misc{dbrepo[[${publicationYear}]],
   author = {[# th:each="creator,idx: ${creators}"][# th:if="${idx.index} &gt; 0"] and [/][[${creator.lastname}]], [[${creator.firstname}]][/]},
   title = {[[${title}]]},
-  howpublished = {\url{[[${doi}]]}},
+  [[${identifierType}]] = {[[${identifierType} == 'url' ? '\url{' : '']][[${identifier}]][[${identifierType} == 'url' ? '}' : '']]},
   month = {[[${publicationMonth}]]},
   year = {[[${publicationYear}]]}
 }
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_ieee.txt b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_ieee.txt
index 67f9e13035097bb4587d2f3ff1a5199ff4d65996..602b73deed44278af106e3a946a1b1aa55996c36 100644
--- a/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_ieee.txt
+++ b/dbrepo-identifier-service/rest-service/src/main/resources/templates/cite_ieee.txt
@@ -1 +1 @@
-[1] [# th:each="creator,idx: ${creators}"][# th:if="${idx.index} &gt; 0"] and [/][[${creator.firstname.substring(0,1)}]]. [[${creator.lastname}]][/][# th:if="${creators.size()} &gt; 0"], [/]“[[${title}]]“, [[${publisher}]], [[${publicationYear}]], doi: [[${doi}]].
\ No newline at end of file
+[1] [# th:each="creator,idx: ${creators}"][# th:if="${idx.index} &gt; 0"] and [/][[${creator.firstname.substring(0,1)}]]. [[${creator.lastname}]][/][# th:if="${creators.size()} &gt; 0"], [/]“[[${title}]]“, [[${publisher}]], [[${publicationYear}]], [[${identifierType} == 'url' ? 'Available: ' : '']][[${identifier}]].
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/main/resources/templates/doi.xml b/dbrepo-identifier-service/rest-service/src/main/resources/templates/doi.xml
index 48a23436cb5faa1d71d4ec6e2ceb8b9f05a6948d..3d52208f994c8d145a2d2bd734e10f6b0089de47 100644
--- a/dbrepo-identifier-service/rest-service/src/main/resources/templates/doi.xml
+++ b/dbrepo-identifier-service/rest-service/src/main/resources/templates/doi.xml
@@ -1,11 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://datacite.org/schema/kernel-4"
           xsi:schemaLocation="http://datacite.org/schema/kernel-4 https://schema.datacite.org/meta/kernel-4.4/metadata.xsd">
-    <identifier identifierType="PID">[[${doi}]]</identifier><creators th:if="${not #lists.isEmpty(creators)}">
+    <identifier th:attr="identifierType=${identifierType}">[[${identifier}]]</identifier>
+    <creators th:if="${not #lists.isEmpty(creators)}">
         <creator th:each="creator: ${creators}">
             <creatorName nameType="Personal">[[${creator.lastname}]], [[${creator.firstname}]]</creatorName>
             <givenName>[[${creator.firstname}]]</givenName>
-            <familyName>[[${creator.lastname}]]</familyName><affiliation th:if="${creator.affiliation != null}">[[${creator.affiliation}]]</affiliation><nameIdentifier th:if="${creator.orcid != null}" schemeURI="https://orcid.org" nameIdentifierScheme="ORCID">[[${creator.orcid}]]</nameIdentifier>
+            <familyName>[[${creator.lastname}]]</familyName>
+            <nameIdentifier th:if="${creator.orcid != null}" schemeURI="https://orcid.org" nameIdentifierScheme="ORCID">
+                [[${creator.orcid}]]
+            </nameIdentifier>
+            <affiliation th:if="${creator.affiliation != null}">[[${creator.affiliation}]]</affiliation>
         </creator>
     </creators>
     <titles>
@@ -17,9 +22,12 @@
         <date dateType="Issued">[[${created}]]</date>
         <date dateType="Available">[[${created}]]</date>
     </dates>
-    <resourceType resourceTypeGeneral="Dataset">Dataset</resourceType><relatedIdentifiers th:if="${not #lists.isEmpty(relatedIdentifiers)}">
+    <resourceType resourceTypeGeneral="Dataset">Dataset</resourceType>
+    <relatedIdentifiers th:if="${not #lists.isEmpty(relatedIdentifiers)}">
         <relatedIdentifier th:each="relatedIdentifier: ${relatedIdentifiers}"
-                           th:attr="relatedIdentifierType=${relatedIdentifier.type},relationType=${relatedIdentifier.relation}">[[${relatedIdentifier.value}]]</relatedIdentifier>
+                           th:attr="relatedIdentifierType=${relatedIdentifier.type},relationType=${relatedIdentifier.relation}">
+            [[${relatedIdentifier.value}]]
+        </relatedIdentifier>
     </relatedIdentifiers>
     <descriptions th:if="${description != null}">
         <description descriptionType="Abstract">[[${description}]]</description>
diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointUnitTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointUnitTest.java
index 22fb7cf7ca355c2f60654d64a36d0d18581cfad0..8ad52e70ba3bd5fe30900d7a635e4fd0ee0f7c09 100644
--- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointUnitTest.java
+++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/IdentifierEndpointUnitTest.java
@@ -209,8 +209,6 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest {
                 .dbid(IDENTIFIER_1_DATABASE_ID)
                 .description(IDENTIFIER_1_DESCRIPTION)
                 .title(IDENTIFIER_1_TITLE)
-                .doi(IDENTIFIER_1_DOI)
-                .visibility(IDENTIFIER_1_VISIBILITY_DTO)
                 .relatedIdentifiers(List.of())
                 .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
                 .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
@@ -234,8 +232,6 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest {
                 .dbid(IDENTIFIER_1_DATABASE_ID)
                 .description(IDENTIFIER_1_DESCRIPTION)
                 .title(IDENTIFIER_1_TITLE)
-                .doi(IDENTIFIER_1_DOI)
-                .visibility(IDENTIFIER_1_VISIBILITY_DTO)
                 .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_2_CREATE_DTO))
                 .publicationDay(IDENTIFIER_2_PUBLICATION_DAY)
                 .publicationMonth(IDENTIFIER_2_PUBLICATION_MONTH)
@@ -297,7 +293,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest {
     @Test
     @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"})
     public void update_dataSteward_succeeds() throws IdentifierPublishingNotAllowedException,
-            IdentifierNotFoundException {
+            IdentifierNotFoundException, IdentifierRequestException {
 
         /* test */
         generic_update();
@@ -328,7 +324,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest {
 
     @Test
     @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"})
-    public void delete_dataSteward_succeeds() throws IdentifierNotFoundException {
+    public void delete_dataSteward_succeeds() throws IdentifierNotFoundException, NotAllowedException {
 
         /* test */
         generic_delete();
@@ -397,7 +393,8 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest {
         return persistenceEndpoint.find(IDENTIFIER_1_ID, accept);
     }
 
-    protected void generic_update() throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException {
+    protected void generic_update()
+            throws IdentifierPublishingNotAllowedException, IdentifierNotFoundException, IdentifierRequestException {
 
         /* mock */
         when(identifierService.update(IDENTIFIER_3_ID, IDENTIFIER_3_DTO))
@@ -419,7 +416,7 @@ public class IdentifierEndpointUnitTest extends BaseUnitTest {
         assertEquals(IDENTIFIER_3_RESULT_HASH, body.getResultHash());
     }
 
-    protected void generic_delete() throws IdentifierNotFoundException {
+    protected void generic_delete() throws IdentifierNotFoundException, NotAllowedException {
 
         /* mock */
         doNothing()
diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/PersistenceEndpointUnitTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/PersistenceEndpointUnitTest.java
index 50213b8fdaf27b010fc691608fb0e1af67d8fadf..491b3eb7c574a0222ec07b5c56ecaf182dd5cbe4 100644
--- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/PersistenceEndpointUnitTest.java
+++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/endpoint/PersistenceEndpointUnitTest.java
@@ -307,6 +307,25 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest {
         assertEquals(compare, body);
     }
 
+    @Test
+    public void find_bibliographyApa4_succeeds() throws IdentifierNotFoundException, QueryNotFoundException,
+            RemoteUnavailableException, IdentifierRequestException, IOException {
+        final String accept = "text/bibliography; style=apa";
+        final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_apa4.txt"),
+                StandardCharsets.UTF_8);
+
+        /* mock */
+        when(identifierRepository.findById(IDENTIFIER_1_ID))
+                .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI));
+
+        /* test */
+        final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final String body = (String) response.getBody();
+        assertNotNull(body);
+        assertEquals(compare, body);
+    }
+
     @Test
     public void find_bibliographyIeee0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException,
             RemoteUnavailableException, IdentifierRequestException, IOException {
@@ -364,6 +383,25 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest {
         assertEquals(compare, body);
     }
 
+    @Test
+    public void find_bibliographyIeee3_succeeds() throws IdentifierNotFoundException, QueryNotFoundException,
+            RemoteUnavailableException, IdentifierRequestException, IOException {
+        final String accept = "text/bibliography; style=ieee";
+        final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_ieee3.txt"),
+                StandardCharsets.UTF_8);
+
+        /* mock */
+        when(identifierRepository.findById(IDENTIFIER_1_ID))
+                .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI));
+
+        /* test */
+        final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final String body = (String) response.getBody();
+        assertNotNull(body);
+        assertEquals(compare, body);
+    }
+
     @Test
     public void find_bibliographyBibtex0_succeeds() throws IdentifierNotFoundException, QueryNotFoundException,
             RemoteUnavailableException, IdentifierRequestException, IOException {
@@ -421,6 +459,25 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest {
         assertEquals(compare, body);
     }
 
+    @Test
+    public void find_bibliographyBibtex3_succeeds() throws IdentifierNotFoundException, QueryNotFoundException,
+            RemoteUnavailableException, IdentifierRequestException, IOException {
+        final String accept = "text/bibliography; style=bibtex";
+        final String compare = FileUtils.readFileToString(new File("src/test/resources/bibliography/style_bibtex3.txt"),
+                StandardCharsets.UTF_8);
+
+        /* mock */
+        when(identifierRepository.findById(IDENTIFIER_1_ID))
+                .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI));
+
+        /* test */
+        final ResponseEntity<?> response = persistenceEndpoint.find(IDENTIFIER_1_ID, accept);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final String body = (String) response.getBody();
+        assertNotNull(body);
+        assertEquals(compare, body);
+    }
+
     protected static String inputStreamToString(InputStream inputStream) throws IOException {
         return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
     }
diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServiceUnitTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServiceUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..21505d50dd165dc4353d2dd219b20b5ea17a3640
--- /dev/null
+++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServiceUnitTest.java
@@ -0,0 +1,216 @@
+package at.tuwien.service;
+
+import at.tuwien.BaseUnitTest;
+import at.tuwien.api.identifier.IdentifierCreateDto;
+import at.tuwien.api.identifier.IdentifierDto;
+import at.tuwien.config.DataCiteConfig;
+import at.tuwien.config.EndpointConfig;
+import at.tuwien.config.IndexInitializer;
+import at.tuwien.datacite.DataCiteBody;
+import at.tuwien.datacite.DataCiteData;
+import at.tuwien.datacite.doi.DataCiteDoi;
+import at.tuwien.entities.identifier.Identifier;
+import at.tuwien.exception.*;
+import at.tuwien.repository.jpa.ContainerRepository;
+import at.tuwien.repository.jpa.DatabaseRepository;
+import at.tuwien.repository.jpa.IdentifierRepository;
+import at.tuwien.repository.jpa.ImageRepository;
+import at.tuwien.service.impl.IdentifierServiceImpl;
+import org.apache.http.auth.BasicUserPrincipal;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Answers;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+
+import java.security.Principal;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(SpringExtension.class)
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
+@SpringBootTest
+@ActiveProfiles("doi")
+public class DataCiteIdentifierServiceUnitTest extends BaseUnitTest {
+
+    @MockBean
+    private IndexInitializer indexInitializer;
+
+    @MockBean(answer = Answers.RETURNS_MOCKS)
+    private DataCiteConfig dataCiteConfig;
+
+    @MockBean(answer = Answers.RETURNS_MOCKS)
+    private EndpointConfig endpointConfig;
+
+    @Autowired
+    private ImageRepository imageRepository;
+
+    @Autowired
+    private ContainerRepository containerRepository;
+
+    @Autowired
+    private DatabaseRepository databaseRepository;
+
+    @Autowired
+    private IdentifierRepository identifierRepository;
+
+    @MockBean
+    private RestTemplate restTemplate;
+
+    @MockBean(answer = Answers.RETURNS_SELF)
+    private RestTemplateBuilder restTemplateBuilder;
+
+    @MockBean
+    private IdentifierServiceImpl identifierService;
+
+    @Autowired
+    private IdentifierService dataCiteIdentifierService;
+
+    @BeforeEach
+    public void beforeEach() {
+        imageRepository.save(IMAGE_1);
+        containerRepository.save(CONTAINER_1);
+        databaseRepository.save(DATABASE_1);
+        when(restTemplateBuilder.build()).thenReturn(restTemplate);
+        IDENTIFIER_1.setCreators(null);
+    }
+
+    @Test
+    public void create_database_succeeds()
+            throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException,
+            QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException,
+            IdentifierRequestException {
+        final Principal principal = new BasicUserPrincipal(USER_1_USERNAME);
+        final String bearer = "Bearer abcxyz";
+        final DataCiteBody<DataCiteDoi> response =
+                new DataCiteBody<>(new DataCiteData<>(null, "dois", new DataCiteDoi(IDENTIFIER_1_DOI_NOT_NULL)));
+
+        /* mock */
+        when(identifierService.create(any(IdentifierCreateDto.class), eq(principal), eq(bearer)))
+                .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1));
+        when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class),
+                any(ParameterizedTypeReference.class)))
+                .thenReturn(ResponseEntity.status(HttpStatus.CREATED).body(response));
+
+        /* test */
+        Identifier result = dataCiteIdentifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer);
+        assertTrue(identifierRepository.existsById(result.getId()));
+        assertEquals(IDENTIFIER_1_DOI_NOT_NULL, result.getDoi());
+    }
+
+    @Test
+    public void create_invalidMetadata_fails()
+            throws IdentifierAlreadyExistsException, UserNotFoundException, QueryNotFoundException,
+            DatabaseNotFoundException, RemoteUnavailableException, IdentifierPublishingNotAllowedException,
+            IdentifierRequestException {
+        final Principal principal = new BasicUserPrincipal(USER_1_USERNAME);
+        final String bearer = "Bearer abcxyz";
+
+        /* mock */
+        when(identifierService.create(any(IdentifierCreateDto.class), eq(principal), eq(bearer)))
+                .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1));
+        when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class),
+                any(ParameterizedTypeReference.class)))
+                .thenThrow(HttpClientErrorException.BadRequest.class);
+
+        /* test */
+        assertThrows(IdentifierRequestException.class, () -> {
+            dataCiteIdentifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer);
+        });
+        assertEquals(0, identifierRepository.count());
+    }
+
+    @Test
+    public void create_restClientException_fails()
+            throws IdentifierAlreadyExistsException, UserNotFoundException, QueryNotFoundException,
+            DatabaseNotFoundException, RemoteUnavailableException, IdentifierPublishingNotAllowedException,
+            IdentifierRequestException {
+        final Principal principal = new BasicUserPrincipal(USER_1_USERNAME);
+        final String bearer = "Bearer abcxyz";
+
+        /* mock */
+        when(identifierService.create(any(IdentifierCreateDto.class), eq(principal), eq(bearer)))
+                .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1));
+        when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class),
+                any(ParameterizedTypeReference.class)))
+                .thenThrow(RestClientException.class);
+
+        /* test */
+        assertThrows(InternalError.class, () -> {
+            dataCiteIdentifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer);
+        });
+        assertEquals(0, identifierRepository.count());
+    }
+
+    @Test
+    public void update_existing_succeeds()
+            throws IdentifierRequestException, IdentifierNotFoundException {
+        final DataCiteBody<DataCiteDoi> response =
+                new DataCiteBody<>(new DataCiteData<>(null, "dois", new DataCiteDoi(IDENTIFIER_1_DOI_NOT_NULL)));
+
+        /* mock */
+        when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierDto.class)))
+                .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1_WITH_DOI));
+        when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class),
+                any(ParameterizedTypeReference.class), eq(IDENTIFIER_1_DOI_NOT_NULL)))
+                .thenReturn(ResponseEntity.ok(response));
+
+        /* test */
+        Identifier result = dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI_DTO);
+        assertTrue(identifierRepository.existsById(IDENTIFIER_1_ID));
+        assertEquals(IDENTIFIER_1_DOI_NOT_NULL, result.getDoi());
+    }
+
+    @Test
+    public void update_invalidMetadata_fails()
+            throws IdentifierRequestException, IdentifierNotFoundException {
+
+        /* mock */
+        when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierDto.class)))
+                .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1_WITH_DOI));
+        when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class),
+                any(ParameterizedTypeReference.class), eq(IDENTIFIER_1_DOI_NOT_NULL)))
+                .thenThrow(HttpClientErrorException.BadRequest.class);
+
+        /* test */
+        assertThrows(IdentifierRequestException.class, () -> {
+            dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI_DTO);
+        });
+        assertEquals(0, identifierRepository.count());
+    }
+
+    @Test
+    public void update_restClientException_fails()
+            throws IdentifierRequestException, IdentifierNotFoundException {
+
+        /* mock */
+        when(identifierService.update(eq(IDENTIFIER_1_ID), any(IdentifierDto.class)))
+                .thenAnswer((i) -> identifierRepository.save(IDENTIFIER_1_WITH_DOI));
+        when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class),
+                any(ParameterizedTypeReference.class), eq(IDENTIFIER_1_DOI_NOT_NULL)))
+                .thenThrow(RestClientException.class);
+
+        /* test */
+        assertThrows(InternalError.class, () -> {
+            dataCiteIdentifierService.update(IDENTIFIER_1_ID, IDENTIFIER_1_WITH_DOI_DTO);
+        });
+        assertEquals(0, identifierRepository.count());
+    }
+
+}
diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java
index 7a7663650241f6919bd18d741a487c61b98470c4..4133a24409469526e74a2a87bfca541f73dd4e07 100644
--- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java
+++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceIntegrationTest.java
@@ -3,18 +3,15 @@ package at.tuwien.service;
 import at.tuwien.BaseUnitTest;
 import at.tuwien.api.database.query.QueryDto;
 import at.tuwien.api.identifier.IdentifierDto;
-import at.tuwien.api.identifier.VisibilityTypeDto;
 import at.tuwien.config.IndexInitializer;
 import at.tuwien.entities.identifier.Identifier;
 import at.tuwien.entities.identifier.RelatedIdentifier;
-import at.tuwien.entities.identifier.VisibilityType;
 import at.tuwien.exception.*;
 import at.tuwien.gateway.QueryServiceGateway;
 import at.tuwien.repository.elastic.IdentifierIdxRepository;
 import at.tuwien.repository.jpa.*;
 import lombok.extern.log4j.Log4j2;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -86,7 +83,8 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest {
     @Test
     public void create_subsetRelatedIdentifiers_succeeds()
             throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException,
-            QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException {
+            QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException,
+            IdentifierRequestException {
         final String bearer = "Bearer abcxyz";
 
         /* mock */
@@ -139,7 +137,8 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest {
 
     @Test
     @Transactional(readOnly = true)
-    public void update_succeeds() throws IdentifierNotFoundException, IdentifierPublishingNotAllowedException {
+    public void update_succeeds()
+            throws IdentifierNotFoundException, IdentifierPublishingNotAllowedException, IdentifierRequestException {
 
         /* mock */
         when(identifierIdxRepository.save(any(IdentifierDto.class)))
@@ -155,46 +154,7 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest {
     }
 
     @Test
-    @Disabled("Multiple representation of the same entity")
-    public void publish_everyone_succeeds() throws IdentifierAlreadyPublishedException, IdentifierNotFoundException {
-
-        /* mock */
-        identifierRepository.save(IDENTIFIER_1);
-        identifierRepository.save(IDENTIFIER_2);
-
-        /* mock */
-        when(identifierIdxRepository.save(any(IdentifierDto.class)))
-                .thenReturn(IDENTIFIER_2_DTO);
-
-        /* test */
-        final Identifier response = identifierService.publish(IDENTIFIER_2_ID, VisibilityTypeDto.EVERYONE);
-        assertEquals(IDENTIFIER_2_ID, response.getId());
-        assertEquals(IDENTIFIER_2_TITLE, response.getTitle());
-        assertEquals(IDENTIFIER_2_DESCRIPTION, response.getDescription());
-        assertEquals(IDENTIFIER_2_DOI, response.getDoi());
-        assertEquals(IDENTIFIER_2_PUBLISHER, response.getPublisher());
-        assertEquals(IDENTIFIER_2_CONTAINER_ID, response.getContainerId());
-        assertEquals(IDENTIFIER_2_DATABASE_ID, response.getDatabaseId());
-        assertEquals(IDENTIFIER_2_PUBLICATION_YEAR, response.getPublicationYear());
-        assertEquals(IDENTIFIER_2_PUBLICATION_MONTH, response.getPublicationMonth());
-        assertEquals(IDENTIFIER_2_PUBLICATION_DAY, response.getPublicationDay());
-        assertEquals(VisibilityType.EVERYONE, response.getVisibility());
-    }
-
-    @Test
-    @Disabled("Constraint identifier")
-    public void publish_trusted_succeeds() throws IdentifierAlreadyPublishedException, IdentifierNotFoundException {
-
-        /* mock */
-        when(identifierIdxRepository.save(any(IdentifierDto.class)))
-                .thenReturn(IDENTIFIER_2_DTO);
-
-        /* test */
-        identifierService.publish(IDENTIFIER_2_ID, VisibilityTypeDto.TRUSTED);
-    }
-
-    @Test
-    public void delete_succeeds() throws IdentifierNotFoundException {
+    public void delete_succeeds() throws IdentifierNotFoundException, NotAllowedException {
 
         /* mock */
         doNothing()
@@ -210,7 +170,7 @@ public class IdentifierServiceIntegrationTest extends BaseUnitTest {
 
         /* test */
         assertThrows(IdentifierNotFoundException.class, () -> {
-            identifierService.publish(IDENTIFIER_2_ID, VisibilityTypeDto.EVERYONE);
+            identifierService.delete(IDENTIFIER_2_ID);
         });
     }
 
diff --git a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceUnitTest.java b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceUnitTest.java
index 0ac6b1b7196b67bfe4941e9e77837190f9373b06..9301bd73535d5491fa766f23e728118abff59f22 100644
--- a/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceUnitTest.java
+++ b/dbrepo-identifier-service/rest-service/src/test/java/at/tuwien/service/IdentifierServiceUnitTest.java
@@ -188,65 +188,60 @@ public class IdentifierServiceUnitTest extends BaseUnitTest {
     }
 
     @Test
-    public void create_database_succeeds()
-            throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException,
-            QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException {
-        final Principal principal = new BasicUserPrincipal(USER_1_USERNAME);
-        final String bearer = "Bearer abcxyz";
+    public void update_doiChange_fails() {
 
-        /* mock */
-        when(databaseService.find(DATABASE_1_ID))
-                .thenReturn(DATABASE_1);
-        when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(QueryDto.class)))
-                .thenReturn(ResponseEntity.ok(QUERY_1_DTO));
-        when(userService.findByUsername(USER_1_USERNAME))
-                .thenReturn(USER_1);
-        when(identifierRepository.save(any(Identifier.class)))
-                .thenReturn(IDENTIFIER_1);
-        when(identifierIdxRepository.save(any(IdentifierDto.class)))
-                .thenReturn(IDENTIFIER_1_DTO);
+        IdentifierDto identifierWithNewDoiDto = IdentifierDto.builder().id(IDENTIFIER_1_ID).visibility(VisibilityTypeDto.EVERYONE).doi("10.000/thisisadifferentdoi").build();
 
+        /* mock */
+        when(identifierRepository.findById(IDENTIFIER_1_ID))
+                .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI));
 
         /* test */
-        identifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer);
+        assertThrows(IdentifierRequestException.class, () -> {
+            identifierService.update(IDENTIFIER_1_ID, identifierWithNewDoiDto);
+        });
     }
 
     @Test
-    public void create_publicDatabaseTrustedDataset_fails()
-            throws DatabaseNotFoundException {
-        final Principal principal = new BasicUserPrincipal(USER_1_USERNAME);
-        final String bearer = "Bearer abcxyz";
+    public void update_notVisibleByEveryone_fails() {
 
-        /* mock */
-        when(databaseService.find(DATABASE_3_ID))
-                .thenReturn(DATABASE_3);
-        when(identifierRepository.save(any(Identifier.class)))
-                .thenReturn(IDENTIFIER_3);
+        Identifier identifier = Identifier.builder().id(IDENTIFIER_1_ID).build();
+        IdentifierDto identifierDto = IdentifierDto.builder().id(IDENTIFIER_1_ID).visibility(VisibilityTypeDto.TRUSTED).build();
+        IDENTIFIER_1_DTO.setVisibility(VisibilityTypeDto.TRUSTED);
 
+        /* mock */
+        when(identifierRepository.findById(IDENTIFIER_1_ID))
+                .thenReturn(Optional.of(identifier));
 
         /* test */
-        assertThrows(IdentifierPublishingNotAllowedException.class, () -> {
-            identifierService.create(IDENTIFIER_3_DTO_TRUSTED_REQUEST, principal, bearer);
+        assertThrows(IdentifierRequestException.class, () -> {
+            identifierService.update(IDENTIFIER_1_ID, identifierDto);
         });
     }
 
     @Test
-    public void create_publicDatabaseSelfDataset_fails()
-            throws DatabaseNotFoundException {
+    public void create_database_succeeds()
+            throws DatabaseNotFoundException, UserNotFoundException, IdentifierAlreadyExistsException,
+            QueryNotFoundException, IdentifierPublishingNotAllowedException, RemoteUnavailableException,
+            IdentifierRequestException {
         final Principal principal = new BasicUserPrincipal(USER_1_USERNAME);
         final String bearer = "Bearer abcxyz";
 
         /* mock */
-        when(databaseService.find(DATABASE_3_ID))
-                .thenReturn(DATABASE_3);
+        when(databaseService.find(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(QueryDto.class)))
+                .thenReturn(ResponseEntity.ok(QUERY_1_DTO));
+        when(userService.findByUsername(USER_1_USERNAME))
+                .thenReturn(USER_1);
         when(identifierRepository.save(any(Identifier.class)))
-                .thenReturn(IDENTIFIER_3);
+                .thenReturn(IDENTIFIER_1);
+        when(identifierIdxRepository.save(any(IdentifierDto.class)))
+                .thenReturn(IDENTIFIER_1_DTO);
 
 
         /* test */
-        assertThrows(IdentifierPublishingNotAllowedException.class, () -> {
-            identifierService.create(IDENTIFIER_3_DTO_SELF_REQUEST, principal, bearer);
-        });
+        identifierService.create(IDENTIFIER_1_DTO_REQUEST, principal, bearer);
     }
 
     @Test
@@ -288,7 +283,7 @@ public class IdentifierServiceUnitTest extends BaseUnitTest {
     }
 
     @Test
-    public void delete_succeeds() throws IdentifierNotFoundException {
+    public void delete_succeeds() throws IdentifierNotFoundException, NotAllowedException {
 
         /* mock */
         when(identifierRepository.findById(IDENTIFIER_1_ID))
@@ -320,6 +315,22 @@ public class IdentifierServiceUnitTest extends BaseUnitTest {
         });
     }
 
+    @Test
+    public void delete_withDoi_fails() {
+
+        /* mock */
+        when(identifierRepository.findById(IDENTIFIER_1_ID))
+                .thenReturn(Optional.of(IDENTIFIER_1_WITH_DOI));
+        doNothing()
+                .when(identifierRepository)
+                .delete(IDENTIFIER_1);
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            identifierService.delete(IDENTIFIER_1_ID);
+        });
+    }
+
     @Test
     public void exportMetadata_succeeds() throws IdentifierNotFoundException {
 
@@ -358,30 +369,4 @@ public class IdentifierServiceUnitTest extends BaseUnitTest {
         });
     }
 
-    @Test
-    public void publish_alreadyEveryone_fails() {
-
-        /* mock */
-        when(identifierRepository.findById(IDENTIFIER_1_ID))
-                .thenReturn(Optional.of(IDENTIFIER_1));
-
-        /* test */
-        assertThrows(IdentifierAlreadyPublishedException.class, () -> {
-            identifierService.publish(IDENTIFIER_1_ID, VisibilityTypeDto.SELF);
-        });
-    }
-
-    @Test
-    public void publish_alreadyEveryone2_fails() {
-
-        /* mock */
-        when(identifierRepository.findById(IDENTIFIER_1_ID))
-                .thenReturn(Optional.of(IDENTIFIER_1));
-
-        /* test */
-        assertThrows(IdentifierAlreadyPublishedException.class, () -> {
-            identifierService.publish(IDENTIFIER_1_ID, VisibilityTypeDto.TRUSTED);
-        });
-    }
-
 }
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_apa4.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_apa4.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ce6a65da6b9e838043cf89acd5febd86ae3b6d08
--- /dev/null
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_apa4.txt
@@ -0,0 +1 @@
+M., Mustermann. (2022). Austrian weather data. Austrian Government. https://doi.org/10.1000/183
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex0.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex0.txt
index b59c85b51e49426f8a22404fc0fd9456a301669c..0f797e3861818f4a17cc6487e97f6ce08d2efa48 100644
--- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex0.txt
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex0.txt
@@ -1,7 +1,7 @@
 @misc{dbrepo2022,
   author = {},
   title = {Sweden weather data},
-  howpublished = {\url{http://localhost:3000/pid/4}},
+  url = {\url{http://localhost:3000/pid/4}},
   month = {7},
   year = {2022}
 }
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex1.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex1.txt
index 4feb1be0d63cb77464a6f9ea0ee7a710b8547913..bab1cdadb13192dbf2c16f9dbfebb9fcc8385a36 100644
--- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex1.txt
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex1.txt
@@ -1,7 +1,7 @@
 @misc{dbrepo2022,
   author = {Mustermann, Max},
   title = {Austrian weather data},
-  howpublished = {\url{http://localhost:3000/pid/1}},
+  url = {\url{http://localhost:3000/pid/1}},
   month = {5},
   year = {2022}
 }
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex2.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex2.txt
index 0d7b5e3d7aa746d08084c85600f06e9fb4178ed0..9df96bed21348ede73192f16686e79aecc9c02dc 100644
--- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex2.txt
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex2.txt
@@ -1,7 +1,7 @@
 @misc{dbrepo2022,
   author = {Mustermann, Max and Mustermann, Martina},
   title = {Australian weather data},
-  howpublished = {\url{http://localhost:3000/pid/2}},
+  url = {\url{http://localhost:3000/pid/2}},
   month = {7},
   year = {2022}
 }
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex3.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex3.txt
new file mode 100644
index 0000000000000000000000000000000000000000..44190f2752de38baf1aa1856981729e433a1a4af
--- /dev/null
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_bibtex3.txt
@@ -0,0 +1,7 @@
+@misc{dbrepo2022,
+  author = {Mustermann, Max},
+  title = {Austrian weather data},
+  doi = {10.1000/183},
+  month = {5},
+  year = {2022}
+}
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee0.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee0.txt
index 1d79dc4dfbaeddd7ead4a97cf1a2dd687567fec9..35a5374403f5de61697a0eaab9910352b4aed9a2 100644
--- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee0.txt
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee0.txt
@@ -1 +1 @@
-[1] “Sweden weather data“, Swedish Government, 2022, doi: http://localhost:3000/pid/4.
\ No newline at end of file
+[1] “Sweden weather data“, Swedish Government, 2022, Available: http://localhost:3000/pid/4.
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee1.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee1.txt
index c1376a5ba53376bdba9154900d367a2908a98ea9..ac28d6fdeea46ae4c57e39c4dd6166b26e49703e 100644
--- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee1.txt
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee1.txt
@@ -1 +1 @@
-[1] M. Mustermann, “Austrian weather data“, Austrian Government, 2022, doi: http://localhost:3000/pid/1.
\ No newline at end of file
+[1] M. Mustermann, “Austrian weather data“, Austrian Government, 2022, Available: http://localhost:3000/pid/1.
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee2.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee2.txt
index 5ae90052d725298c58895ff917990a652665bc07..909031b2db36b3f9696af84ae423e33f1ffde199 100644
--- a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee2.txt
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee2.txt
@@ -1 +1 @@
-[1] M. Mustermann and M. Mustermann, “Australian weather data“, Australian Government, 2022, doi: http://localhost:3000/pid/2.
\ No newline at end of file
+[1] M. Mustermann and M. Mustermann, “Australian weather data“, Australian Government, 2022, Available: http://localhost:3000/pid/2.
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee3.txt b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee3.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2576174cf69acc6f7f545b8b6ccfd415d1efd083
--- /dev/null
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/bibliography/style_ieee3.txt
@@ -0,0 +1 @@
+[1] M. Mustermann, “Austrian weather data“, Austrian Government, 2022, 10.1000/183.
\ No newline at end of file
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata0.json b/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata0.json
index c9546518e28cd55888740e8248bd5f433168d8c1..ab803627d4a39d33b65d777a480709d4d18cff9d 100644
--- a/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata0.json
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata0.json
@@ -5,7 +5,7 @@
   "type": "database",
   "title": "Sweden weather data",
   "description": "Selecting all from the weather Sweden table",
-  "doi": "10.1000/184",
+  "doi": null,
   "visibility": "everyone",
   "publisher": "Swedish Government",
   "publication_day": 14,
diff --git a/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata1.json b/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata1.json
index e0eb80bffa0082995c1cf33afb82d3233ad226d4..be2009147bae92bae99101194a405f5ad6d337e0 100644
--- a/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata1.json
+++ b/dbrepo-identifier-service/rest-service/src/test/resources/json/metadata1.json
@@ -8,7 +8,7 @@
   "description": "Selecting all from the weather Austrian table",
   "query": "SELECT `id` FROM `foobar`",
   "query_normalized": "SELECT `id` FROM `foobar`",
-  "doi": "10.1000/182",
+  "doi": null,
   "query_hash": "abc",
   "execution": "2018-11-07T10:59:12.000+00:00",
   "result_hash": "def",
diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/DataCiteConfig.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/DataCiteConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec84c3f4ff382ac41e0f7134217f527ebad87c81
--- /dev/null
+++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/config/DataCiteConfig.java
@@ -0,0 +1,24 @@
+package at.tuwien.config;
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+
+@Getter
+@Profile("doi")
+@Configuration
+public class DataCiteConfig {
+
+    @Value("${fda.datacite.url}")
+    private String url;
+
+    @Value("${fda.datacite.prefix}")
+    private String prefix;
+
+    @Value("${fda.datacite.username}")
+    private String username;
+
+    @Value("${fda.datacite.password}")
+    private String password;
+}
diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/DataCiteMapper.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/DataCiteMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f68ba498c630c9e25998c9af093347fd9a05321
--- /dev/null
+++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/DataCiteMapper.java
@@ -0,0 +1,98 @@
+package at.tuwien.mapper;
+
+import at.tuwien.datacite.doi.*;
+import at.tuwien.entities.database.License;
+import at.tuwien.entities.identifier.Creator;
+import at.tuwien.entities.identifier.Identifier;
+import at.tuwien.entities.identifier.RelatedIdentifier;
+import at.tuwien.utils.EnumToStringConverter;
+import org.mapstruct.*;
+import org.springframework.context.annotation.Profile;
+
+import java.util.List;
+
+@Profile("doi")
+@Mapper(componentModel = "spring", uses = EnumToStringConverter.class)
+public interface DataCiteMapper {
+
+    default <T> List<T> list(T t) {
+        if (t == null) return null;
+        return List.of(t);
+    }
+
+    @Mappings({
+            @Mapping(target = "titles", source = "."),
+            @Mapping(target = "publisher", source = "publisher"),
+            @Mapping(target = "publicationYear", source = "publicationYear"),
+            @Mapping(target = "publicationMonth", source = "publicationMonth"),
+            @Mapping(target = "publicationDay", source = "publicationDay"),
+            @Mapping(target = "language", source = "language"),
+            @Mapping(target = "rightsList",
+                    expression = "java(list(licenseToDoiRights(identifier.getLicense())))"),
+            @Mapping(target = "creators", source = "creators"),
+            @Mapping(target = "relatedIdentifiers", source = "related"),
+    })
+    DataCiteCreateDoi identifierToDataCiteCreateDoi(Identifier identifier);
+
+    default DataCiteCreateDoi identifierToDataCiteCreateDoi(Identifier identifier, String url, String prefix) {
+        return addParametersToCreateDoi(
+                identifierToDataCiteCreateDoi(identifier),
+                url,
+                prefix,
+                DataCiteDoiTypes.DATASET,
+                DataCiteDoiEvent.PUBLISH
+        );
+    }
+
+    DataCiteCreateDoi addParametersToCreateDoi(@MappingTarget DataCiteCreateDoi target,
+                                  String url,
+                                  String prefix,
+                                  DataCiteDoiTypes types,
+                                  DataCiteDoiEvent event);
+
+    default List<DataCiteDoiTitle> identifierToDoiTitles(Identifier identifier) {
+        return List.of(
+                DataCiteDoiTitle.builder().title(identifier.getTitle()).build(),
+                DataCiteDoiTitle.builder()
+                        .title(identifier.getDescription())
+                        .titleType(DataCiteDoiTitle.Type.SUBTITLE)
+                        .build()
+        );
+    }
+
+    @Mappings({
+            @Mapping(target = "rights", source = "identifier"),
+            @Mapping(target = "rightsUri", source = "uri"),
+    })
+    DataCiteDoiRights licenseToDoiRights(License license);
+
+    @Mappings({
+            @Mapping(target = "name", expression = "java(license.getLastname() + \", \" + license.getFirstname())"),
+            @Mapping(target = "givenName", source = "firstname"),
+            @Mapping(target = "familyName", source = "lastname"),
+            @Mapping(target = "affiliation",
+                    expression = "java(list(affiliationStringToDoiCreatorAffiliation(license.getAffiliation())))"),
+            @Mapping(target = "nameIdentifiers",
+                    expression = "java(list(orcidStringToDoiCreatorNameIdentifier(license.getOrcid())))"),
+    })
+    DataCiteDoiCreator creatorToDoiCreator(Creator license);
+
+    @Mappings({
+            @Mapping(target = "name", constant = "affiliation"),
+    })
+    DataCiteDoiCreatorAffiliation affiliationStringToDoiCreatorAffiliation(String affiliation);
+
+    @Mappings({
+            @Mapping(target = "schemeUri", constant = "https://orcid.org"),
+            @Mapping(target = "nameIdentifier", expression = "java(\"https://orcid.org/\" + orcid)"),
+            @Mapping(target = "nameIdentifierScheme", constant = "ORCID"),
+    })
+    DataCiteDoiCreatorNameIdentifier orcidStringToDoiCreatorNameIdentifier(String orcid);
+
+    @Mappings({
+            @Mapping(target = "relatedIdentifier", source = "value"),
+            @Mapping(target = "relatedIdentifierType", source = "type"),
+            @Mapping(target = "relationType", source = "relation"),
+    })
+    DataCiteDoiRelatedIdentifier relatedIdentifierToDoiRelatedIdentifier(RelatedIdentifier relatedIdentifier);
+}
diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java
index 4c6dd57ac46ee6af851a7ed561db89c3c1b320ad..08fa62a8ec066cf57a1304a13e70e12ae27b9f68 100644
--- a/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java
+++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/mapper/IdentifierMapper.java
@@ -1,8 +1,10 @@
 package at.tuwien.mapper;
 
 import at.tuwien.api.identifier.*;
-import at.tuwien.entities.database.Database;
-import at.tuwien.entities.identifier.*;
+import at.tuwien.entities.identifier.Creator;
+import at.tuwien.entities.identifier.Identifier;
+import at.tuwien.entities.identifier.IdentifierType;
+import at.tuwien.entities.identifier.RelatedIdentifier;
 import org.mapstruct.Mapper;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -31,9 +33,6 @@ public interface IdentifierMapper {
     @Transactional
     RelatedIdentifier relatedIdentifierCreateDtoToRelatedIdentifier(RelatedIdentifierCreateDto data);
 
-    @Transactional
-    VisibilityType visibilityTypeDtoToVisibilityType(VisibilityTypeDto data);
-
     IdentifierType identifierTypeDtoToIdentifierType(IdentifierTypeDto data);
 
     default String identifierToLocationUrl(String baseUrl, Identifier data) {
@@ -46,11 +45,4 @@ public interface IdentifierMapper {
         }
     }
 
-    default VisibilityType databaseToVisibilityType(Database data) {
-        if (data.getIsPublic()) {
-            return VisibilityType.EVERYONE;
-        }
-        return VisibilityType.SELF;
-    }
-
 }
diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java
index f052f49c15bdf9ebe2e7557f57eee59dd810f409..95071320600402cf97b75013593a429c237489a7 100644
--- a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java
+++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/IdentifierService.java
@@ -1,12 +1,12 @@
 package at.tuwien.service;
 
-import at.tuwien.api.identifier.*;
-import at.tuwien.ExportResource;
+import at.tuwien.api.identifier.BibliographyTypeDto;
+import at.tuwien.api.identifier.IdentifierCreateDto;
+import at.tuwien.api.identifier.IdentifierDto;
 import at.tuwien.entities.identifier.Identifier;
 import at.tuwien.exception.*;
 import org.springframework.core.io.InputStreamResource;
 import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
 
 import java.security.Principal;
 import java.util.List;
@@ -58,7 +58,7 @@ public interface IdentifierService {
     Identifier create(IdentifierCreateDto data, Principal principal, String authorization)
             throws IdentifierPublishingNotAllowedException, QueryNotFoundException,
             RemoteUnavailableException, IdentifierAlreadyExistsException, UserNotFoundException,
-            DatabaseNotFoundException;
+            DatabaseNotFoundException, IdentifierRequestException;
 
     /**
      * Finds an identifier by given id in the metadata database.
@@ -109,28 +109,15 @@ public interface IdentifierService {
      * @param data         The metadata.
      * @return The updated identifier if successful.
      * @throws IdentifierNotFoundException             TThe identifier was not found in the metadata database or was deleted.
-     * @throws IdentifierPublishingNotAllowedException The identifier contained a visibility change which is not allowed here.
      */
-    Identifier update(Long identifierId, IdentifierDto data) throws IdentifierNotFoundException,
-            IdentifierPublishingNotAllowedException;
-
-    /**
-     * Publishes the identifier for a given identifier id in the metadata database.
-     *
-     * @param identifierId The identifier id.
-     * @param visibility   The new visibility.
-     * @return The updated identifier from the metadata database.
-     * @throws IdentifierNotFoundException         The identifier was not found in the metadata database or was deleted.
-     * @throws IdentifierAlreadyPublishedException The identifier is already published (=EVERYONE) and cannot be un-published.
-     */
-    Identifier publish(Long identifierId, VisibilityTypeDto visibility) throws IdentifierNotFoundException,
-            IdentifierAlreadyPublishedException;
+    Identifier update(Long identifierId, IdentifierDto data) throws IdentifierNotFoundException, IdentifierRequestException;
 
     /**
      * Soft-deletes an identifier for a given id in the metadata database. Does not actually remove the entity from the database, but sets it as deleted.
      *
      * @param identifierId The identifier id.
      * @throws IdentifierNotFoundException The identifier was not found in the metadata database or was deleted.
+     * @throws NotAllowedException Identifiers with a valid DOI cannot be deleted.
      */
-    void delete(Long identifierId) throws IdentifierNotFoundException;
+    void delete(Long identifierId) throws IdentifierNotFoundException, NotAllowedException;
 }
diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..37a02ceb6f4f592a063c943b8637f41fffc7b37e
--- /dev/null
+++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java
@@ -0,0 +1,212 @@
+package at.tuwien.service.impl;
+
+import at.tuwien.api.identifier.BibliographyTypeDto;
+import at.tuwien.api.identifier.IdentifierCreateDto;
+import at.tuwien.api.identifier.IdentifierDto;
+import at.tuwien.config.DataCiteConfig;
+import at.tuwien.config.EndpointConfig;
+import at.tuwien.datacite.DataCiteBody;
+import at.tuwien.datacite.DataCiteData;
+import at.tuwien.datacite.doi.DataCiteCreateDoi;
+import at.tuwien.datacite.doi.DataCiteDoi;
+import at.tuwien.entities.identifier.Identifier;
+import at.tuwien.exception.*;
+import at.tuwien.gateway.QueryServiceGateway;
+import at.tuwien.mapper.DataCiteMapper;
+import at.tuwien.mapper.IdentifierMapper;
+import at.tuwien.repository.elastic.IdentifierIdxRepository;
+import at.tuwien.repository.jpa.IdentifierRepository;
+import at.tuwien.repository.jpa.RelatedIdentifierRepository;
+import at.tuwien.service.DatabaseService;
+import at.tuwien.service.IdentifierService;
+import at.tuwien.service.UserService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.annotation.Profile;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.core.io.InputStreamResource;
+import org.springframework.http.*;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.DefaultUriBuilderFactory;
+import org.thymeleaf.TemplateEngine;
+
+import javax.transaction.Transactional;
+import java.security.Principal;
+import java.util.List;
+import java.util.Optional;
+
+@Slf4j
+@Primary
+@Profile("doi")
+@Service
+public class DataCiteIdentifierServiceImpl implements IdentifierService {
+
+    private final DataCiteConfig dataCiteConfig;
+    private final EndpointConfig endpointConfig;
+    private final DataCiteMapper dataCiteMapper;
+    private final RestTemplateBuilder restTemplateBuilder;
+    private final IdentifierRepository identifierRepository;
+    private final IdentifierService identifierService;
+
+    public DataCiteIdentifierServiceImpl(DataCiteConfig dataCiteConfig, DataCiteMapper dataCiteMapper,
+                                         RestTemplateBuilder restTemplateBuilder, EndpointConfig endpointConfig,
+                                         IdentifierRepository identifierRepository, IdentifierServiceImpl identifierService) {
+        this.dataCiteConfig = dataCiteConfig;
+        this.dataCiteMapper = dataCiteMapper;
+        this.restTemplateBuilder =
+                restTemplateBuilder.basicAuthentication(dataCiteConfig.getUsername(), dataCiteConfig.getPassword())
+                        .uriTemplateHandler(new DefaultUriBuilderFactory(dataCiteConfig.getUrl()));
+        this.endpointConfig = endpointConfig;
+        this.identifierRepository = identifierRepository;
+        this.identifierService = identifierService;
+    }
+
+    @Override
+    public List<Identifier> findAll(Long databaseId, Long queryId) throws IdentifierNotFoundException {
+        return identifierService.findAll(databaseId, queryId);
+    }
+
+    @Override
+    public Identifier find(Long databaseId, Long queryId) throws IdentifierNotFoundException {
+        return identifierService.find(databaseId, queryId);
+    }
+
+    @Override
+    public List<Identifier> findAll() {
+        return identifierService.findAll();
+    }
+
+    @Override
+    @Transactional(rollbackOn = {Exception.class})
+    public Identifier create(IdentifierCreateDto data, Principal principal, String authorization)
+            throws IdentifierPublishingNotAllowedException, QueryNotFoundException, RemoteUnavailableException,
+            IdentifierAlreadyExistsException, UserNotFoundException, DatabaseNotFoundException,
+            IdentifierRequestException {
+        Identifier identifier = identifierService.create(data, principal, authorization);
+        RestTemplate restTemplate = restTemplateBuilder.build();
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        headers.setBasicAuth(dataCiteConfig.getUsername(), dataCiteConfig.getPassword());
+        HttpEntity<DataCiteBody<DataCiteCreateDoi>> request = new HttpEntity<>(
+                DataCiteBody.<DataCiteCreateDoi>builder()
+                        .data(DataCiteData.<DataCiteCreateDoi>builder()
+                                .type("dois")
+                                .attributes(dataCiteMapper.identifierToDataCiteCreateDoi(identifier,
+                                        endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId(),
+                                        dataCiteConfig.getPrefix()))
+                                .build())
+                        .build(),
+                headers
+        );
+
+        try {
+            ResponseEntity<DataCiteBody<DataCiteDoi>> response = restTemplate.exchange("dois", HttpMethod.POST,
+                    request,
+                    new ParameterizedTypeReference<>() {
+                    }
+            );
+
+            if(response.getStatusCode() != HttpStatus.CREATED || response.getBody() == null) {
+                log.error("Could not successfully create DOI. Response: {}", response);
+                throw new IdentifierRequestException("Could not successfully create DOI.");
+            }
+
+            identifier.setDoi(response.getBody().getData().getAttributes().getDoi());
+            this.identifierRepository.save(identifier);
+        } catch(HttpClientErrorException e) {
+            log.error("Invalid DOI metadata.", e);
+            throw new IdentifierRequestException("Invalid DOI metadata.", e);
+        } catch(RestClientException e) {
+            log.error("Could not fulfil request to DataCite server.", e);
+            throw new InternalError("Could not fulfil request to DataCite server.", e);
+        }
+
+        return identifier;
+    }
+
+    @Override
+    public Identifier find(Long identifierId) throws IdentifierNotFoundException {
+        return identifierService.find(identifierId);
+    }
+
+    @Override
+    public InputStreamResource exportMetadata(Long id) throws IdentifierNotFoundException {
+        return identifierService.exportMetadata(id);
+    }
+
+    @Override
+    public String exportBibliography(Long id, BibliographyTypeDto style)
+            throws IdentifierNotFoundException, IdentifierRequestException {
+        return identifierService.exportBibliography(id, style);
+    }
+
+    @Override
+    public InputStreamResource exportResource(Long identifierId)
+            throws IdentifierNotFoundException, QueryNotFoundException, RemoteUnavailableException,
+            IdentifierRequestException {
+        return identifierService.exportResource(identifierId);
+    }
+
+    @Override
+    @Transactional(rollbackOn = {Exception.class})
+    public Identifier update(Long identifierId, IdentifierDto data)
+            throws IdentifierNotFoundException, IdentifierRequestException {
+        Identifier identifier = identifierService.update(identifierId, data);
+        if(identifier.getDoi() == null) {
+            return identifier;
+        }
+
+        RestTemplate restTemplate = restTemplateBuilder.build();
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        headers.setBasicAuth(dataCiteConfig.getUsername(), dataCiteConfig.getPassword());
+        HttpEntity<DataCiteBody<DataCiteCreateDoi>> request = new HttpEntity<>(
+                DataCiteBody.<DataCiteCreateDoi>builder()
+                        .data(DataCiteData.<DataCiteCreateDoi>builder()
+                                .type("dois")
+                                .attributes(dataCiteMapper.identifierToDataCiteCreateDoi(identifier,
+                                        endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId(),
+                                        dataCiteConfig.getPrefix()))
+                                .build())
+                        .build(),
+                headers
+        );
+
+        try {
+            ResponseEntity<DataCiteBody<DataCiteDoi>> response = restTemplate.exchange("dois/{doi}", HttpMethod.PUT,
+                    request,
+                    new ParameterizedTypeReference<>() {
+                    },
+                    identifier.getDoi()
+            );
+
+            if(response.getStatusCode() != HttpStatus.OK || response.getBody() == null) {
+                log.error("Could not successfully create DOI. Response: {}", response);
+                throw new IdentifierRequestException("Could not successfully create DOI.");
+            }
+
+            identifier.setDoi(response.getBody().getData().getAttributes().getDoi());
+            this.identifierRepository.save(identifier);
+        } catch(HttpClientErrorException e) {
+            log.error("Invalid DOI metadata.", e);
+            throw new IdentifierRequestException("Invalid DOI metadata.", e);
+        } catch(RestClientException e) {
+            log.error("Could not fulfil request to DataCite server.", e);
+            throw new InternalError("Could not fulfil request to DataCite server.", e);
+        }
+
+        return identifier;
+    }
+
+    @Override
+    public void delete(Long identifierId) throws IdentifierNotFoundException, NotAllowedException {
+        identifierService.delete(identifierId);
+    }
+
+}
diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
index 3c0a8f0cecb4b1400ad1cb6274353514e889e876..f33ff8911b976e33338e6af8f6f8428c00fb1084 100644
--- a/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
+++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
@@ -4,7 +4,10 @@ import at.tuwien.api.database.query.QueryDto;
 import at.tuwien.api.identifier.*;
 import at.tuwien.config.EndpointConfig;
 import at.tuwien.entities.database.Database;
-import at.tuwien.entities.identifier.*;
+import at.tuwien.entities.identifier.Creator;
+import at.tuwien.entities.identifier.Identifier;
+import at.tuwien.entities.identifier.IdentifierType;
+import at.tuwien.entities.identifier.RelatedIdentifier;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
 import at.tuwien.gateway.QueryServiceGateway;
@@ -17,6 +20,7 @@ import at.tuwien.service.IdentifierService;
 import at.tuwien.service.UserService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.IOUtils;
+import org.springframework.context.annotation.Profile;
 import org.springframework.core.io.InputStreamResource;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -96,13 +100,10 @@ public class IdentifierServiceImpl implements IdentifierService {
     @Transactional
     public Identifier create(IdentifierCreateDto data, Principal principal, String authorization)
             throws QueryNotFoundException, RemoteUnavailableException, IdentifierAlreadyExistsException,
-            UserNotFoundException, DatabaseNotFoundException, IdentifierPublishingNotAllowedException {
+            UserNotFoundException, DatabaseNotFoundException, IdentifierPublishingNotAllowedException,
+            IdentifierRequestException {
         /* check */
         final Database database = databaseService.find(data.getDbid());
-        if (database.getIsPublic() && !data.getVisibility().equals(VisibilityTypeDto.EVERYONE)) {
-            log.error("Identifier cannot restrict the result set");
-            throw new IdentifierPublishingNotAllowedException("Identifier cannot restrict the result set");
-        }
         if (data.getType().equals(IdentifierTypeDto.DATABASE) && identifierRepository.existsByDatabaseIdAndType(data.getDbid(), IdentifierType.DATABASE)) {
             log.error("Identifier already issued for database with id {}", data.getDbid());
             throw new IdentifierAlreadyExistsException("Database identifier already exists");
@@ -120,7 +121,6 @@ public class IdentifierServiceImpl implements IdentifierService {
         if (data.getType().equals(IdentifierTypeDto.SUBSET)) {
             log.debug("identifier describes a subset");
             final QueryDto query = queryServiceGateway.find(data.getCid(), data.getDbid(), data, authorization);
-            tmp.setVisibility(identifierMapper.visibilityTypeDtoToVisibilityType(data.getVisibility()));
             tmp.setQuery(query.getQuery());
             tmp.setQueryId(query.getId());
             tmp.setQueryNormalized(query.getQueryNormalized());
@@ -128,9 +128,6 @@ public class IdentifierServiceImpl implements IdentifierService {
             tmp.setExecution(query.getExecution());
             tmp.setResultNumber(query.getResultNumber());
             tmp.setResultHash(query.getResultHash());
-        } else if (data.getType().equals(IdentifierTypeDto.DATABASE)) {
-            log.debug("identifier describes a database");
-            tmp.setVisibility(identifierMapper.databaseToVisibilityType(database));
         }
         /* create in metadata database */
         final Identifier entity = identifierRepository.save(tmp);
@@ -181,7 +178,13 @@ public class IdentifierServiceImpl implements IdentifierService {
         final Identifier identifier = find(id);
         /* context */
         final Context context = new Context();
-        context.setVariable("doi", endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId());
+        if(identifier.getDoi() != null) {
+            context.setVariable("identifierType", "DOI");
+            context.setVariable("identifier", identifier.getDoi());
+        } else {
+            context.setVariable("identifierType", "PID");
+            context.setVariable("identifier", endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId());
+        }
         context.setVariable("creators", identifier.getCreators());
         context.setVariable("title", identifier.getTitle());
         context.setVariable("publisher", identifier.getPublisher());
@@ -205,7 +208,13 @@ public class IdentifierServiceImpl implements IdentifierService {
         final Identifier identifier = find(id);
         /* context */
         final Context context = new Context();
-        context.setVariable("doi", endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId());
+        if(identifier.getDoi() != null) {
+            context.setVariable("identifierType", "doi");
+            context.setVariable("identifier", identifier.getDoi());
+        } else {
+            context.setVariable("identifierType", "url");
+            context.setVariable("identifier", endpointConfig.getWebsiteUrl() + "/pid/" + identifier.getId());
+        }
         context.setVariable("creator", identifier.getCreator());
         context.setVariable("creators", identifier.getCreators());
         context.setVariable("title", identifier.getTitle());
@@ -245,9 +254,15 @@ public class IdentifierServiceImpl implements IdentifierService {
     @Override
     @Transactional
     public Identifier update(Long identifierId, IdentifierDto data)
-            throws IdentifierNotFoundException {
+            throws IdentifierNotFoundException, IdentifierRequestException {
         /* check */
-        find(identifierId);
+        Identifier old = find(identifierId);
+        if(data.getVisibility() != VisibilityTypeDto.EVERYONE) {
+            throw new IdentifierRequestException("Cannot set visibility to other value than \"EVERYONE\".");
+        }
+        if(data.getDoi() != null && !data.getDoi().equals(old.getDoi())) {
+            throw new IdentifierRequestException("The DOI of an identifier cannot be changed.");
+        }
         /* map */
         final Identifier entity = identifierMapper.identifierDtoToIdentifier(data);
         entity.getCreators()
@@ -264,29 +279,12 @@ public class IdentifierServiceImpl implements IdentifierService {
 
     @Override
     @Transactional
-    public Identifier publish(Long identifierId, VisibilityTypeDto visibility)
-            throws IdentifierNotFoundException, IdentifierAlreadyPublishedException {
-        final Identifier identifier = find(identifierId);
-        if (identifier.getVisibility().equals(VisibilityType.EVERYONE)) {
-            /* once published, the identifier cannot be reverted, it is persistent! */
-            log.error("Identifier with id {} is already published for everyone", identifier.getId());
-            throw new IdentifierAlreadyPublishedException("Identifier with id " + identifier.getId() + " is already published for everyone");
-        }
-        identifier.setVisibility(identifierMapper.visibilityTypeDtoToVisibilityType(visibility));
-        final Identifier entity = identifierRepository.save(identifier);
-        log.info("Published identifier with id {}", identifierId);
-        log.trace("published identifier {}", entity);
-        /* elastic search */
-        identifierIdxRepository.save(identifierMapper.identifierToIdentifierDto(entity));
-        log.info("Published identifier with id {} in elastic search", identifierId);
-        return entity;
-    }
-
-    @Override
-    @Transactional
-    public void delete(Long identifierId) throws IdentifierNotFoundException {
+    public void delete(Long identifierId) throws IdentifierNotFoundException, NotAllowedException {
         /* check */
         final Identifier identifier = find(identifierId);
+        if(identifier.getDoi() != null) {
+            throw new NotAllowedException("Identifiers with a DOI cannot be deleted.");
+        }
         /* delete */
         identifierRepository.delete(identifier);
         log.info("Deleted identifier with id {}", identifierId);
diff --git a/dbrepo-identifier-service/services/src/main/java/at/tuwien/utils/EnumToStringConverter.java b/dbrepo-identifier-service/services/src/main/java/at/tuwien/utils/EnumToStringConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..2e53ae6cdc17dcf9b915bc9aef3793701cba98e8
--- /dev/null
+++ b/dbrepo-identifier-service/services/src/main/java/at/tuwien/utils/EnumToStringConverter.java
@@ -0,0 +1,15 @@
+package at.tuwien.utils;
+
+import org.mapstruct.TargetType;
+
+public class EnumToStringConverter {
+
+    public static String convert(Enum<?> source) {
+        return source == null ? null : source.toString();
+    }
+
+    public static <E extends Enum<E>> E convert(String source, @TargetType Class<E> enumType) {
+        // You probably need something else here as the methods are not symmetrical
+        return source == null ? null : Enum.valueOf( enumType, source );
+    }
+}
diff --git a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java
index eeb7c449a8098080f8abebd9da7dc76507c1ff1a..67a9e858e95b8dc72b9074870de4e697346e72f8 100644
--- a/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java
+++ b/dbrepo-metadata-db/api/src/main/java/at/tuwien/api/identifier/IdentifierCreateDto.java
@@ -11,6 +11,7 @@ import lombok.Setter;
 
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
+import javax.validation.constraints.NotEmpty;
 import java.util.List;
 
 @Data
@@ -37,13 +38,6 @@ public class IdentifierCreateDto {
     @Schema(example = "Air quality reports at Stephansplatz, Vienna")
     private String description;
 
-    @NotNull
-    @Schema(example = "everyone")
-    private VisibilityTypeDto visibility;
-
-    @Schema(example = "10.1038/nphys1170")
-    private String doi;
-
     @JsonProperty("publication_day")
     @Schema(example = "15")
     private Integer publicationDay;
@@ -65,6 +59,7 @@ public class IdentifierCreateDto {
     private Integer publicationYear;
 
     @NotNull
+    @NotEmpty
     private List<CreatorCreateDto> creators;
 
     @JsonProperty("related_identifiers")
diff --git a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java
index 95ffd29e5907c9585f1f64402e396dc5d24065d1..5cdd7a5aeda30d8eda045d4f9551eb259cd37edd 100644
--- a/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java
+++ b/dbrepo-metadata-db/entities/src/main/java/at/tuwien/entities/identifier/Identifier.java
@@ -109,7 +109,8 @@ public class Identifier implements Serializable {
 
     @Column(nullable = false, columnDefinition = "enum('EVERYONE', 'TRUSTED', 'SELF')")
     @Enumerated(EnumType.STRING)
-    private VisibilityType visibility = VisibilityType.SELF;
+    @Builder.Default
+    private VisibilityType visibility = VisibilityType.EVERYONE;
 
     @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
     @JoinColumns({
diff --git a/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java
index f856227bf321a72134343b1a10d10c4db2b38261..4397e90f4ff82391485d0f18bd0a9d33ca35dda5 100644
--- a/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java
+++ b/dbrepo-metadata-db/test/src/main/java/at/tuwien/test/BaseTest.java
@@ -1875,7 +1875,7 @@ public abstract class BaseTest {
     public final static Long QUERY_1_ID = 1L;
     public final static String QUERY_1_STATEMENT = "SELECT `id`, `date`, `location`, `mintemp`, `rainfall` FROM " +
             "`weather_aus`";
-    public final static String QUERY_1_DOI = "1111/1";
+    public final static String QUERY_1_DOI = null;
     public final static Long QUERY_1_CONTAINER_ID = CONTAINER_1_ID;
     public final static Long QUERY_1_DATABASE_ID = DATABASE_1_ID;
     public final static Long QUERY_1_RESULT_NUMBER = 2L;
@@ -3634,7 +3634,8 @@ public abstract class BaseTest {
     public final static String IDENTIFIER_1_DESCRIPTION_MODIFY = "Selecting some from the weather Austrian table";
     public final static String IDENTIFIER_1_TITLE = "Austrian weather data";
     public final static String IDENTIFIER_1_TITLE_MODIFY = "Austrian weather some data";
-    public final static String IDENTIFIER_1_DOI = "10.1000/182";
+    public final static String IDENTIFIER_1_DOI = null;
+    public final static String IDENTIFIER_1_DOI_NOT_NULL = "10.1000/183";
     public final static VisibilityType IDENTIFIER_1_VISIBILITY = VisibilityType.EVERYONE;
     public final static VisibilityTypeDto IDENTIFIER_1_VISIBILITY_DTO = VisibilityTypeDto.EVERYONE;
     public final static Instant IDENTIFIER_1_CREATED = Instant.ofEpochSecond(1641588352) /* 2022-01-07 20:45:52 */;
@@ -3693,6 +3694,30 @@ public abstract class BaseTest {
             .creators(List.of(IDENTIFIER_1_CREATOR_1))
             .build();
 
+    public final static Identifier IDENTIFIER_1_WITH_DOI = Identifier.builder()
+            .id(IDENTIFIER_1_ID)
+            .containerId(IDENTIFIER_1_CONTAINER_ID)
+            .databaseId(IDENTIFIER_1_DATABASE_ID)
+            .queryId(IDENTIFIER_1_QUERY_ID)
+            .description(IDENTIFIER_1_DESCRIPTION)
+            .title(IDENTIFIER_1_TITLE)
+            .doi(IDENTIFIER_1_DOI_NOT_NULL)
+            .visibility(IDENTIFIER_1_VISIBILITY)
+            .created(IDENTIFIER_1_CREATED)
+            .lastModified(IDENTIFIER_1_MODIFIED)
+            .execution(IDENTIFIER_1_EXECUTION)
+            .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
+            .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
+            .queryHash(IDENTIFIER_1_QUERY_HASH)
+            .resultHash(IDENTIFIER_1_RESULT_HASH)
+            .query(IDENTIFIER_1_QUERY)
+            .queryNormalized(IDENTIFIER_1_NORMALIZED)
+            .resultNumber(IDENTIFIER_1_RESULT_NUMBER)
+            .publisher(IDENTIFIER_1_PUBLISHER)
+            .type(IDENTIFIER_1_TYPE)
+            .creators(List.of(IDENTIFIER_1_CREATOR_1))
+            .build();
+
     public final static IdentifierDto IDENTIFIER_1_DTO = IdentifierDto.builder()
             .id(IDENTIFIER_1_ID)
             .containerId(IDENTIFIER_1_CONTAINER_ID)
@@ -3718,13 +3743,38 @@ public abstract class BaseTest {
             .creators(List.of(IDENTIFIER_1_CREATOR_1_DTO))
             .build();
 
+    public final static IdentifierDto IDENTIFIER_1_WITH_DOI_DTO = IdentifierDto.builder()
+            .id(IDENTIFIER_1_ID)
+            .containerId(IDENTIFIER_1_CONTAINER_ID)
+            .databaseId(IDENTIFIER_1_DATABASE_ID)
+            .queryId(IDENTIFIER_1_QUERY_ID)
+            .description(IDENTIFIER_1_DESCRIPTION)
+            .title(IDENTIFIER_1_TITLE)
+            .doi(IDENTIFIER_1_DOI_NOT_NULL)
+            .visibility(IDENTIFIER_1_VISIBILITY_DTO)
+            .created(IDENTIFIER_1_CREATED)
+            .lastModified(IDENTIFIER_1_MODIFIED)
+            .execution(IDENTIFIER_1_EXECUTION)
+            .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
+            .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
+            .queryHash(IDENTIFIER_1_QUERY_HASH)
+            .resultHash(IDENTIFIER_1_RESULT_HASH)
+            .query(IDENTIFIER_1_QUERY)
+            .queryNormalized(IDENTIFIER_1_NORMALIZED)
+            .resultNumber(IDENTIFIER_1_RESULT_NUMBER)
+            .publisher(IDENTIFIER_1_PUBLISHER)
+            .type(IDENTIFIER_1_TYPE_DTO)
+            .creator(USER_1_DTO)
+            .creators(List.of(IDENTIFIER_1_CREATOR_1_DTO))
+            .build();
+
     public final static Long IDENTIFIER_2_ID = 2L;
     public final static Long IDENTIFIER_2_QUERY_ID = QUERY_2_ID;
     public final static Long IDENTIFIER_2_CONTAINER_ID = CONTAINER_2_ID;
     public final static Long IDENTIFIER_2_DATABASE_ID = DATABASE_2_ID;
     public final static String IDENTIFIER_2_DESCRIPTION = "Selecting all from the weather Austria table";
     public final static String IDENTIFIER_2_TITLE = "Australian weather data";
-    public final static String IDENTIFIER_2_DOI = "10.1000/183";
+    public final static String IDENTIFIER_2_DOI = null;
     public final static VisibilityType IDENTIFIER_2_VISIBILITY = VisibilityType.EVERYONE;
     public final static VisibilityTypeDto IDENTIFIER_2_VISIBILITY_DTO = VisibilityTypeDto.EVERYONE;
     public final static Instant IDENTIFIER_2_CREATED = Instant.ofEpochSecond(1641588352);
@@ -3917,8 +3967,6 @@ public abstract class BaseTest {
             .dbid(IDENTIFIER_1_DATABASE_ID)
             .description(IDENTIFIER_1_DESCRIPTION)
             .title(IDENTIFIER_1_TITLE)
-            .doi(IDENTIFIER_1_DOI)
-            .visibility(IDENTIFIER_1_VISIBILITY_DTO)
             .relatedIdentifiers(List.of())
             .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
@@ -3932,8 +3980,6 @@ public abstract class BaseTest {
             .dbid(IDENTIFIER_1_DATABASE_ID)
             .description(IDENTIFIER_1_DESCRIPTION)
             .title(IDENTIFIER_1_TITLE)
-            .doi(IDENTIFIER_1_DOI)
-            .visibility(VisibilityTypeDto.TRUSTED)
             .relatedIdentifiers(List.of())
             .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
@@ -3947,8 +3993,6 @@ public abstract class BaseTest {
             .dbid(IDENTIFIER_1_DATABASE_ID)
             .description(IDENTIFIER_1_DESCRIPTION)
             .title(IDENTIFIER_1_TITLE)
-            .doi(IDENTIFIER_1_DOI)
-            .visibility(VisibilityTypeDto.SELF)
             .relatedIdentifiers(List.of())
             .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
@@ -3985,8 +4029,6 @@ public abstract class BaseTest {
             .dbid(IDENTIFIER_2_DATABASE_ID)
             .description(IDENTIFIER_2_DESCRIPTION)
             .title(IDENTIFIER_2_TITLE)
-            .doi(IDENTIFIER_2_DOI)
-            .visibility(IDENTIFIER_2_VISIBILITY_DTO)
             .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_2_CREATE_DTO))
             .publicationDay(IDENTIFIER_2_PUBLICATION_DAY)
             .publicationMonth(IDENTIFIER_2_PUBLICATION_MONTH)
@@ -4002,7 +4044,7 @@ public abstract class BaseTest {
     public final static Long IDENTIFIER_3_DATABASE_ID = DATABASE_3_ID;
     public final static String IDENTIFIER_3_DESCRIPTION = "Selecting all from the weather Norwegian table";
     public final static String IDENTIFIER_3_TITLE = "Norwegian weather data";
-    public final static String IDENTIFIER_3_DOI = "10.1000/183";
+    public final static String IDENTIFIER_3_DOI = null;
     public final static VisibilityType IDENTIFIER_3_VISIBILITY = VisibilityType.EVERYONE;
     public final static VisibilityTypeDto IDENTIFIER_3_VISIBILITY_DTO = VisibilityTypeDto.EVERYONE;
     public final static Instant IDENTIFIER_3_CREATED = Instant.ofEpochSecond(1641588352);
@@ -4128,8 +4170,6 @@ public abstract class BaseTest {
             .dbid(IDENTIFIER_3_DATABASE_ID)
             .description(IDENTIFIER_3_DESCRIPTION)
             .title(IDENTIFIER_3_TITLE)
-            .doi(IDENTIFIER_3_DOI)
-            .visibility(IDENTIFIER_3_VISIBILITY_DTO)
             .relatedIdentifiers(List.of())
             .publicationMonth(IDENTIFIER_3_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_3_PUBLICATION_YEAR)
@@ -4143,8 +4183,6 @@ public abstract class BaseTest {
             .dbid(IDENTIFIER_3_DATABASE_ID)
             .description(IDENTIFIER_3_DESCRIPTION)
             .title(IDENTIFIER_3_TITLE)
-            .doi(IDENTIFIER_3_DOI)
-            .visibility(VisibilityTypeDto.TRUSTED)
             .relatedIdentifiers(List.of())
             .publicationMonth(IDENTIFIER_3_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_3_PUBLICATION_YEAR)
@@ -4158,8 +4196,6 @@ public abstract class BaseTest {
             .dbid(IDENTIFIER_3_DATABASE_ID)
             .description(IDENTIFIER_3_DESCRIPTION)
             .title(IDENTIFIER_3_TITLE)
-            .doi(IDENTIFIER_3_DOI)
-            .visibility(VisibilityTypeDto.SELF)
             .relatedIdentifiers(List.of())
             .publicationMonth(IDENTIFIER_3_PUBLICATION_MONTH)
             .publicationYear(IDENTIFIER_3_PUBLICATION_YEAR)
@@ -4173,7 +4209,7 @@ public abstract class BaseTest {
     public final static Long IDENTIFIER_4_DATABASE_ID = DATABASE_4_ID;
     public final static String IDENTIFIER_4_DESCRIPTION = "Selecting all from the weather Sweden table";
     public final static String IDENTIFIER_4_TITLE = "Sweden weather data";
-    public final static String IDENTIFIER_4_DOI = "10.1000/184";
+    public final static String IDENTIFIER_4_DOI = null;
     public final static VisibilityType IDENTIFIER_4_VISIBILITY = VisibilityType.EVERYONE;
     public final static Instant IDENTIFIER_4_CREATED = Instant.ofEpochSecond(1641588352);
     public final static Instant IDENTIFIER_4_MODIFIED = Instant.ofEpochSecond(1541588352);
diff --git a/dbrepo-ui/.env.example b/dbrepo-ui/.env.example
index 6d67c5eca979378377bc21dd27644ba038a41d86..28ec04e8937e8362c56a46c890de7a142ddc285c 100644
--- a/dbrepo-ui/.env.example
+++ b/dbrepo-ui/.env.example
@@ -6,3 +6,4 @@ BROKER_USERNAME=fda
 BROKER_PASSWORD=fda
 SANDBOX=false
 SHARED_FILESYSTEM=/tmp
+DOI_URL="https://doi.org"
diff --git a/dbrepo-ui/components/dialogs/Persist.vue b/dbrepo-ui/components/dialogs/Persist.vue
index a8595e18fd1151270c670c256f2509997c799b63..b8ed2529476f21729aa44d8cc25f28a35965bad4 100644
--- a/dbrepo-ui/components/dialogs/Persist.vue
+++ b/dbrepo-ui/components/dialogs/Persist.vue
@@ -64,19 +64,6 @@
                 required />
             </v-col>
           </v-row>
-          <v-row dense>
-            <v-col>
-              <v-select
-                id="visibility"
-                v-model="visibility"
-                name="visibility"
-                label="Visibility *"
-                :items="['Public']"
-                disabled
-                :rules="[v => !!v || $t('Required')]"
-                required />
-            </v-col>
-          </v-row>
           <v-row v-for="(creator,i) in identifier.creators" :key="`c-${i}`" dense>
             <v-col cols="3">
               <v-text-field
@@ -109,7 +96,7 @@
                 label="ORCID" />
             </v-col>
             <v-col cols="1" class="mt-5">
-              <v-btn icon x-small @click="deleteCreator(i)">
+              <v-btn v-if="!i" icon x-small @click="deleteCreator(i)">
                 <v-icon>mdi-close</v-icon>
               </v-btn>
             </v-col>
@@ -225,10 +212,15 @@ export default {
         publication_month: formatMonthUTC(Date.now()),
         publication_day: formatDayUTC(Date.now()),
         license: null,
-        visibility: 'everyone',
         type: this.type,
-        doi: null,
-        creators: [],
+        creators: [
+          {
+            firstname: null,
+            lastname: null,
+            affiliation: null,
+            orcid: null
+          }
+        ],
         related_identifiers: []
       },
       relatedTypes: [
diff --git a/dbrepo-ui/components/identifier/Banner.vue b/dbrepo-ui/components/identifier/Banner.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8756962c9e9b4e3106078e603e1db4209b272fab
--- /dev/null
+++ b/dbrepo-ui/components/identifier/Banner.vue
@@ -0,0 +1,33 @@
+<template>
+  <p v-if="identifier?.doi">DOI: <a :href="doiUrl(identifier.doi)">{{ identifier.doi }}</a></p>
+  <p v-else-if="identifier?.id"><a :href="pidUrl(identifier.id)">{{ pidUrl(identifier.id) }}</a></p>
+</template>
+yarn dev
+<script>
+
+export default {
+  props: {
+    identifier: {
+      type: Object,
+      default () {
+        return null
+      }
+    }
+  },
+  computed: {
+    baseUrl () {
+      return `${location.protocol}//${location.host}`
+    }
+  },
+  methods: {
+    pidUrl (pid) {
+      return `${this.baseUrl}/pid/${pid}`
+    },
+    doiUrl (doi) {
+      return `${this.$config.doiUrl}/${doi}`
+    }
+  }
+}
+</script>
+
+<style scoped></style>
diff --git a/dbrepo-ui/nuxt.config.js b/dbrepo-ui/nuxt.config.js
index 66d11e53c6c5a59abc1f2f729acb9895844603d6..e87dd45befc8457dccec4029180428f0547abdba 100644
--- a/dbrepo-ui/nuxt.config.js
+++ b/dbrepo-ui/nuxt.config.js
@@ -81,7 +81,8 @@ export default {
     logo: process.env.LOGO || '/logo.png',
     mailVerify: process.env.MAIL_VERIFY || false,
     tokenMax: process.env.TOKEN_MAX || 5,
-    elasticPassword: process.env.ELASTIC_PASSWORD || 'elastic'
+    elasticPassword: process.env.ELASTIC_PASSWORD || 'elastic',
+    doiUrl: process.env.DOI_URL || 'https://doi.org'
   },
 
   proxy: {
diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue
index b9224362e62047fb60aa8b81f72f49b67b949b53..99edcf8c78809f354db5227eca7a382e44ab6fa9 100644
--- a/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue
+++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/info.vue
@@ -15,7 +15,7 @@
                   </v-list-item-title>
                   <v-list-item-content v-if="publisher">
                     <v-skeleton-loader v-if="loading" type="text" class="skeleton-small" />
-                    <a v-if="!loading" :href="pid">{{ pid }}</a>
+                    <Banner v-if="!loading" :identifier="database.identifier" />
                   </v-list-item-content>
                   <v-list-item-title v-if="publisher" class="mt-2">
                     Database Publisher
@@ -222,6 +222,7 @@ import DBToolbar from '@/components/DBToolbar'
 import Persist from '@/components/dialogs/Persist'
 import OrcidIcon from '@/components/icons/OrcidIcon'
 import Citation from '@/components/identifier/Citation'
+import Banner from '@/components/identifier/Banner'
 import { formatTimestampUTCLabel, formatUser, isDataSteward, isResearcher } from '@/utils'
 
 export default {
@@ -229,7 +230,8 @@ export default {
     DBToolbar,
     Persist,
     OrcidIcon,
-    Citation
+    Citation,
+    Banner
   },
   data () {
     return {
@@ -300,9 +302,6 @@ export default {
     isDataSteward () {
       return isDataSteward(this.user)
     },
-    pid () {
-      return `${this.baseUrl}/pid/${this.database.identifier.id}`
-    },
     createdUTC () {
       return formatTimestampUTCLabel(this.database.created)
     },
diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue
index 4ca589d1527db7c2bd3eea1a845348c3f3d2d9c9..71d72baab90497b3f667e041d59972aa6aecef22 100644
--- a/dbrepo-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue
+++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/query/_query_id/index.vue
@@ -84,7 +84,7 @@
                 Persistent Identifier
               </v-list-item-title>
               <v-list-item-content>
-                <a :href="`${baseUrl}/pid/${query.identifier.id}`">{{ baseUrl }}/pid/{{ query.identifier.id }}</a>
+                <Banner :identifier="query.identifier" />
               </v-list-item-content>
               <v-list-item-title class="mt-2">
                 Title
@@ -220,13 +220,15 @@
 <script>
 import Persist from '@/components/dialogs/Persist'
 import Citation from '@/components/identifier/Citation'
+import Banner from '@/components/identifier/Banner'
 import { formatTimestampUTCLabel, formatDateUTC } from '@/utils'
 
 export default {
   name: 'QueryShow',
   components: {
     Persist,
-    Citation
+    Citation,
+    Banner
   },
   data () {
     return {
diff --git a/docker-compose.dbrepo1.yml b/docker-compose.dbrepo1.yml
index 5d89457c84f64126c67ed4a4b1180e0274af13e1..b85af3cd4fdd74be65d4e20685fcba9d642ef986 100644
--- a/docker-compose.dbrepo1.yml
+++ b/docker-compose.dbrepo1.yml
@@ -187,6 +187,8 @@ services:
       core:
     env_file:
       - .env
+    environment:
+      - SPRING_PROFILES_ACTIVE=doi
     depends_on:
       dbrepo-query-service:
         condition: service_healthy
diff --git a/docker-compose.dbrepo2.yml b/docker-compose.dbrepo2.yml
index b403413a70034ba328ccc2fa60b061192e1aadf6..d02ee089f0211b09c11f5d446a60fc4690683489 100644
--- a/docker-compose.dbrepo2.yml
+++ b/docker-compose.dbrepo2.yml
@@ -187,6 +187,8 @@ services:
       core:
     env_file:
       - .env
+    environment:
+      - SPRING_PROFILES_ACTIVE=doi
     depends_on:
       dbrepo-query-service:
         condition: service_healthy