Înregistrare și storage
Înregistrările, transcrierile și export-urile pot fi scrise în bucket-ul tău, nu în al platformei. Pentru un client reglementat asta e de obicei întrebarea care decide, așa că se configurează per organizație, per tip de artefact, și e validată cu o scriere reală înainte să fie crezută.
Unde ajung artefactele
O destinație de storage e un bucket plus credențiale plus un domeniu de aplicare. Există opt drivere:
| Driver | Secretele de care are nevoie | Observații |
|---|---|---|
platform | niciunul | Bucket-ul gestionat al platformei. Implicit, când nu e configurat altceva. |
s3 | access_key_id, secret_access_key | Amazon S3. Are nevoie de region. |
s3_sts | external_id | Assume-role, fără chei statice. Are nevoie de role_arn în settings. |
r2 | access_key_id, secret_access_key | Cloudflare R2. Are nevoie de account_name. |
azure_blob | account_key | Azure Blob Storage. Câmpul de bucket e containerul. |
gcs | service_account_json | Google Cloud Storage. |
supabase | access_key_id, secret_access_key | Gateway-ul S3 al Supabase. |
s3_compatible | access_key_id, secret_access_key | MinIO, Backblaze B2, Wasabi. Cere un endpoint. |
Fiecare driver declară de ce câmpuri are nevoie — region, endpoint, account_name — iar selectorul oferă doar driverele pe care instalarea asta le poate construi efectiv, verificate față de adaptoarele Flysystem prezente. Un driver pe care nu îl poți construi nu e oferit ca opțiune pe care descoperi mai târziu că e stricată.
| Câmp | Ce înseamnă |
|---|---|
bucket | Numele containerului. |
region / endpoint | Obligatorii, în funcție de driver. |
path_prefix | Tot ce scrie CallAgent merge sub el. |
use_path_style | Pentru endpoint-uri compatibile S3 care au nevoie de el. |
applies_to | recordings, transcripts sau exports — o destinație nu trebuie să le ia pe toate trei. |
retention_days | Curăță după atâtea zile. |
is_default | Folosită când nicio destinație mai specifică nu se potrivește. |
Validează înainte să te bazezi pe ea
Acțiunea Test face o scriere, o citire și o ștergere reale în bucket și înregistrează rezultatul. Mai mult: compară octet cu octet ce a citit înapoi cu ce a scris, ceea ce prinde un bucket versionat, cu cache sau doar-scriere. „Credențialele sunt salvate" și „bucket-ul funcționează" sunt afirmații diferite, iar interfața le ține separate.
Destinațiile de storage se configurează din dashboard, la Platform → Storage destinations. Nu există niciun endpoint /api/v1 pentru ele.
Custodia e urmărită per artefact: call_artifacts.storage_destination_id reține în ce destinație a aterizat efectiv un obiect, deci schimbarea celei implicite nu orfanizează tot ce a fost scris înainte. Artefactele existente sunt mutate de un job de relocare, nu re-indicate în tăcere.
Înregistrare
Înregistrarea se setează per asistent, în artifact_plan, și per număr de telefon.
| Câmp | Implicit | Observații |
|---|---|---|
recording_enabled | true | Se poate reda un anunț; un refuz nu poate fi respectat — vezi mai jos. |
recording_format | wav | wav · mp3 · ogg |
stereo_recording | false | |
transcript_enabled | true | |
messages_enabled | true | |
events_enabled | true | |
logs_enabled | true | |
debug_json_enabled | false | |
retention_days | — | 0–3650. |
pii_redaction | oprit | Vezi mai jos. |
post_call_transcription | oprit | { enabled, provider, model, language }. Sonix e singurul adaptor. |
Calea de storage nu e configurabilă
storagePathTemplate există în contract, cu valoarea implicită org/{{call.organizationId}}/calls/{{call.id}}/{{artifact.kind}}.{{artifact.ext}}. Recorderul nu îl citește niciodată — își scrie hardcodat org/{organizationId}/calls/{callId}/{channel}.wav. Folosește path_prefix pe destinația de storage dacă ai nevoie de un prefix propriu.
Două canale
mono amestecă ambele părți într-un singur canal. stereo și dual țin apelantul și asistentul separabili, ceea ce face posibile o formă de undă pe două piste, analiza pe vorbitor și o redactare curată — nu poți scoate curat numărul de card al apelantului dintr-o înregistrare amestecată.
Asta e dusă până la carrier, nu simulată: integrarea Twilio setează RecordingChannels pe dual, iar generatorul de directoare FreeSWITCH emite record_stereo pentru trunk-urile proprii. Twilio, Telnyx și BYO SIP declară toate înregistrarea pe două canale ca implementată; verifică matricea de capabilități a furnizorului înainte să presupui că o face și un al patrulea carrier.
call_legs.recording_channel reține ce canal a ocupat fiecare leg, iar rândul de artefact poartă channels, sample_rate, codec, duration_seconds, size_bytes și un checksum_sha256.
Consimțământ
recording_consent_plan.mode stabilește ce se spune:
| Mod | Comportament |
|---|---|
none | Implicit. Nu se spune nimic. |
announcement | Textul configurat e rostit înainte de salut. |
assistant_spoken | Asistentul îl spune cu vocea lui. |
custom_audio | Fișierul tău audio de la audio_url. |
Se redă un anunț. Consimțământul nu se obține.
Trei lucruri, toate verificate în cod:
Înregistrarea pornește înainte de anunț. Recorderul e construit și pornit, redarea începe, și abia apoi se rostește notificarea. Anunțul e el însuși în înregistrare.
Un refuz nu poate fi respectat. Gateway-ul raportează exact o stare de consimțământ: announced. Nu raportează niciodată granted sau denied, pentru că nimic nu colectează o confirmare — niciun prompt DTMF, niciun matcher de expresii. on_refusal e purtat în configurația apelului, nu e citit și nu se poate declanșa.
regions e documentație. Nimic nu se ramifică pe el. Serviciul care calculează planul spune în propriul comentariu că nu determină dacă o înregistrare e legală.
Dacă obligația ta e să informezi, un anunț o satisface. Dacă e să obții și să respecți consimțământul, rezolvă asta înainte ca apelul să ajungă la asistent, sau nu înregistra. Vezi Dezvăluire AI și consimțământ.
Starea se reține pe apel ca consent_policy, consent_state, consent_recorded_at și consent_region. Citește consent_state: "announced" ca „s-a redat o notificare", nu ca acord.
Redactare PII
artifact_plan.pii_redaction scoate categorii din textul stocat — conținutul mesajelor, transcrierea, rezumatul și payload-urile de evenimente — o singură dată, la finalizarea apelului. E oprită implicit. Pornirea ei aduce implicit card (validat Luhn) și national_id (SSN american, CNP românesc, NINo britanic); mai sunt disponibile email, phone, iban și tipare custom date de tine.
redact_recording nu redactează înregistrarea
Câmpul există. Fișierul audio nu e modificat niciodată. Dacă un apelant citește un număr de card cu voce tare, el e în WAV. Ca să nu ajungă în storage, folosește zero data retention sau nu înregistra.
Cum obții o înregistrare
GET /api/v1/calls/{call}/recording un URL semnat
DELETE /api/v1/calls/{call}/recording o șterge
GET /api/v1/calls/{call}/artifacts tot ce a produs apelul
GET /api/v1/calls/{call}/artifacts/{artifact}/downloadRuta de download e singura excepție de la autentificarea cu bearer din tot API-ul: e autorizată de un URL semnat, deci un link de înregistrare poate fi dat unui browser sau unui player audio fără să scurgă o cheie.
# Întoarce un URL semnat, de scurtă durată, nu audio-ul în sine
curl "$CALLAGENT_API_URL/calls/01JQ…/recording" \
-H "Authorization: Bearer $CALLAGENT_API_KEY"const { data } = await fetch(`${process.env.CALLAGENT_API_URL}/calls/${callId}/recording`, {
headers: { Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}` },
}).then((response) => response.json());
// data.url e semnat și expiră; nu îl stoca, cere-l din nou.Ștergerea unei înregistrări e o ștergere reală, ceea ce cere o solicitare GDPR. Vezi Securitate.
Retenție și zero data retention
Retenția e per organizație și per categorie de artefact. Șase categorii, fiecare cu propriul câmp {categorie}_days: recordings_days, transcripts_days, events_days, logs_days, structured_outputs_days, analytics_days.
null înseamnă păstrează pentru totdeauna. 0 înseamnă curăță la următoarea rulare. Job-ul de noapte e php artisan callagent:calls:apply-retention, programat la 03:15.
zero_data_retention e modul care ține de toată organizația. Golește conținutul mesajelor, argumentele și rezultatele tools-urilor, timpii pe cuvânt, payload-urile evenimentelor, transcrierea, rezumatul, analiza, structured output-ul și subiectele, și șterge artefactele de înregistrare — lăsând rândul de artefact marcat purged, ca dovadă că a existat ceva. Păstrează intenționat id-ul apelului, statusul, durata, cele două numere E.164, costurile, metricile, leg-urile și jurnalul de audit, pentru că facturarea și rezolvarea disputelor au nevoie de ele și niciunul nu e conținut de conversație.
zdr_delay_minutes: 0 îl rulează inline, la finalizare, nu la job-ul de noapte.
Un apel reține când s-a aplicat fiecare politică (redaction_applied_at, zdr_applied_at, content_purged_at), deci un audit poate arăta nu doar că o politică există, ci că a rulat.
Ștergerea unei înregistrări lasă rândul
RecordingService::purge() șterge audio-ul și pune recording_status = purged, păstrând rândul de artefact ca dovadă că a existat o înregistrare și că a fost ștearsă. De obicei asta vrea o cerere de ștergere — dovada ștergerii, nu absența oricărei urme.
Mai departe
- Apeluri și artefacte — tot ce mai lasă în urmă un apel.
- Securitate și tenancy — retenție, audit și ștergere.