From 0ed33907cc0128c5ab03bd29584f6dab5e415813 Mon Sep 17 00:00:00 2001 From: Dhruva Reddy Date: Thu, 15 Oct 2026 17:03:47 -0700 Subject: [PATCH 1/3] docs: explain artifact retrieval with zero data retention --- fern/assistants/call-recording.mdx | 14 ++- .../zero-data-retention.mdx | 97 ++++++++++++++++++- 2 files changed, 101 insertions(+), 10 deletions(-) diff --git a/fern/assistants/call-recording.mdx b/fern/assistants/call-recording.mdx index b455f4d96..ca6a054a2 100644 --- a/fern/assistants/call-recording.mdx +++ b/fern/assistants/call-recording.mdx @@ -207,6 +207,10 @@ Configure different types of artifacts with these options: ## Storage Configuration + +With Zero Data Retention (ZDR), Vapi does not retain call content or artifact URLs in the call record. Configure your own storage before the call to keep a copy. See [Find recordings, transcripts, logs, and metadata with ZDR](/security-and-privacy/zero-data-retention#find-recordings-transcripts-logs-and-metadata) for lookup steps and webhook timing. + + ### Default Storage By default, Vapi stores artifacts securely in the cloud: @@ -230,8 +234,8 @@ Configure custom storage for different artifact types: "recordingUseCustomStorageEnabled": true, "loggingUseCustomStorageEnabled": true, "pcapUseCustomStorageEnabled": true, - "recordingPath": "https://your-bucket.s3.amazonaws.com/recordings/", - "loggingPath": "https://your-bucket.s3.amazonaws.com/logs/", + "recordingPath": "/recordings", + "loggingPath": "/logs", "pcapS3PathPrefix": "/pcaps", "transcriptPlan": { "enabled": true, @@ -260,8 +264,8 @@ Configure custom storage for different artifact types: "recordingUseCustomStorageEnabled": true, "loggingUseCustomStorageEnabled": true, "pcapUseCustomStorageEnabled": true, - "recordingPath": "gs://your-bucket/recordings/", - "loggingPath": "gs://your-bucket/logs/", + "recordingPath": "/recordings", + "loggingPath": "/logs", "transcriptPlan": { "enabled": true, "assistantName": "Assistant", @@ -375,7 +379,7 @@ Enable comprehensive logging for debugging and analysis: "artifactPlan": { "loggingEnabled": true, "loggingUseCustomStorageEnabled": true, - "loggingPath": "https://your-bucket.s3.amazonaws.com/logs/" + "loggingPath": "/logs" } } ``` diff --git a/fern/security-and-privacy/zero-data-retention.mdx b/fern/security-and-privacy/zero-data-retention.mdx index c9f78adab..493d874cc 100644 --- a/fern/security-and-privacy/zero-data-retention.mdx +++ b/fern/security-and-privacy/zero-data-retention.mdx @@ -1,6 +1,6 @@ --- title: Zero Data Retention (ZDR) -subtitle: Enable Zero Data Retention so Vapi processes calls without storing recordings, transcripts, or logs +subtitle: Understand what Vapi retains and where to find call artifacts in your own storage description: Zero Data Retention (ZDR) is an organization-level setting that stops Vapi from storing call recordings, transcripts, messages, summaries, and logs after a call ends. slug: security-and-privacy/zero-data-retention --- @@ -9,7 +9,7 @@ Zero Data Retention (ZDR) is an organization-level setting that stops Vapi from ## How it works -With ZDR active, Vapi processes each call in real time but does not save call content to its storage. Vapi does not retain recordings, transcripts, messages, summaries, or structured outputs. Vapi keeps only operational metadata, such as call history, cost, and latency, so those still appear in the Dashboard. +With ZDR active, Vapi processes each call in real time but does not save call content to its storage. Vapi does not retain recordings, transcripts, messages, or summaries. By default, Vapi also removes structured outputs. Vapi retains operational metadata, such as call history, cost, and latency, so those still appear in the Dashboard. ZDR is enabled for an organization from the Dashboard. It is not a setting in the API. @@ -19,11 +19,13 @@ ZDR is enabled for an organization from the Dashboard. It is not a setting in th | Transcripts | No | | Messages | No | | Call summary | No | -| Structured outputs | No | +| Structured outputs | No by default. An explicit storage override can retain selected outputs. | | Detailed call logs | No | | Call history, cost, latency | Yes | | Billing and usage metadata | Yes | +Review each structured output's `compliancePlan.forceStoreOnHipaaEnabled` setting. Setting this override to `true` also allows Vapi to retain that output under ZDR. Leave it unset or `false` when Vapi must not retain extracted outputs. Only enable the override for outputs without sensitive data. This field does not enable HIPAA mode. + ZDR and HIPAA mode are mutually exclusive. HIPAA mode additionally supports a Business Associate Agreement (BAA), requires HIPAA-compliant providers, and lets you retrieve recordings and logs from private storage. To switch, disable one mode before enabling the other. See [HIPAA Compliance](/security-and-privacy/hipaa) to compare. @@ -33,12 +35,97 @@ ZDR and HIPAA mode are mutually exclusive. HIPAA mode additionally supports a Bu Enable **Zero data retention (ZDR)** for your organization from the Dashboard. ZDR does not require a separate purchase. -Keeping your own copy of call data is optional. To retain recording files, transcripts, and logs, configure a [storage bucket](/assistants/call-recording#storage-configuration). To save structured outputs, subscribe to the [`end-of-call-report` webhook](/server-url/events). Set this up before enabling ZDR because once ZDR is active, any call content you do not send to your own systems cannot be recovered. +Keeping your own copy of call data is optional. To retain recording files, transcripts, and logs, configure a [storage bucket](/assistants/call-recording#storage-configuration). To save call content in your application, subscribe to the [`end-of-call-report` webhook](/server-url/events#end-of-call-report). Set this up before enabling ZDR because once ZDR is active, any call content you do not send to your own systems cannot be recovered. +## Find recordings, transcripts, logs, and metadata + +**ZDR** controls what Vapi retains. **Custom storage** controls where you keep your own copy. Enabling ZDR does not create a bucket, enable recording, or configure artifact uploads. + +A blank `recordingUrl` or `stereoRecordingUrl` on a call retrieved from the API does not prove that no recording exists. With ZDR, Vapi removes the retained artifact URLs and call content. Check your own storage and the webhook payloads your application saved. + +| What you need | Where to look with ZDR | +| --- | --- | +| Audio recording | Your configured storage bucket, if recording and custom storage were enabled and the upload succeeded | +| Final transcript and conversation messages | The call JSON uploaded to your bucket, or the `message.artifact` in an `end-of-call-report` your application saved | +| Detailed call logs | Compressed `.jsonl.gz` objects in your configured logging path, if logging and custom storage were enabled and the upload succeeded | +| Full call JSON, including available artifacts and analysis | Your bucket's call report object, described below. Audio and logs have separate files | +| Call history, cost, latency, and billing metadata | Vapi Dashboard and call API. ZDR retains this operational data | + +### Configure your copy before the call + + + + Select the organization that handles the call. Confirm its ZDR setting. Check its storage credential, bucket, and prefix. Credentials and settings in another organization do not establish where this call's artifacts go. Keep API keys and endpoints in the [same region](/security-and-privacy/eu-region) as the organization. + + + Follow the [S3 setup guide](/providers/cloud/s3) or the [storage configuration guide](/assistants/call-recording#storage-configuration). For audio, set `artifactPlan.recordingEnabled` to `true`. Check that any [recording consent plan](/security-and-privacy/recording-consent-plan) allows recording. For logs, enable `artifactPlan.loggingEnabled`. For a final transcript, enable `artifactPlan.transcriptPlan.enabled`. + + Leave `recordingUseCustomStorageEnabled` and `loggingUseCustomStorageEnabled` enabled for the artifacts you want to upload. With ZDR, setting either to `false` prevents uploads of that artifact. Vapi storage is also unavailable for that artifact. + + + Configure a [server URL](/server-url/setting-server-urls). Include `end-of-call-report` and `call.artifact.upload` in the assistant's `serverMessages`. Keep any other events you need. Save the call ID with each payload to correlate events with objects in your bucket. + + + +### Understand the two post-call events + +The **`end-of-call-report`** can carry the transcript, messages, analysis, and recording URLs generated for the call. Read `message.artifact` for this content. Under ZDR, the embedded `message.call.artifact` and later API responses do not retain the same call content. + +When present, use `message.artifact.recording.mono.combinedUrl` for combined mono audio and `message.artifact.recording.stereoUrl` for stereo audio. `message.artifact.recordingUrl` and `message.artifact.stereoRecordingUrl` are older fields. These fields depend on the artifacts produced and uploaded. They are not guaranteed on every report. + +The **`call.artifact.upload`** event reports customer-storage upload outcomes. Its `message.artifacts` array contains a `type` and a boolean `success` for each reported artifact. For example, an entry can have type `end-of-call-report`, `recording-mono`, `recording-stereo`, or `log`. The entries do not contain object keys or download URLs. Under ZDR, do not rely on this event's embedded call record to recover recording URLs. + +This event requires a subscription and at least one artifact targeting your own storage. An omitted artifact is not a successful upload. A `success: false` result needs investigation even if another artifact succeeded. + +Call end, webhook delivery, and storage completion are separate steps. Receiving an `end-of-call-report` is not confirmation that the call JSON has finished uploading. Do not assume the two events arrive in a fixed order. Allow post-call processing to complete, check the upload outcomes, and confirm the objects in your bucket. + +### Locate objects in S3 + +For S3, Vapi joins the storage credential's prefix, the artifact-specific path, and the generated filename. The prefix is `s3PathPrefix` for a `provider: "s3"` credential and `bucketPlan.pathPrefix` for a `provider: "aws"` credential. Other storage providers use their own bucket path settings. + +| Object | Key within the configured bucket | +| --- | --- | +| Audio | `//---mono.` or the corresponding `-stereo.` | +| Call logs | `//--.jsonl.gz` | +| Call JSON | `/vapi/call/.json` | + +`recordingPath` and `loggingPath` come from the assistant's `artifactPlan`. Use bucket-relative paths such as `/recordings` and `/logs`. These fields do not accept full bucket URLs. Empty path components are omitted and repeated slashes are normalized. For audio, the extension is `.wav` by default or `.mp3` when configured. If your prefix uses date templates, search the rendered prefix for the upload date. + +A complete audio or log key requires the call ID, generated timestamp, and UUID. Use a URL captured in your own end-of-call report, or list objects using the call ID as a filename prefix. The call JSON has a deterministic key once you know the credential prefix. It can be written more than once during call finalization, so an early copy may not contain the final artifacts. + +For example, if your S3 credential prefix is `call-data` and your assistant uses `recordingPath: "/recordings"` and `loggingPath: "/logs"`, these read-only commands list matching audio and logs and fetch the call JSON: + +```bash title="Look up a call in your own S3 bucket" +export BUCKET="YOUR_BUCKET_NAME" +export CALL_ID="YOUR_CALL_ID" + +aws s3 ls "s3://${BUCKET}/call-data/recordings/${CALL_ID}-" +aws s3 ls "s3://${BUCKET}/call-data/logs/${CALL_ID}-" +aws s3 cp "s3://${BUCKET}/call-data/vapi/call/${CALL_ID}.json" ./call.json +``` + +Replace `call-data`, `recordings`, and `logs` with your actual configuration. The prefix `call-data/` is an example. Use the prefix configured for your organization. Inspect `artifact.transcript`, `artifact.messages`, and `artifact.recording` in the downloaded JSON when those artifacts are available. The call JSON contains the transcript. The audio key identifies the audio file. + + +An object URL identifies a location. Private buckets require authentication. Use your storage provider's authenticated console, CLI, SDK, or a signed URL generated by your application. For S3, the identity running these examples needs `s3:ListBucket` for the relevant prefixes and `s3:GetObject` for downloads. Encrypted objects may also require permission to decrypt with the bucket's KMS key. The credential Vapi uses to upload needs write access to every configured artifact prefix. Do not make the bucket public to resolve an access error. + + +### Troubleshoot missing artifacts + +| Symptom | What to check | +| --- | --- | +| Empty recording or transcript in the Dashboard or call API | Expected under ZDR. Look in your bucket or saved end-of-call report. | +| Audio missing but call JSON exists | Confirm recording was enabled, consent allowed it, audio was produced, custom storage was enabled, and the audio upload succeeded. The call JSON upload is separate. | +| No `call.artifact.upload` event | Check the server URL, the assistant's event subscription, webhook delivery, and whether any artifact targeted custom storage. The event is not sent when nothing targets your storage. | +| A reported upload fails | Check the storage credential, bucket region, write permissions for the full key, encryption policy, and any configured storage fallback. | +| Objects not found under the expected prefix | Confirm the call's organization, credential prefix, assistant paths, rendered date prefix, and configured audio format. Check each configured storage destination if you use fallback credentials. | +| URL returns access denied | Authenticate to the bucket with read permissions. A URL alone does not authorize access. | +| An older ZDR call has no customer-side copy | Vapi cannot reconstruct call content that was not successfully delivered to your own systems. Configuring storage now applies to future calls. | + ## Verify it works -Place a test call, then open it in the Dashboard. The call appears in your call history with cost and latency, but the transcript, recording, and analysis are empty. +Place a test call, then open it in the Dashboard. The call appears in your call history with cost and latency, but the transcript, recording, and analysis are empty. If you configured your own storage, verify the audio, logs, and final call JSON in that bucket. Check the webhooks saved by your application. ## Related From 64bd1944899590432a9059a2711eda7cd569d996 Mon Sep 17 00:00:00 2001 From: Dhruva Reddy Date: Thu, 15 Oct 2026 18:11:41 -0700 Subject: [PATCH 2/3] docs: simplify ZDR retrieval guidance --- fern/assistants/call-recording.mdx | 2 +- .../zero-data-retention.mdx | 144 ++++-------------- 2 files changed, 32 insertions(+), 114 deletions(-) diff --git a/fern/assistants/call-recording.mdx b/fern/assistants/call-recording.mdx index ca6a054a2..16a53462f 100644 --- a/fern/assistants/call-recording.mdx +++ b/fern/assistants/call-recording.mdx @@ -208,7 +208,7 @@ Configure different types of artifacts with these options: ## Storage Configuration -With Zero Data Retention (ZDR), Vapi does not retain call content or artifact URLs in the call record. Configure your own storage before the call to keep a copy. See [Find recordings, transcripts, logs, and metadata with ZDR](/security-and-privacy/zero-data-retention#find-recordings-transcripts-logs-and-metadata) for lookup steps and webhook timing. +With Zero Data Retention (ZDR), Vapi does not retain call content or artifact URLs in the call record. Configure your own storage before the call to keep a copy. See [Find recordings, transcripts, logs, and metadata with ZDR](/security-and-privacy/zero-data-retention#find-recordings-transcripts-logs-and-metadata) for retrieval steps and troubleshooting. ### Default Storage diff --git a/fern/security-and-privacy/zero-data-retention.mdx b/fern/security-and-privacy/zero-data-retention.mdx index 493d874cc..ecaca2461 100644 --- a/fern/security-and-privacy/zero-data-retention.mdx +++ b/fern/security-and-privacy/zero-data-retention.mdx @@ -1,139 +1,57 @@ --- title: Zero Data Retention (ZDR) -subtitle: Understand what Vapi retains and where to find call artifacts in your own storage -description: Zero Data Retention (ZDR) is an organization-level setting that stops Vapi from storing call recordings, transcripts, messages, summaries, and logs after a call ends. +subtitle: Understand what Vapi retains and where to retrieve your call data +description: Learn what Zero Data Retention keeps in Vapi, how to retrieve recordings, transcripts, and logs from your own storage, and what to check when files are missing. slug: security-and-privacy/zero-data-retention --- -Zero Data Retention (ZDR) is an organization-level setting that stops Vapi from storing call content after a call ends. ZDR applies across your entire organization once active. Use it when your organization must not retain recordings, transcripts, or logs in Vapi. +**Zero Data Retention (ZDR)** stops Vapi from retaining call content after a call ends. Enable it for your organization in the Dashboard. ZDR applies to all calls in that organization and does not require a separate purchase. -## How it works +## What ZDR retains -With ZDR active, Vapi processes each call in real time but does not save call content to its storage. Vapi does not retain recordings, transcripts, messages, or summaries. By default, Vapi also removes structured outputs. Vapi retains operational metadata, such as call history, cost, and latency, so those still appear in the Dashboard. +Vapi processes calls in real time. After a call ends, it retains operational metadata so you can still see call history and usage. -ZDR is enabled for an organization from the Dashboard. It is not a setting in the API. - -| Data | Retained by Vapi with ZDR enabled | +| Data | Retained in Vapi with ZDR | | --- | --- | -| Recordings | No | -| Transcripts | No | -| Messages | No | -| Call summary | No | -| Structured outputs | No by default. An explicit storage override can retain selected outputs. | -| Detailed call logs | No | -| Call history, cost, latency | Yes | -| Billing and usage metadata | Yes | +| Recordings, transcripts, messages, summaries, and detailed call logs | No | +| Structured outputs | No by default | +| Call history, cost, latency, billing, and usage metadata | Yes | -Review each structured output's `compliancePlan.forceStoreOnHipaaEnabled` setting. Setting this override to `true` also allows Vapi to retain that output under ZDR. Leave it unset or `false` when Vapi must not retain extracted outputs. Only enable the override for outputs without sensitive data. This field does not enable HIPAA mode. +A structured output with `compliancePlan.forceStoreOnHipaaEnabled: true` can still be retained under ZDR. Leave this override unset or `false` to prevent storage of that output. -ZDR and HIPAA mode are mutually exclusive. HIPAA mode additionally supports a Business Associate Agreement (BAA), requires HIPAA-compliant providers, and lets you retrieve recordings and logs from private storage. To switch, disable one mode before enabling the other. See [HIPAA Compliance](/security-and-privacy/hipaa) to compare. +ZDR and [HIPAA mode](/security-and-privacy/hipaa) are mutually exclusive. The structured-output override does not enable HIPAA mode. -## Enable ZDR - -Enable **Zero data retention (ZDR)** for your organization from the Dashboard. ZDR does not require a separate purchase. - - -Keeping your own copy of call data is optional. To retain recording files, transcripts, and logs, configure a [storage bucket](/assistants/call-recording#storage-configuration). To save call content in your application, subscribe to the [`end-of-call-report` webhook](/server-url/events#end-of-call-report). Set this up before enabling ZDR because once ZDR is active, any call content you do not send to your own systems cannot be recovered. - - ## Find recordings, transcripts, logs, and metadata -**ZDR** controls what Vapi retains. **Custom storage** controls where you keep your own copy. Enabling ZDR does not create a bucket, enable recording, or configure artifact uploads. - -A blank `recordingUrl` or `stereoRecordingUrl` on a call retrieved from the API does not prove that no recording exists. With ZDR, Vapi removes the retained artifact URLs and call content. Check your own storage and the webhook payloads your application saved. - -| What you need | Where to look with ZDR | -| --- | --- | -| Audio recording | Your configured storage bucket, if recording and custom storage were enabled and the upload succeeded | -| Final transcript and conversation messages | The call JSON uploaded to your bucket, or the `message.artifact` in an `end-of-call-report` your application saved | -| Detailed call logs | Compressed `.jsonl.gz` objects in your configured logging path, if logging and custom storage were enabled and the upload succeeded | -| Full call JSON, including available artifacts and analysis | Your bucket's call report object, described below. Audio and logs have separate files | -| Call history, cost, latency, and billing metadata | Vapi Dashboard and call API. ZDR retains this operational data | - -### Configure your copy before the call - - - - Select the organization that handles the call. Confirm its ZDR setting. Check its storage credential, bucket, and prefix. Credentials and settings in another organization do not establish where this call's artifacts go. Keep API keys and endpoints in the [same region](/security-and-privacy/eu-region) as the organization. - - - Follow the [S3 setup guide](/providers/cloud/s3) or the [storage configuration guide](/assistants/call-recording#storage-configuration). For audio, set `artifactPlan.recordingEnabled` to `true`. Check that any [recording consent plan](/security-and-privacy/recording-consent-plan) allows recording. For logs, enable `artifactPlan.loggingEnabled`. For a final transcript, enable `artifactPlan.transcriptPlan.enabled`. - - Leave `recordingUseCustomStorageEnabled` and `loggingUseCustomStorageEnabled` enabled for the artifacts you want to upload. With ZDR, setting either to `false` prevents uploads of that artifact. Vapi storage is also unavailable for that artifact. - - - Configure a [server URL](/server-url/setting-server-urls). Include `end-of-call-report` and `call.artifact.upload` in the assistant's `serverMessages`. Keep any other events you need. Save the call ID with each payload to correlate events with objects in your bucket. - - - -### Understand the two post-call events +With ZDR, retrieve call content from **your own storage**. Empty recording URLs or transcripts in the Dashboard or call API are expected. They do not establish whether a copy exists in your bucket. -The **`end-of-call-report`** can carry the transcript, messages, analysis, and recording URLs generated for the call. Read `message.artifact` for this content. Under ZDR, the embedded `message.call.artifact` and later API responses do not retain the same call content. +Before the call: -When present, use `message.artifact.recording.mono.combinedUrl` for combined mono audio and `message.artifact.recording.stereoUrl` for stereo audio. `message.artifact.recordingUrl` and `message.artifact.stereoRecordingUrl` are older fields. These fields depend on the artifacts produced and uploaded. They are not guaranteed on every report. +1. Connect a bucket using the [storage setup guide](/assistants/call-recording#storage-configuration) or [S3 guide](/providers/cloud/s3). +2. Enable recording, transcription, and logging for the artifacts you need in the assistant's **Recording & Artifacts** settings. Keep custom storage enabled for those artifacts. Recording also depends on any [consent plan](/security-and-privacy/recording-consent-plan). +3. Confirm the storage settings belong to the organization handling the call. Use the [matching region](/security-and-privacy/eu-region) for its API key and endpoints. -The **`call.artifact.upload`** event reports customer-storage upload outcomes. Its `message.artifacts` array contains a `type` and a boolean `success` for each reported artifact. For example, an entry can have type `end-of-call-report`, `recording-mono`, `recording-stereo`, or `log`. The entries do not contain object keys or download URLs. Under ZDR, do not rely on this event's embedded call record to recover recording URLs. +Enabling ZDR alone does not configure uploads. Without custom storage, Vapi does not retain these files for you. -This event requires a subscription and at least one artifact targeting your own storage. An omitted artifact is not a successful upload. A `success: false` result needs investigation even if another artifact succeeded. - -Call end, webhook delivery, and storage completion are separate steps. Receiving an `end-of-call-report` is not confirmation that the call JSON has finished uploading. Do not assume the two events arrive in a fixed order. Allow post-call processing to complete, check the upload outcomes, and confirm the objects in your bucket. - -### Locate objects in S3 - -For S3, Vapi joins the storage credential's prefix, the artifact-specific path, and the generated filename. The prefix is `s3PathPrefix` for a `provider: "s3"` credential and `bucketPlan.pathPrefix` for a `provider: "aws"` credential. Other storage providers use their own bucket path settings. - -| Object | Key within the configured bucket | +| What you need | Where to look in your bucket | | --- | --- | -| Audio | `//---mono.` or the corresponding `-stereo.` | -| Call logs | `//--.jsonl.gz` | -| Call JSON | `/vapi/call/.json` | - -`recordingPath` and `loggingPath` come from the assistant's `artifactPlan`. Use bucket-relative paths such as `/recordings` and `/logs`. These fields do not accept full bucket URLs. Empty path components are omitted and repeated slashes are normalized. For audio, the extension is `.wav` by default or `.mp3` when configured. If your prefix uses date templates, search the rendered prefix for the upload date. - -A complete audio or log key requires the call ID, generated timestamp, and UUID. Use a URL captured in your own end-of-call report, or list objects using the call ID as a filename prefix. The call JSON has a deterministic key once you know the credential prefix. It can be written more than once during call finalization, so an early copy may not contain the final artifacts. - -For example, if your S3 credential prefix is `call-data` and your assistant uses `recordingPath: "/recordings"` and `loggingPath: "/logs"`, these read-only commands list matching audio and logs and fetch the call JSON: +| Audio recording | The configured recording path, as WAV or MP3 files | +| Transcript, messages, and available analysis | The call JSON at `/vapi/call/.json` | +| Detailed call logs | The configured logging path, as `.jsonl.gz` files | -```bash title="Look up a call in your own S3 bucket" -export BUCKET="YOUR_BUCKET_NAME" -export CALL_ID="YOUR_CALL_ID" +The storage credential's prefix combines with the assistant's `recordingPath` or `loggingPath`. These are bucket-relative paths, such as `/recordings` and `/logs`. Search the configured location using the call ID as a filename prefix. Audio and log filenames also contain generated timestamps and UUIDs, so you cannot derive a complete filename from the call ID alone. -aws s3 ls "s3://${BUCKET}/call-data/recordings/${CALL_ID}-" -aws s3 ls "s3://${BUCKET}/call-data/logs/${CALL_ID}-" -aws s3 cp "s3://${BUCKET}/call-data/vapi/call/${CALL_ID}.json" ./call.json -``` - -Replace `call-data`, `recordings`, and `logs` with your actual configuration. The prefix `call-data/` is an example. Use the prefix configured for your organization. Inspect `artifact.transcript`, `artifact.messages`, and `artifact.recording` in the downloaded JSON when those artifacts are available. The call JSON contains the transcript. The audio key identifies the audio file. + +You can also save the [`end-of-call-report` webhook](/server-url/events#end-of-call-report) in your own application. Its `message.artifact` can contain the transcript and recording URLs available for the call. Save the payload when it arrives because later call API responses do not retain that content under ZDR. + - -An object URL identifies a location. Private buckets require authentication. Use your storage provider's authenticated console, CLI, SDK, or a signed URL generated by your application. For S3, the identity running these examples needs `s3:ListBucket` for the relevant prefixes and `s3:GetObject` for downloads. Encrypted objects may also require permission to decrypt with the bucket's KMS key. The credential Vapi uses to upload needs write access to every configured artifact prefix. Do not make the bucket public to resolve an access error. - +## If you cannot find your data -### Troubleshoot missing artifacts +- **Check setup.** Confirm the relevant artifacts and custom storage were enabled before the call. A call JSON file does not guarantee that audio was recorded. +- **Check the location.** Verify the organization, bucket, credential prefix, and assistant paths. If a prefix uses date templates, use the upload date. +- **Check upload and access.** Allow post-call processing to finish. Confirm upload success and bucket write permissions. Use an authenticated storage console, CLI, or SDK with read access to retrieve private files. +- **Check your saved copies.** Vapi cannot reconstruct call content that was not successfully delivered to your own systems. Storage configured now applies to future calls. -| Symptom | What to check | -| --- | --- | -| Empty recording or transcript in the Dashboard or call API | Expected under ZDR. Look in your bucket or saved end-of-call report. | -| Audio missing but call JSON exists | Confirm recording was enabled, consent allowed it, audio was produced, custom storage was enabled, and the audio upload succeeded. The call JSON upload is separate. | -| No `call.artifact.upload` event | Check the server URL, the assistant's event subscription, webhook delivery, and whether any artifact targeted custom storage. The event is not sent when nothing targets your storage. | -| A reported upload fails | Check the storage credential, bucket region, write permissions for the full key, encryption policy, and any configured storage fallback. | -| Objects not found under the expected prefix | Confirm the call's organization, credential prefix, assistant paths, rendered date prefix, and configured audio format. Check each configured storage destination if you use fallback credentials. | -| URL returns access denied | Authenticate to the bucket with read permissions. A URL alone does not authorize access. | -| An older ZDR call has no customer-side copy | Vapi cannot reconstruct call content that was not successfully delivered to your own systems. Configuring storage now applies to future calls. | - -## Verify it works - -Place a test call, then open it in the Dashboard. The call appears in your call history with cost and latency, but the transcript, recording, and analysis are empty. If you configured your own storage, verify the audio, logs, and final call JSON in that bucket. Check the webhooks saved by your application. - -## Related - - - - Compare ZDR with HIPAA-compliant storage and the BAA option. - - - How call data moves through Vapi and customer-managed storage. - - +After setup, place a test call. Confirm that its files appear in your bucket. Check that the call appears in Vapi's history with cost and latency. From 7c8717b3a36b7fcc90eb9d24a8092146832cc82f Mon Sep 17 00:00:00 2001 From: Dhruva Reddy Date: Thu, 15 Oct 2026 18:35:59 -0700 Subject: [PATCH 3/3] docs: clarify missing artifact URLs with ZDR --- fern/security-and-privacy/zero-data-retention.mdx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fern/security-and-privacy/zero-data-retention.mdx b/fern/security-and-privacy/zero-data-retention.mdx index ecaca2461..b813b2c78 100644 --- a/fern/security-and-privacy/zero-data-retention.mdx +++ b/fern/security-and-privacy/zero-data-retention.mdx @@ -25,7 +25,13 @@ ZDR and [HIPAA mode](/security-and-privacy/hipaa) are mutually exclusive. The st ## Find recordings, transcripts, logs, and metadata -With ZDR, retrieve call content from **your own storage**. Empty recording URLs or transcripts in the Dashboard or call API are expected. They do not establish whether a copy exists in your bucket. +With ZDR, retrieve call content from **your own storage**. + + +Vapi removes recording and other artifact URLs from the retained call record. Later API responses and the Dashboard can show blank URLs even when files were delivered to your configured bucket. A blank URL alone does not establish recording or upload failure. + +Check the storage locations below and any live end-of-call report your application saved. If a file is missing there too, check whether recording was enabled and the upload succeeded. + Before the call: