// List + fetch call recordings from a CodeB tenant (.NET 6+). // European Digital Identity Wallet-enabled tenants use the same key surface. // [compliance-archive-cookbook 2026-07-23] // // Endpoints live-verified in signal.ashx: // GET signal.ashx?recordings-list=1 // GET signal.ashx?recording=&download=1 // // dotnet run using System; using System.IO; using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; using System.Threading.Tasks; class FetchRecordings { static async Task Main() { var tenant = Environment.GetEnvironmentVariable("CODEB_TENANT") ?? "https://"; var apiKey = Environment.GetEnvironmentVariable("CODEB_API_KEY"); var outDir = Environment.GetEnvironmentVariable("OUT_DIR") ?? "./codeb-recordings"; if (string.IsNullOrEmpty(apiKey)) { Console.Error.WriteLine("CODEB_API_KEY not set"); Environment.Exit(2); } Directory.CreateDirectory(outDir); using var http = new HttpClient { BaseAddress = new Uri(tenant), Timeout = TimeSpan.FromMinutes(2) }; http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); var idxJson = await http.GetStringAsync("/signal.ashx?recordings-list=1"); using var doc = JsonDocument.Parse(idxJson); if (!doc.RootElement.TryGetProperty("items", out var items)) return; Console.WriteLine($"[list] {items.GetArrayLength()} sidecars"); foreach (var it in items.EnumerateArray()) { if (!it.TryGetProperty("callId", out var cidEl)) continue; var cid = cidEl.GetString(); if (string.IsNullOrEmpty(cid)) continue; var dst = Path.Combine(outDir, cid + ".wav"); using var stream = await http.GetStreamAsync("/signal.ashx?recording=" + Uri.EscapeDataString(cid) + "&download=1"); using var fs = File.Create(dst); await stream.CopyToAsync(fs); Console.WriteLine($"[fetch] {cid} -> {dst}"); } } }