diff --git a/services/acl/Acl.Api/Program.cs b/services/acl/Acl.Api/Program.cs
index 7812234..21509c8 100644
--- a/services/acl/Acl.Api/Program.cs
+++ b/services/acl/Acl.Api/Program.cs
@@ -85,6 +85,12 @@ app.MapPost("/documenten", async (StoreDocumentRequest body, AclService acl, Can
return Results.Ok(new { informatieobjectUrl = url.ToString() });
});
+// List the published zaaktypen — the read-only catalogus the beheer portal shows (S-15a). The BFF
+// proxies this behind medewerker-realm + beheerder authorization; the ACL trusts its callers (§8.3)
+// and is the only code allowed to read the ZGW Catalogi API (§8.1).
+app.MapGet("/catalogi/zaaktypen", async (AclService acl, CancellationToken ct) =>
+ Results.Ok(await acl.ListZaaktypenAsync(ct)));
+
app.Run();
public sealed record OpenZaakRequest(string Bsn, string Reference);
diff --git a/services/acl/Acl.Application/AclService.cs b/services/acl/Acl.Application/AclService.cs
index 34c0ced..52bb9d7 100644
--- a/services/acl/Acl.Application/AclService.cs
+++ b/services/acl/Acl.Application/AclService.cs
@@ -45,8 +45,7 @@ public sealed class AclService(IZaakGateway gateway, AclDefaults defaults, IZaak
/// The published zaaktypen, for the beheer catalogus viewer (S-15a). Read-only passthrough:
/// no default-fill, the ACL is simply the only code allowed to read ZGW (§8.1).
public Task> ListZaaktypenAsync(CancellationToken ct = default) =>
- // ponytail: stub for the red test; real passthrough lands in the green commit.
- Task.FromResult>([]);
+ gateway.ListZaaktypenAsync(ct);
/// The zaak's reference (its ZGW identificatie), for the read projection (#78).
public Task GetZaakReferenceAsync(Uri zaakUrl, CancellationToken ct = default)
diff --git a/services/acl/Acl.Infrastructure/OpenZaakGateway.cs b/services/acl/Acl.Infrastructure/OpenZaakGateway.cs
index 1978a4f..73a49ff 100644
--- a/services/acl/Acl.Infrastructure/OpenZaakGateway.cs
+++ b/services/acl/Acl.Infrastructure/OpenZaakGateway.cs
@@ -170,9 +170,15 @@ public sealed class OpenZaakGateway(HttpClient http, OpenZaakOptions options) :
return new Uri(match.Url);
}
- public Task> ListZaaktypenAsync(CancellationToken ct = default) =>
- // ponytail: stub for the red test; real Catalogi read lands in the green commit.
- throw new NotImplementedException();
+ public async Task> ListZaaktypenAsync(CancellationToken ct = default)
+ {
+ // Only published zaaktypen (status=definitief excludes concepts) — the read-only catalogus the
+ // beheer portal shows. Public-safe fields only.
+ var page = await GetAsync("/catalogi/api/v1/zaaktypen?status=definitief", "zaaktypen", ct);
+ return (page.Results ?? [])
+ .Select(z => new ZaaktypeSummary(z.Identificatie ?? "", z.Omschrijving ?? "", new Uri(z.Url)))
+ .ToList();
+ }
// GETs an absolute-by-path ZGW resource with auth (no CRS — catalogi is not a geo API).
private async Task GetAsync(string pathAndQuery, string label, CancellationToken ct)
@@ -350,7 +356,8 @@ public sealed class OpenZaakGateway(HttpClient http, OpenZaakOptions options) :
private sealed record ZaaktypeDto(
[property: JsonPropertyName("url")] string Url,
- [property: JsonPropertyName("identificatie")] string? Identificatie);
+ [property: JsonPropertyName("identificatie")] string? Identificatie,
+ [property: JsonPropertyName("omschrijving")] string? Omschrijving = null);
private sealed record InformatieobjecttypePage(
[property: JsonPropertyName("results")] IReadOnlyList? Results);