--- title: "Security & Compliance" sidebarTitle: "Security" description: "Tenant isolation you can enforce with a key, encryption, SOC 2 Type 2, GDPR and HIPAA posture, and how to actually delete a user's data." icon: "shield-halved" --- This page is written for your security review. It covers the isolation model and how to enforce it, encryption, compliance status (SOC 2 Type 2 {/* CONFIRM: publishable SOC 2 Type 2 wording */}, GDPR, HIPAA/BAA), what we do and don't do with your data, and the exact API calls that implement a right-to-erasure request. Where an answer is a document rather than an API call, the [last section](#request-the-reports) tells you how to get it. ## The isolation guarantee: scoped API keys Here's the question every reviewer asks, so let's answer it first: *"Suppose I'm a malicious developer — or a compromised client, or a prompt-injected agent. `containerTag` is only a request parameter. What stops me from changing it and reading another user's memories?"* If the caller holds your org-wide API key: nothing. That key can touch every container in your organization — that's what an org key is for, and it should never leave your server. Anything closer to the user gets a **scoped key** instead: a key bound to exactly one [container tag](/concepts/permissioning) at creation time. To mint one, call the scoped-key endpoint from your server with your org key: ```typescript TypeScript // POST /v3/auth/scoped-key — server-side only, never from the client const res = await fetch("https://api.supermemory.ai/v3/auth/scoped-key", { method: "POST", headers: { Authorization: `Bearer ${process.env.SUPERMEMORY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ containerTag: "user_4f8a", expiresInDays: 30, }), }); const { key } = await res.json(); // hand `key` to the client session — it can only ever see user_4f8a ``` ```python Python # POST /v3/auth/scoped-key — server-side only, never from the client import os import requests res = requests.post( "https://api.supermemory.ai/v3/auth/scoped-key", headers={"Authorization": f"Bearer {os.environ['SUPERMEMORY_API_KEY']}"}, json={"containerTag": "user_4f8a", "expiresInDays": 30}, ) key = res.json()["key"] # hand key to the client session — it can only ever see user_4f8a ``` ```bash cURL curl -X POST "https://api.supermemory.ai/v3/auth/scoped-key" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTag": "user_4f8a", "expiresInDays": 30 }' ``` The boundary is then enforced at the data layer, not in your application code: - A request naming any **other** container tag is rejected with `403 Forbidden`. - A request that omits the tag is automatically scoped to the key's own container. - The key only reaches memory endpoints (`/v3/documents`, `/v3/search`, `/v4/search`, `/v4/memories`, `/v4/profile`, `/v4/conversations`, `/v3/container-tags`) — no billing, account, or key-management routes. - Revocation is immediate: `DELETE /v3/auth/scoped-key/:keyId`, and the key gets `401` from that moment on. So the answer for your review, in print: the attacker can change the parameter, and the API refuses the request. Isolation doesn't depend on your code being correct — it depends on which key the caller holds, and the check runs server-side on every request, not in a client library that could be patched out. The SDK has no helper for key management yet — mint and revoke via the REST endpoint as above. Full parameters (rate-limit overrides, naming, expiry bounds) are in the [authentication reference](/authentication), and the complete multi-tenant design — including the mistakes to avoid — is in [permissioning](/concepts/permissioning). Human access works the same way: organization members can be restricted to specific container tags in the console, with the same 403 behavior applied to people instead of keys. ## Encryption in transit and at rest Your data is encrypted in transit and at rest. {/* CONFIRM: encryption specifics — TLS version, at-rest cipher (AES-256?), key management details */} On self-hosted deployments, encryption keys are yours: the enterprise and managed on-prem tiers run inside your infrastructure, so key custody follows your own KMS setup. One gotcha worth knowing before it bites: on self-hosted installs, stored data is encrypted against your configured key — lose or rotate that key incorrectly and existing data becomes unreadable. The setup and recovery notes are in [self-hosting troubleshooting](/self-hosting/troubleshooting). ## Compliance status **SOC 2 Type 2.** Supermemory is SOC 2 Type 2 certified. {/* CONFIRM: exact publishable statement — "certified" vs "audited/report available", audit period, auditor */} The report is available under NDA — see [how to request it](#request-the-reports). **GDPR.** Supermemory supports GDPR-compliant deployments: a Data Processing Agreement (DPA) is available {/* CONFIRM: DPA availability + whether self-serve or on request */}, and the right-to-erasure mechanics are a first-class API operation, [documented below](#delete-a-users-data-the-right-to-erasure-path) — not a support ticket. **HIPAA.** A Business Associate Agreement (BAA) is available on the managed cloud only. {/* CONFIRM: BAA availability + cloud-only scope */} The scoping matters, so here's the reasoning: a BAA covers infrastructure *we* operate and audit. On a self-hosted deployment the infrastructure is yours, so there's nothing for us to attest — you inherit your own cloud's compliance posture instead (which is often exactly what a healthcare security team wants; see [deployment tiers](/self-hosting/tiers)). If you're evaluating for a regulated environment, the practical split is: managed cloud when you want our controls and paperwork to cover you, self-hosted when your controls must cover everything. ## Your data is not training data We don't train models on your data. {/* CONFIRM: exact scope of no-training commitment — all customers or paid plans only; contractual wording */} Worth understanding *how* that's true mechanically, because it's a common source of skepticism. Supermemory's ingestion pipeline runs a custom fine-tuned memory model — but fine-tuning happened before your data ever arrived. When you ingest content, the model derives memories from it at inference time; nothing about your content updates model weights. The derived state (memories, graph, [profiles](/concepts/user-profiles)) lives inside your container as data, not inside any model — which is also why deleting a container [actually removes what was learned from it](#delete-a-users-data-the-right-to-erasure-path). ## Choose where your data lives Data residency comes in four shapes, from most managed to most yours: - **Managed cloud** — the default. {/* CONFIRM: cloud hosting region(s) and whether region pinning / EU residency is available */} - **Managed on-prem** — we operate supermemory inside your cloud account; data never leaves your VPC. - **Enterprise self-hosted** — you run everything, including on air-gapped infrastructure. - **Local binary** — for development and small workloads, entirely on your machine. If "data cannot leave our environment" is a hard requirement (it was the number-one blocker for more than one enterprise we've worked with), the answer isn't a cloud configuration flag — it's picking the right tier. The full matrix, including scale ceilings and what the OSS tier lacks, is in [deployment tiers](/self-hosting/tiers). ## Delete a user's data: the right-to-erasure path The container boundary is also the deletion boundary. A GDPR erasure request, an offboarded tenant, and an ended client contract are all the same operation: bulk-delete everything under the container tag. ```typescript TypeScript // DELETE /v3/documents/bulk — removes every document in the container // (no SDK helper yet — call the endpoint directly) await fetch("https://api.supermemory.ai/v3/documents/bulk", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.SUPERMEMORY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ containerTags: ["user_4f8a"] }), }); ``` ```python Python # DELETE /v3/documents/bulk — removes every document in the container import os import requests requests.delete( "https://api.supermemory.ai/v3/documents/bulk", headers={"Authorization": f"Bearer {os.environ['SUPERMEMORY_API_KEY']}"}, json={"containerTags": ["user_4f8a"]}, ) ``` ```bash cURL curl -X DELETE "https://api.supermemory.ai/v3/documents/bulk" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "containerTags": ["user_4f8a"] }' ``` This removes the user's documents and the memories derived from them. {/* CONFIRM: derived memories, graph edges, and profile fully cleared by bulk delete; backup/retention window before data is unrecoverable */} If the user held a scoped key, revoke it too: `DELETE /v3/auth/scoped-key/:keyId` takes effect immediately. Deletion is permanent and there's no recovery — gate this behind your own confirmation flow. Note that deleting documents does **not** restore used quota: you're billed at ingestion, not for storage held. See [usage & billing](/trust/usage-and-billing). ### Know the difference: delete vs forget Supermemory has two removal mechanisms, and for compliance work you must use the right one: - **Delete (v3, document-level) is the erasure path.** `DELETE /v3/documents/:id` (`client.documents.delete(id)` in the SDK) removes one document; `DELETE /v3/documents/bulk` removes everything under a container tag. This is hard removal — use it for right-to-erasure requests. - **Forget (v4, memory-level) is a product feature, not erasure.** `DELETE /v4/memories` and `POST /v4/memories/forget-matching` soft-delete individual derived facts: the memory stops appearing in search results but is preserved in the database (`isForgotten: true`), so the system can reason about what it used to believe. That's the right tool for "the user corrected an outdated fact" — and the wrong tool for "the user invoked their legal right to be forgotten." For a single stale fact rather than a whole user, forget is what you want: ```typescript TypeScript // DELETE /v4/memories — soft-delete one memory by id or exact content await fetch("https://api.supermemory.ai/v4/memories", { method: "DELETE", headers: { Authorization: `Bearer ${process.env.SUPERMEMORY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ id: "mem_abc123", containerTag: "user_4f8a", reason: "outdated information", }), }); ``` ```python Python # DELETE /v4/memories — soft-delete one memory by id or exact content import os import requests requests.delete( "https://api.supermemory.ai/v4/memories", headers={"Authorization": f"Bearer {os.environ['SUPERMEMORY_API_KEY']}"}, json={ "id": "mem_abc123", "containerTag": "user_4f8a", "reason": "outdated information", }, ) ``` ```bash cURL curl -X DELETE "https://api.supermemory.ai/v4/memories" \ -H "Authorization: Bearer $SUPERMEMORY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "id": "mem_abc123", "containerTag": "user_4f8a", "reason": "outdated information" }' ``` And `forget-matching` handles "forget everything about X" agentically, with a `dryRun` mode to preview the blast radius before committing. Both are covered in depth in [memory operations](/memory-operations). ## Request the reports For anything that's a document rather than an API call — the SOC 2 report, the DPA, a BAA, or answers to a security questionnaire — reach the team through your account contact, or at the address on [console.supermemory.ai](https://console.supermemory.ai). {/* CONFIRM: security/report-request contact address (security@supermemory.ai?) */} The SOC 2 report ships under NDA; the DPA and BAA come back countersigned. --- That's it — every answer on this page is either an API call you can test or a document you can request. ## Where next The complete isolation model — container tags, metadata, scoped keys, and the anti-patterns. Cloud, managed on-prem, enterprise self-hosted, and local — pick by residency requirement. Scoped-key parameters, rate-limit overrides, and revocation. What you're charged for, and why deletion doesn't restore quota.