diff --git a/backend/src/main/java/org/raddatz/familienarchiv/relationship/RelationshipController.java b/backend/src/main/java/org/raddatz/familienarchiv/relationship/RelationshipController.java
new file mode 100644
index 00000000..1a94cc29
--- /dev/null
+++ b/backend/src/main/java/org/raddatz/familienarchiv/relationship/RelationshipController.java
@@ -0,0 +1,92 @@
+package org.raddatz.familienarchiv.relationship;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.raddatz.familienarchiv.model.Person;
+import org.raddatz.familienarchiv.relationship.dto.CreateRelationshipRequest;
+import org.raddatz.familienarchiv.relationship.dto.FamilyMemberPatchDTO;
+import org.raddatz.familienarchiv.relationship.dto.InferredRelationshipDTO;
+import org.raddatz.familienarchiv.relationship.dto.InferredRelationshipWithPersonDTO;
+import org.raddatz.familienarchiv.relationship.dto.NetworkDTO;
+import org.raddatz.familienarchiv.relationship.dto.RelationshipDTO;
+import org.raddatz.familienarchiv.security.Permission;
+import org.raddatz.familienarchiv.security.RequirePermission;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Stammbaum API. Endpoints split across two roots:
+ *
+ * - {@code /api/network} — the family graph
+ * - {@code /api/persons/{id}/...} — per-person relationship operations
+ * (PersonController is intentionally left untouched)
+ *
+ */
+@RestController
+@RequiredArgsConstructor
+public class RelationshipController {
+
+ private final RelationshipService relationshipService;
+
+ @GetMapping("/api/network")
+ public NetworkDTO getNetwork() {
+ return relationshipService.getFamilyNetwork();
+ }
+
+ @GetMapping("/api/persons/{id}/relationships")
+ public List getRelationships(@PathVariable UUID id) {
+ return relationshipService.getRelationships(id);
+ }
+
+ @GetMapping("/api/persons/{id}/inferred-relationships")
+ public List getInferredRelationships(@PathVariable UUID id) {
+ return relationshipService.getInferredRelationships(id);
+ }
+
+ @GetMapping("/api/persons/{aId}/relationship-to/{bId}")
+ public InferredRelationshipDTO getRelationshipBetween(@PathVariable UUID aId, @PathVariable UUID bId) {
+ return relationshipService.getRelationshipBetween(aId, bId)
+ .orElseThrow(() -> new ResponseStatusException(
+ HttpStatus.NOT_FOUND, "No relationship path between " + aId + " and " + bId));
+ }
+
+ @PostMapping("/api/persons/{id}/relationships")
+ @RequirePermission(Permission.WRITE_ALL)
+ public ResponseEntity addRelationship(
+ @PathVariable UUID id,
+ @Valid @RequestBody CreateRelationshipRequest dto) {
+ validateRelationType(dto.relationType());
+ return ResponseEntity.status(HttpStatus.CREATED)
+ .body(relationshipService.addRelationship(id, dto));
+ }
+
+ @DeleteMapping("/api/persons/{id}/relationships/{relId}")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ @RequirePermission(Permission.WRITE_ALL)
+ public void deleteRelationship(@PathVariable UUID id, @PathVariable UUID relId) {
+ relationshipService.deleteRelationship(id, relId);
+ }
+
+ @PatchMapping("/api/persons/{id}/family-member")
+ @RequirePermission(Permission.WRITE_ALL)
+ public Person patchFamilyMember(@PathVariable UUID id, @RequestBody FamilyMemberPatchDTO dto) {
+ return relationshipService.setFamilyMember(id, dto.familyMember());
+ }
+
+ private static void validateRelationType(String typeName) {
+ if (typeName == null || typeName.isBlank()) {
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "relationType is required");
+ }
+ try {
+ RelationType.valueOf(typeName);
+ } catch (IllegalArgumentException e) {
+ throw new ResponseStatusException(
+ HttpStatus.BAD_REQUEST, "Unknown relationType: " + typeName);
+ }
+ }
+}
diff --git a/backend/src/main/java/org/raddatz/familienarchiv/relationship/dto/FamilyMemberPatchDTO.java b/backend/src/main/java/org/raddatz/familienarchiv/relationship/dto/FamilyMemberPatchDTO.java
new file mode 100644
index 00000000..74d0bcb5
--- /dev/null
+++ b/backend/src/main/java/org/raddatz/familienarchiv/relationship/dto/FamilyMemberPatchDTO.java
@@ -0,0 +1,4 @@
+package org.raddatz.familienarchiv.relationship.dto;
+
+/** Body for {@code PATCH /api/persons/{id}/family-member}. */
+public record FamilyMemberPatchDTO(boolean familyMember) {}