When you are transcribing depositions or sensitive board meetings, the most important question isn’t ‘which model is best?’ It is 'where does my data sit, and who can see it?
A lot of STT vendors keep source audio around for debugging, or train on customer data unless you negotiate an opt-out. SpeechWeave was built with privacy first in mind.
-
Source audio is deleted when the job hits a terminal state (
completedorfailed). There is a 7-day storage-expiry backstop if something is left behind, but it is a safety net, not a retention product. -
Audio, transcripts, and request prompts are never used to train, fine-tune, or evaluate our models, or those of infrastructure partners. There is no opt-out because there is no opt-in.
-
Customer text in our logs is a workspace setting, not a per-request flag in the SDK. That includes the transcript and the
promptyou send with the job (names, captions, glossary hints). Default keeps both for debugging. Metadata-only wipes both after a successful webhook delivery (or after retries exhaust). -
Zero-persistence (in-memory only, nothing queryable) is still in early access and not generally available yet. Request Early Access
-
Self-serve is not a BAA. The public Terms bar PHI and other workloads that need a signed BAA. That traffic belongs on Enterprise.
This guide gives you the exact architecture to pass a strict security review. We don’t promise ‘HIPAA-compliance in five lines’ because real compliance takes diligence, but we give you the exact primitives to do it right.
Three layers, three clocks
SpeechWeave keeps three kinds of data. They expire on different schedules.
-
Uploaded audio: You send bytes to a short-lived upload URL (or as a multipart body on the OpenAI-compatible route). When the job hits a terminal state, that object is deleted.
-
Job metadata: The live job row (id, status, model, timings, and, in Standard mode, transcript text and
prompt) ages out of the live database after about 30 days. After that, timings, model, and status remain while transcript text andpromptdo not. -
Webhook delivery logs: Successful deliveries are kept 72 hours by default, or 30 days if the workspace opts into extended debugging. Failed or in-retry deliveries are capped at 7 days either way. Replay from the dashboard only works while the stored payload still has transcript text. Webhook bodies never include
prompt. That field lives on the job row until the same wipe as the transcript.
Debug Mode (Project settings, off by default): This is the one exception on audio. Failed jobs only can keep the received file in an isolated staging area for 24 hours so you can download it from Job Logs and see whether the codec was garbage. Successful jobs still wipe at terminal state. Support will not open that file without written consent.
Retention modes (workspace, not a request body)
There is no privacy={ zero_persistence: true } argument on jobs.create. Retention is a workspace setting under Project settings. Every job in that workspace inherits it.
| Mode | What you get | When to use it |
|---|---|---|
| Standard (default) | Full transcript in the job record and in webhook delivery logs until TTL. Request prompt stays on the job row for the same window. Dashboard replay works. |
Building the integration. You will thank yourself the first time a handler 500s. |
| Metadata-only | Transcript text and request prompt are erased after a 2xx webhook delivery (or after retries exhaust on failure). Job id, timestamps, model, status, duration, HTTP status, and error strings remain. Replay of scrubbed payloads is refused. |
Production for anything you do not want sitting in our logs. Requires a working webhook. If you only poll GET /v1/jobs/:id, the transcript is still on the job row until the 30-day job TTL. |
| Zero-persistence (Premium, not GA) | Designed so transcripts never enter queryable storage and the sync response is the only copy. | Legal, medical, or fintech pipelines that need the in-memory guarantee. zero_persistence: true on POST /v1/jobs is rejected with a “not available yet” error. Request early access. |
Flip metadata-only in the dashboard, not in code. Confirm a test job: webhook 200, then the delivery log shows the transcript as erased.
What the SDK actually looks like
The modes above are workspace policy. The create call is the same either way. Retention is not a field you pass here.
Python:
from speechweave import SpeechWeave, wait_for_job
sw = SpeechWeave() # SPEECHWEAVE_API_KEY
job = sw.jobs.create(
file="./deposition.m4a",
model="max", # harder audio, legal vocabulary
service_mode="deferred", # 50% cheaper; result via poll or webhook
language="en",
prompt="Exhibit, voir dire, sidebar, Your Honor",
)
done = wait_for_job(sw, job["id"])
print(done["id"], done["status"], done["transcript"])
If the backend is Node, the same create call looks like this.
Node:
import { SpeechWeave, waitForJob } from "@speechweave/node";
const sw = new SpeechWeave({ api_key: process.env.SPEECHWEAVE_API_KEY! });
const job = await sw.jobs.create({
file: "./deposition.m4a",
model: "max",
service_mode: "deferred",
language: "en",
prompt: "Exhibit, voir dire, sidebar, Your Honor",
});
const done = await waitForJob(sw, job.id);
console.log(done.id, done.status, done.transcript);
There is no job.audit.retention_policy and no purged_at field. Proof for a reviewer is the public data retention doc, the Project settings screenshot, a webhook delivery that shows a scrubbed payload, and the Terms and Privacy Policy dates.
prompt is a hint for the first ~30 seconds, not a sealed glossary. Treat it like the transcript would be, useful on the way in, content in our logs afterward. Metadata-only removes it from the job row with the transcript. Archive removal drops it for every mode. GET /v1/jobs/:id and webhook payloads never return it. max is the accuracy tier, it does not change retention.
Webhooks, and why metadata-only needs them
Completion notifications go to HTTPS endpoints you register once under Dashboard → Webhooks. They are not a field on POST /v1/jobs. SpeechWeave signs the body with SpeechWeave-Signature (t=unix,v1=hex, HMAC-SHA256 over t.raw_body). Use the helper instead of rolling a hex digest of the body alone.
On metadata-only this handler is the handoff. Persist the transcript before you return 2xx, because that is when we wipe our copy. Verify the signature first.
import { verifyWebhook } from "@speechweave/node";
const raw = await req.text(); // App Router: raw string, not req.json() first
const result = verifyWebhook(
process.env.SPEECHWEAVE_WEBHOOK_SECRET!,
raw,
req.headers.get("SpeechWeave-Signature") ?? "",
);
if (!result.ok) {
return new Response("invalid signature", { status: 401 });
}
const event = JSON.parse(raw);
// ping from the dashboard Test button: { type: "ping", id, created_at }
if (event.type === "ping") {
return new Response("ok");
}
// job.completed / job.failed:
// { object: "job", id, status, transcript, duration, model, service_mode, language, error, created_at, completed_at }
On metadata-only, persist event.transcript in your database during this handler. After you return a 2xx status, we wipe our copy of the transcript and drop prompt from the job row. If the handler crashes before you write, retries will still include the transcript until they succeed or exhaust. prompt is never in that payload, so keep your own copy if you need it later.
Once the handler is persisting payloads and the workspace is on metadata-only, the remaining work is the architecture note for review.
What to tell counsel (and what not to)
Safe to put in an architecture note today:
-
Source audio deleted at job terminal state. 30-day storage backstop. Optional 24-hour failed-job debug staging that you turn on.
-
No training, fine-tuning, or evaluation on customer audio, transcripts, or request prompts, including partners.
-
Standard vs metadata-only is a workspace control. Metadata-only leaves job id, timestamps, model, status, duration, HTTP status, and error strings. Transcript text and
promptare content and are removed together. -
TLS in transit. Webhook signatures with timestamp skew checks and rotation (
v1=list, pass[active, previous]secrets). -
DPA, GDPR, and CCPA requests: [email protected].
Do not claim:
- “Zero-persistence is on for this account.” The API currently rejects the flag.
- “HIPAA compliant” or “we sign a BAA on the self-serve tier.” Public Terms forbid PHI on self-serve. Enterprise is the path for a BAA, MSA, or custom DPA.
- Per-request
log_retentionor a cryptographic purge receipt on the job object. Those fields do not exist.
Enterprise is also the path for custom retention paperwork, isolated routing, and files above the self-serve cap. Contact [email protected].
Practical sequence for a regulated SaaS
-
Integrate on Standard with a staging webhook. Replay a delivery from the dashboard. Confirm signature verification.
-
Persist transcripts (and any
promptyou care about) in your own store (encrypted at rest, your retention policy). Treat SpeechWeave as a compute engine, not your system of record. -
Switch the workspace to metadata-only. Run one job, confirm the delivery log is scrubbed after a 200 response.
-
Leave Debug Mode off in production.
-
If the review requires in-memory-only processing, ask for zero-persistence early access rather than inventing a flag in client code.
Related: Data retention, Security, Privacy Policy.