diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResource.java b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a5d37676d95578418ebba3385ddb8f5f7287e99
--- /dev/null
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResource.java
@@ -0,0 +1,8 @@
+package de.ozgcloud.alfa.resource;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+@JsonIgnoreProperties(ignoreUnknown=true)
+public class OzgcloudResource {
+
+}
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceController.java
new file mode 100644
index 0000000000000000000000000000000000000000..a0ccfcd416ac66767589bc373197b2238d4e99c5
--- /dev/null
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceController.java
@@ -0,0 +1,47 @@
+package de.ozgcloud.alfa.resource;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.Link;
+import org.springframework.hateoas.RepresentationModel;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import de.ozgcloud.alfa.common.errorhandling.ResourceNotFoundException;
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import jakarta.validation.constraints.NotBlank;
+import lombok.RequiredArgsConstructor;
+
+@RestController
+@RequestMapping(OzgcloudResourceController.PATH)
+@RequiredArgsConstructor
+public class OzgcloudResourceController {
+
+	static final String PATH = "/api/resources";
+	static final String PARAM_URI = "uri";
+
+	private final Collection<OzgcloudResourceURIResolver> resolvers;
+	private final OzgcloudResourceModelAssembler assembler;
+
+	@GetMapping(params = PARAM_URI)
+	public RepresentationModel<EntityModel<OzgcloudResource>> getOzgcloudResource(@RequestParam @NotBlank String uri) {
+		var resourceLink = resolveUri(uri);
+		if (resourceLink.isEmpty()) {
+			throw new ResourceNotFoundException(OzgcloudResource.class, uri);
+		}
+		return assembler.toModel(new OzgcloudResourceURIResolveResult(uri, resourceLink.get()));
+	}
+
+	private Optional<Link> resolveUri(String uri) {
+		return resolvers.stream()
+				.map(resolver -> resolver.resolve(uri))
+				.flatMap(Optional::stream)
+				.reduce((r1, r2) -> {
+					throw new TechnicalException("Multiple resolvers accepted uri " + uri);
+				});
+	}
+}
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceModelAssembler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceModelAssembler.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3e83cf3a97ba6615974c72667fb32b976b6a733
--- /dev/null
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceModelAssembler.java
@@ -0,0 +1,24 @@
+package de.ozgcloud.alfa.resource;
+
+import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
+
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.Link;
+import org.springframework.hateoas.server.RepresentationModelAssembler;
+import org.springframework.stereotype.Component;
+
+import lombok.RequiredArgsConstructor;
+
+@Component
+@RequiredArgsConstructor
+class OzgcloudResourceModelAssembler implements RepresentationModelAssembler<OzgcloudResourceURIResolveResult, EntityModel<OzgcloudResource>> {
+
+	@Override
+	public EntityModel<OzgcloudResource> toModel(OzgcloudResourceURIResolveResult uriResolveResult) {
+		return EntityModel.of(new OzgcloudResource()).add(getSelfLink(uriResolveResult.getResourceURI())).add(uriResolveResult.getResourceLink());
+	}
+
+	private Link getSelfLink(String uri) {
+		return linkTo(methodOn(OzgcloudResourceController.class).getOzgcloudResource(uri)).withSelfRel();
+	}
+}
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceRootProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceRootProcessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..ebe46beb467ae69720dafe31da6121fcd4b4a956
--- /dev/null
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceRootProcessor.java
@@ -0,0 +1,20 @@
+package de.ozgcloud.alfa.resource;
+
+import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
+
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.server.RepresentationModelProcessor;
+import org.springframework.stereotype.Component;
+
+import de.ozgcloud.alfa.Root;
+
+@Component
+class OzgcloudResourceRootProcessor implements RepresentationModelProcessor<EntityModel<Root>> {
+
+	static final String REL_RESOURCE = "resource";
+
+	@Override
+	public EntityModel<Root> process(EntityModel<Root> model) {
+		return model.add(linkTo(methodOn(OzgcloudResourceController.class).getOzgcloudResource(null)).withRel(REL_RESOURCE));
+	}
+}
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolveResult.java b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolveResult.java
new file mode 100644
index 0000000000000000000000000000000000000000..edcbd9ef072905e7a30930396e1afa0d68a90aa3
--- /dev/null
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolveResult.java
@@ -0,0 +1,15 @@
+package de.ozgcloud.alfa.resource;
+
+import org.springframework.hateoas.Link;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.ToString;
+
+@Builder
+@Getter
+@ToString
+public class OzgcloudResourceURIResolveResult {
+	private final String resourceURI;
+	private final Link resourceLink;
+}
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolver.java b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..de38d882ecb8d41c198915eafc20b830798f4fbc
--- /dev/null
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolver.java
@@ -0,0 +1,10 @@
+package de.ozgcloud.alfa.resource;
+
+import java.util.Optional;
+
+import org.springframework.hateoas.Link;
+
+public interface OzgcloudResourceURIResolver {
+
+	Optional<Link> resolve(String uri);
+}
diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangURIResolver.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangURIResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3b6a185dce5c3e3189fabe78e6301c23ccce5a0
--- /dev/null
+++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangURIResolver.java
@@ -0,0 +1,29 @@
+package de.ozgcloud.alfa.vorgang;
+
+import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
+
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import org.springframework.hateoas.Link;
+import org.springframework.stereotype.Component;
+
+import de.ozgcloud.alfa.resource.OzgcloudResourceURIResolver;
+
+@Component
+class VorgangURIResolver implements OzgcloudResourceURIResolver {
+
+	static final String REL_NAME = "vorgang";
+
+	private final Pattern pattern = Pattern.compile("ozgcloud://[^/]+/vorgangs/([0-9a-fA-F]+)");
+
+	@Override
+	public Optional<Link> resolve(String uri) {
+		var matcher = pattern.matcher(uri);
+		if (!matcher.matches()) {
+			return Optional.empty();
+		}
+		var vorgangId = matcher.group(1);
+		return Optional.of(linkTo(VorgangController.class).slash(vorgangId).withRel(REL_NAME));
+	}
+}
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/RootControllerITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/RootControllerITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..c4bc47bb1aba700a268c32f282fb569ecb3f909e
--- /dev/null
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/RootControllerITCase.java
@@ -0,0 +1,52 @@
+package de.ozgcloud.alfa;
+
+import static org.mockito.Mockito.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import org.hamcrest.core.StringEndsWith;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
+
+import de.ozgcloud.alfa.common.user.CurrentUserService;
+import de.ozgcloud.alfa.common.user.UserProfileTestFactory;
+
+@AutoConfigureMockMvc
+@SpringBootTest
+@WithMockUser
+class RootControllerITCase {
+
+	@MockBean
+	private CurrentUserService currentUserService;
+
+	@Autowired
+	private MockMvc mockMvc;
+
+	@BeforeEach
+	void init() {
+		when(currentUserService.getUser()).thenReturn(UserProfileTestFactory.create());
+	}
+
+	@Nested
+	class TestProcess {
+
+		@Test
+		void shouldAddResourceLink() throws Exception {
+			var response = doRequest();
+
+			response.andExpect(jsonPath("$._links.resource.href").value(StringEndsWith.endsWith("/api/resources?uri={uri}")));
+		}
+	}
+
+	private ResultActions doRequest() throws Exception {
+		return mockMvc.perform(get(RootController.PATH)).andExpect(status().isOk());
+	}
+}
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceControllerITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceControllerITCase.java
new file mode 100644
index 0000000000000000000000000000000000000000..a545d2ee05325037544e883b529b7c39d4d9c991
--- /dev/null
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceControllerITCase.java
@@ -0,0 +1,90 @@
+package de.ozgcloud.alfa.resource;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import org.hamcrest.core.StringEndsWith;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.security.test.context.support.WithMockUser;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.ResultActions;
+
+import de.ozgcloud.alfa.common.AlfaTestUtils;
+import lombok.SneakyThrows;
+
+@AutoConfigureMockMvc
+@SpringBootTest
+@WithMockUser
+class OzgcloudResourceControllerITCase {
+
+	@Autowired
+	private MockMvc mockMvc;
+
+	@Nested
+	class TestGetOzgcloudResource {
+
+		private static final String VORGANG_ID = AlfaTestUtils.createMongoDbObjectId();
+
+		@Test
+		void shouldReturnStatusOk() throws Exception {
+			var response = doRequest("ozgcloud://test.de/vorgangs/" + VORGANG_ID);
+
+			response.andExpect(status().isOk());
+		}
+
+		@Test
+		void shouldHaveVorgangLink() throws Exception {
+			var response = doRequest("ozgcloud://test.de/vorgangs/" + VORGANG_ID);
+
+			response.andExpect(jsonPath("$._links.vorgang.href").value(StringEndsWith.endsWith("/api/vorgangs/" + VORGANG_ID)));
+		}
+
+		@Test
+		void shouldHaveSelfLink() throws Exception {
+			var uri = "ozgcloud://test.de/vorgangs/" + VORGANG_ID;
+			var encodedUri = URLEncoder.encode(uri, StandardCharsets.UTF_8);
+
+			var response = doRequest(uri);
+
+			response.andExpect(jsonPath("$._links.self.href").value(StringEndsWith.endsWith(OzgcloudResourceController.PATH + "?uri=" + encodedUri)));
+		}
+
+		@Test
+		void shouldReturnStatusNotFound() throws Exception {
+			var response = doRequest("dummy://test.de");
+
+			response.andExpect(status().isNotFound());
+		}
+
+		@Test
+		void shouldReturnBadRequestOnNoRequestParam() throws Exception {
+			var response = doRequestWithQueryString("");
+
+			response.andExpect(status().isBadRequest());
+		}
+
+		@Test
+		void shouldReturnBadRequestOnEmptyUri() throws Exception {
+			var response = doRequestWithQueryString("?uri=");
+
+			response.andExpect(status().isBadRequest());
+		}
+
+		@SneakyThrows
+		private ResultActions doRequest(String uri) {
+			return doRequestWithQueryString("?uri=" + uri);
+		}
+
+		@SneakyThrows
+		private ResultActions doRequestWithQueryString(String queryString) {
+			return mockMvc.perform(get(OzgcloudResourceController.PATH + queryString));
+		}
+	}
+}
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceControllerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ea5e3d62933188bc4a839a69b7b3cb4d2c63cb1
--- /dev/null
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceControllerTest.java
@@ -0,0 +1,130 @@
+package de.ozgcloud.alfa.resource;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Optional;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.RepresentationModel;
+
+import de.ozgcloud.alfa.common.errorhandling.ResourceNotFoundException;
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import lombok.SneakyThrows;
+
+class OzgcloudResourceControllerTest {
+
+	@Mock
+	private OzgcloudResourceURIResolver resourceAResolver;
+	@Mock
+	private OzgcloudResourceURIResolver resourceBResolver;
+	@Spy
+	private Collection<OzgcloudResourceURIResolver> resourceResolvers = new ArrayList<>();
+	@Mock
+	private OzgcloudResourceModelAssembler assembler;
+	@InjectMocks
+	private OzgcloudResourceController controller;
+
+	@BeforeEach
+	void init() {
+		resourceResolvers.add(resourceAResolver);
+		resourceResolvers.add(resourceBResolver);
+	}
+
+	@Nested
+	class TestGetOzgcloudResource {
+
+		@Nested
+		class OnUriCouldBeResolved {
+
+			private final ArgumentMatcher<OzgcloudResourceURIResolveResult> HAS_MATCHING_RESOURCE_URI = mapping -> mapping.getResourceURI().equals(
+					OzgcloudResourceURIResolveResultTestFactory.RESOURCE_URI);
+			private final ArgumentMatcher<OzgcloudResourceURIResolveResult> HAS_MATCHING_RESOURCE_LINK = mapping -> mapping.getResourceLink().equals(
+					OzgcloudResourceURIResolveResultTestFactory.RESOURCE_LINK);
+
+			@BeforeEach
+			void init() {
+				when(resourceBResolver.resolve(OzgcloudResourceURIResolveResultTestFactory.RESOURCE_URI)).thenReturn(Optional.of(
+						OzgcloudResourceURIResolveResultTestFactory.RESOURCE_LINK));
+			}
+
+			@Test
+			void shouldCallResolver() {
+				callController();
+
+				verify(resourceBResolver).resolve(OzgcloudResourceURIResolveResultTestFactory.RESOURCE_URI);
+			}
+
+			@Test
+			void shouldCallAssemblerWithUri() {
+				callController();
+
+				verify(assembler).toModel(argThat(HAS_MATCHING_RESOURCE_URI));
+			}
+
+			@Test
+			void shouldCallAssemblerWithResourceLink() {
+				callController();
+
+				verify(assembler).toModel(argThat(HAS_MATCHING_RESOURCE_LINK));
+			}
+
+			@Test
+			void shouldReturnModelFromAssembler() {
+				EntityModel<OzgcloudResource> modelFromAssembler = EntityModel.of(new OzgcloudResource());
+				when(assembler.toModel(argThat(mapping -> HAS_MATCHING_RESOURCE_URI.matches(mapping) && HAS_MATCHING_RESOURCE_LINK.matches(mapping))))
+						.thenReturn(modelFromAssembler);
+
+				var model = callController();
+
+				assertThat(model).isEqualTo(modelFromAssembler);
+			}
+		}
+
+		@Nested
+		class OnUriCouldNotBeResolved {
+
+			@BeforeEach
+			void init() {
+				when(resourceAResolver.resolve(OzgcloudResourceURIResolveResultTestFactory.RESOURCE_URI)).thenReturn(Optional.empty());
+				when(resourceBResolver.resolve(OzgcloudResourceURIResolveResultTestFactory.RESOURCE_URI)).thenReturn(Optional.empty());
+			}
+
+			@Test
+			void shouldThrowResourceNotFoundException() {
+				assertThatThrownBy(TestGetOzgcloudResource.this::callController).isInstanceOf(ResourceNotFoundException.class);
+			}
+		}
+
+		@Nested
+		class OnMultipleResolveResults {
+
+			@BeforeEach
+			void init() {
+				when(resourceAResolver.resolve(OzgcloudResourceURIResolveResultTestFactory.RESOURCE_URI)).thenReturn(Optional.of(
+						OzgcloudResourceURIResolveResultTestFactory.RESOURCE_LINK));
+				when(resourceBResolver.resolve(OzgcloudResourceURIResolveResultTestFactory.RESOURCE_URI)).thenReturn(Optional.of(
+						OzgcloudResourceURIResolveResultTestFactory.RESOURCE_LINK));
+			}
+
+			@Test
+			void shouldThrowTechnicalException() {
+				assertThatThrownBy(TestGetOzgcloudResource.this::callController).isInstanceOf(TechnicalException.class);
+			}
+		}
+
+		@SneakyThrows
+		private RepresentationModel<EntityModel<OzgcloudResource>> callController() {
+			return controller.getOzgcloudResource(OzgcloudResourceURIResolveResultTestFactory.RESOURCE_URI);
+		}
+	}
+}
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceModelAssemblerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed144040a39978e418d2248ca496007c4c4f05ac
--- /dev/null
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceModelAssemblerTest.java
@@ -0,0 +1,43 @@
+package de.ozgcloud.alfa.resource;
+
+import static de.ozgcloud.alfa.resource.OzgcloudResourceController.*;
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.IanaLinkRelations;
+import org.springframework.hateoas.Link;
+
+class OzgcloudResourceModelAssemblerTest {
+
+	@InjectMocks
+	private OzgcloudResourceModelAssembler assembler;
+
+	@Nested
+	class TestToModel {
+
+		private final OzgcloudResourceURIResolveResult uriResolveResult = OzgcloudResourceURIResolveResultTestFactory.create();
+
+		@Test
+		void shouldHaveSelfLink() {
+			var model = callAssembler();
+
+			assertThat(model.getLink(IanaLinkRelations.SELF_VALUE)).isPresent().get().extracting(Link::getHref)
+					.isEqualTo(OzgcloudResourceController.PATH + "?" + PARAM_URI + "=" + OzgcloudResourceURIResolveResultTestFactory.ENCODED_RESOURCE_URI);
+		}
+
+		@Test
+		void shouldHaveResourceLink() {
+			var model = callAssembler();
+
+			assertThat(model.getLink(OzgcloudResourceURIResolveResultTestFactory.RESOURCE_LINK.getRel())).isPresent().get().isEqualTo(
+					OzgcloudResourceURIResolveResultTestFactory.RESOURCE_LINK);
+		}
+
+		private EntityModel<OzgcloudResource> callAssembler() {
+			return assembler.toModel(uriResolveResult);
+		}
+	}
+}
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceRootProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceRootProcessorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..71d60de1e746dd5f801b5d0a4f87b0d93493bcc7
--- /dev/null
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceRootProcessorTest.java
@@ -0,0 +1,39 @@
+package de.ozgcloud.alfa.resource;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.springframework.hateoas.EntityModel;
+import org.springframework.hateoas.Link;
+
+import de.ozgcloud.alfa.Root;
+import de.ozgcloud.alfa.RootTestFactory;
+
+class OzgcloudResourceRootProcessorTest {
+
+	private final OzgcloudResourceRootProcessor processor = new OzgcloudResourceRootProcessor();
+
+	@Nested
+	class TestProcess {
+
+		private final EntityModel<Root> model = EntityModel.of(RootTestFactory.create());
+
+		@Test
+		void shouldReturnOriginalModel() {
+			var result = processor.process(model);
+
+			assertThat(result).isEqualTo(model);
+		}
+
+		@Test
+		void shouldAddResourceLink() {
+			processor.process(model);
+
+			assertThat(model.getLink(OzgcloudResourceRootProcessor.REL_RESOURCE)).isPresent().get().extracting(Link::getHref)
+					.isEqualTo(OzgcloudResourceController.PATH + "?uri={uri}");
+		}
+	}
+}
+
+
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolveResultTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolveResultTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd0ea565f584133ece1265c2c2a9782135a65a74
--- /dev/null
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/resource/OzgcloudResourceURIResolveResultTestFactory.java
@@ -0,0 +1,23 @@
+package de.ozgcloud.alfa.resource;
+
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+
+import org.springframework.hateoas.Link;
+
+import com.thedeanda.lorem.LoremIpsum;
+
+class OzgcloudResourceURIResolveResultTestFactory {
+
+	public static final String RESOURCE_URI = String.format("%s://%s.%s/%s", (Object[]) LoremIpsum.getInstance().getWords(4).split("\\s"));
+	public static final String ENCODED_RESOURCE_URI = URLEncoder.encode(RESOURCE_URI, StandardCharsets.UTF_8);
+	public static final Link RESOURCE_LINK = Link.of(LoremIpsum.getInstance().getUrl()).withRel(LoremIpsum.getInstance().getWords(1));
+
+	public static OzgcloudResourceURIResolveResult create() {
+		return createBuilder().build();
+	}
+
+	public static OzgcloudResourceURIResolveResult.OzgcloudResourceURIResolveResultBuilder createBuilder() {
+		return OzgcloudResourceURIResolveResult.builder().resourceURI(RESOURCE_URI).resourceLink(RESOURCE_LINK);
+	}
+}
diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangURIResolverTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangURIResolverTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d691c621cc7e42d207861ccb6503bd04026a74c
--- /dev/null
+++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangURIResolverTest.java
@@ -0,0 +1,61 @@
+package de.ozgcloud.alfa.vorgang;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.springframework.hateoas.Link;
+import org.springframework.hateoas.LinkRelation;
+
+import de.ozgcloud.alfa.common.AlfaTestUtils;
+
+class VorgangURIResolverTest {
+
+	private VorgangURIResolver resolver = new VorgangURIResolver();
+
+	@Nested
+	class TestResolve {
+
+		private final String VORGANG_ID = AlfaTestUtils.createMongoDbObjectId();
+		private final String VORGANG_URI = "ozgcloud://dummy.de/vorgangs/" + VORGANG_ID;
+
+		@ParameterizedTest
+		@ValueSource(strings = {
+				"http://dummy.de/vorgangs/123",
+				"ozgcloud://dummy.de/res-a/123",
+				"ozgcloud://dummy.de/xyz/vorgangs/123",
+				"ozgcloud://dummy.de/vorgangs/xyz/123" })
+		void shouldReturnEmptyIfUriIsNotRecognized(String uri) {
+			var result = resolver.resolve(uri);
+
+			assertThat(result).isEmpty();
+		}
+
+		@Test
+		void shouldReturnLink() {
+			var result = resolver.resolve(VORGANG_URI);
+
+			assertThat(result).isPresent();
+		}
+
+		@Nested
+		class TestVorgangLink {
+
+			@Test
+			void shouldHaveCorrectRelValue() {
+				var result = resolver.resolve(VORGANG_URI);
+
+				assertThat(result).get().extracting(Link::getRel).extracting(LinkRelation::value).isEqualTo(VorgangURIResolver.REL_NAME);
+			}
+
+			@Test
+			void shouldHaveHrefOfVorgang() {
+				var result = resolver.resolve(VORGANG_URI);
+
+				assertThat(result).get().extracting(Link::getHref).isEqualTo("/api/vorgangs/" + VORGANG_ID);
+			}
+		}
+	}
+}