Developer cookbook

Compliance archive cookbook - recordings, transcripts, CDR.

Three tenant-scoped archive surfaces exposed through signal.ashx: call recordings (WAV + JSON sidecar), AI transcripts (JSON with _transcriptTurns), and CDR (Call Detail Records, CSV or JSON). Every endpoint is bearer-authenticated (ak_ API key or admin OIDC), tenant-scoped by request Host, and honestly framed under NIS2 and DORA archive-integrity duties. European Digital Identity Wallet-enabled tenants use the same auth surface.

Task A - list and fetch call recordings

Endpoints verified in live code: GET /signal.ashx?recordings-list=1 returns {ts, tenant, rootDisplay, rootExists, countAll, countReturned, totalBytes, items[]}, then GET /signal.ashx?recording=<callId>&download=1 for the WAV byte stream. Auth: Authorization: Bearer ak_... or admin OIDC bearer.

LanguageLibraryRecipeSnippet
cURLplain shellOpenfetch-recordings-curl.sh
Node.jsaxios / wsOpenfetch-recordings-nodejs.js
Pythonrequests / websocketsOpenfetch-recordings-python.py
PHPcurlOpenfetch-recordings-php.php
.NETHttpClientOpenfetch-recordings-dotnet.cs

Recording fetch - cURL Recipe

Download raw: fetch-recordings-curl.sh

Recording fetch - Node.js Recipe

Download raw: fetch-recordings-nodejs.js

Recording fetch - Python Recipe

Download raw: fetch-recordings-python.py

Recording fetch - PHP Recipe

Download raw: fetch-recordings-php.php

Recording fetch - .NET Recipe

Download raw: fetch-recordings-dotnet.cs

Task B - list and fetch AI transcripts

Endpoints verified in live code: GET /signal.ashx?list-transcripts=1 returns {count, errors, dirs[], items[]}, then GET /signal.ashx?get-transcript=<basename>.json for the full JSON file including the _transcriptTurns array (written by GeminiTranscriberTap). Auth model: Bearer required (admin OIDC or ak_ API key). The HMAC header is not accepted at this endpoint today.

LanguageLibraryRecipeSnippet
cURLplain shellOpenfetch-transcripts-curl.sh
Node.jsaxios / wsOpenfetch-transcripts-nodejs.js
Pythonrequests / websocketsOpenfetch-transcripts-python.py
PHPcurlOpenfetch-transcripts-php.php
.NETHttpClientOpenfetch-transcripts-dotnet.cs

Transcript fetch - cURL Recipe

Download raw: fetch-transcripts-curl.sh

Transcript fetch - Node.js Recipe

Download raw: fetch-transcripts-nodejs.js

Transcript fetch - Python Recipe

Download raw: fetch-transcripts-python.py

Transcript fetch - PHP Recipe

Download raw: fetch-transcripts-php.php

Transcript fetch - .NET Recipe

Download raw: fetch-transcripts-dotnet.cs

Task C - export CDR (Call Detail Records)

Endpoint verified in live code: GET /signal.ashx?cdrlog=1 with optional filters direction=in|out|transit, trunk=, date=YYYY-MM-DD, limit=N, q=. Response: JSON {events[], count, trunks[], dates[]}. Add format=csv for the same rows as CSV with columns recordedUtc, startUtc, endUtc, direction, trunk, callingNumber, calledNumber, durationSec, answered, outcome, releaseReason. Ideal for BI ingest.

LanguageLibraryRecipeSnippet
cURLplain shellOpenexport-cdr-curl.sh
Node.jsaxios / wsOpenexport-cdr-nodejs.js
Pythonrequests / websocketsOpenexport-cdr-python.py
PHPcurlOpenexport-cdr-php.php
.NETHttpClientOpenexport-cdr-dotnet.cs

CDR export - cURL Recipe

Download raw: export-cdr-curl.sh

CDR export - Node.js Recipe

Download raw: export-cdr-nodejs.js

CDR export - Python Recipe

Download raw: export-cdr-python.py

CDR export - PHP Recipe

Download raw: export-cdr-php.php

CDR export - .NET Recipe

Download raw: export-cdr-dotnet.cs

Auth model

All three endpoints accept an Authorization: Bearer <token> where the token is either an ak_-prefixed API key (see the M2M cookbook) or an admin OIDC access token. The recordings and CDR endpoints additionally accept an HMAC signature header X-CodeB-Admin-Signature for legacy internal jobs. The transcripts endpoint is Bearer-only.

Share-link model

An admin can create an anonymous, read-only, time-limited share for a single recording (create-recording-share) or a single transcript (create-transcript-share). The returned token becomes a public URL under recording-shares.html or transcript-shares.html. The share stream endpoint applies anti-download headers; the WAV audio plays inline. Shares are revocable in one click from the shares admin page.

Retention model

Retention is per-tenant, driven by Recording:RetentionDays in the bridge appsettings. The bridge default is 90 days; individual tenants can raise or lower the window. The sweeper runs on the bridge process, not in signal.ashx.

Legal framing - NIS2 / DORA / CRA

Recordings, transcripts and CDR support obligations across NIS2 (secure log preservation), DORA (financial-services incident evidence) and the EU Cyber Resilience Act (product-side auditability). The archive layer is designed for tenant isolation and integrity, but we are explicit about limits:

RASP posture

Every archive endpoint runs inline anomaly detection + integrity checks with loud [api-key-diag], [REC-*], [TRANSCRIBE-*] and [cdr-*] diagnostics. Per-tenant Host-header binding is enforced on every request; a recording sidecar carrying a different tenant field is skipped and logged. Filename inputs use a strict [A-Za-z0-9._-] allowlist to block path traversal.

FAQ

Structured answers are embedded in the schema block above.