How Rasket protects your mail and your account
Every team's rows are separated by the database itself, not only by our code. API keys are stored as hashes, every other secret is encrypted under a key of its own, and every event we post to you is signed. Here is how, and here is what we do not claim.
GET /emails Authorization: Bearer rk_live_2f7a…
→ team 8f3c2a41-…
SET LOCAL app.current_team_id = '8f3c2a41-…'
SELECT count(*) FROM emails412
SET LOCAL app.current_team_id = '1c05be77-…'
SELECT count(*) FROM emails0
● Same table, same query, no context to borrow
How your data is separated
A team is the boundary. Every row we hold for a customer carries the team it belongs to, and two independent checks keep it there.
- 1
The request resolves to one team
An API key belongs to exactly one team, and there is no team parameter anywhere in the API to argue with it. A dashboard request takes its team from the address bar, checked against your membership first.
- 2
The transaction names that team
Every transaction opens by declaring the one team it is allowed to touch. The declaration dies with the transaction, so it cannot survive into the next request or leak across a pooled connection.
- 3
The database denies the rest
Each table of customer data carries a policy comparing the row's team with the one the transaction declared. No team declared means no rows at all, so a forgotten check fails closed.
The second check is row-level security, and it exists because the first one is written by people. Our code filters by team on every query, the database compares the same team again on every row, and the two have to agree. It is the fence that catches the bug nobody wrote a test for.
A team slug you are not a member of answers 404 rather than 403, so the dashboard never tells you which teams exist. An API key resolves to one team and reaches nothing outside it, and a key sent to an endpoint its permission does not cover is refused with a named error rather than an empty result. Our own mail, including your verification email, goes through the ordinary sending pipeline from a tenant of our own, so there is no privileged path around any of it.
How secrets are stored
Three kinds of credential exist here, and each is stored differently. The one you paste into your server is the one we cannot read back.
API keys are hashed, and shown once
A key is readable at the moment you create it and never again. We store the SHA-256 hash of the whole token, plus the first few characters so you can tell keys apart in a list. Nothing in our database can be turned back into your key, so losing one means creating another.
Every other secret is encrypted
Webhook signing secrets, DKIM private keys, two-factor secrets and single sign-on client secrets are encrypted with AES-256-GCM. Each row gets its own data key, and each data key is wrapped by a master key that lives outside the database and never reaches a backup.
Keys carry permissions, not blanket access
A key is either full access or sending access, and a sending key can be pinned to a single domain. A sending key can read the template it is about to send; it cannot create another key, change a plan or touch your team.
Every event we post is signed
A webhook delivery carries an id, a timestamp and an HMAC-SHA256 signature over the exact bytes we sent. Verify the signature, refuse a timestamp outside a short window, and a captured request cannot be replayed at you later.
Logs that leave the secrets out
Logging redacts authorization headers, cookies and every field named like a token, a password or a key, along with message bodies. An attachment is recorded as its filename, size and checksum.
Encrypted in transit, encrypted at rest
Connections to Rasket use TLS, and so do the connections from Rasket to its own database and storage. The database is encrypted at rest and backed up on a schedule.
Rotating a master key adds a version and rewraps a few bytes per row. No message and no private key is ever re-encrypted, and a ciphertext is bound to the row it came from, so a row copied elsewhere will not decrypt.
The verification half of a signed webhook is published as a package with no dependencies, so checking a delivery is one call rather than an afternoon:
// app/api/hooks/rasket/route.tsimport { verify } from "@rasket/webhook-verify";
export async function POST(request: Request) { const rawBody = await request.text(); const event = verify( rawBody, request.headers, process.env.RASKET_WEBHOOK_SECRET );
// id, timestamp and signature all checked, and a timestamp more // than five minutes from now refused as a replay. await handle(event); return new Response(null, { status: 200 });}The full scheme, the retry schedule and the eight reasons a verification can fail are on events and webhooks, and the endpoint reference is under webhooks.
Signing in to the dashboard
Three ways into an account, and a team can decide which of them it allows.
Two-factor authentication
Any member can turn on a time-based code from their authenticator app, with ten single-use recovery codes. The secret is encrypted at rest and the codes are stored as hashes. Passwords are hashed with Argon2id, must be at least twelve characters, and are checked against a breached-password list. A reset signs every session out.
Single sign-on
Connect your identity provider over OpenID Connect, prove each email domain with a DNS record, and let people in through it. Turn enforcement on and the team is reachable only from a session your provider signed. An admin can mark a member exempt for the day the provider is down, and an exempt member needs two-factor authentication.
OAuth for other apps
Another app can act on a team's account without anyone pasting a key into it. Rasket runs the authorization code flow with PKCE, every client is public so there is no client secret to lose, and a token holds only the scopes the person approved. No scope reaches API keys, the member list, billing or the list of connected apps.
What we keep, and for how long
Retention is an attribute of your plan rather than something we forgot to decide. Email metadata, rendered HTML and text, raw messages, attachments, delivery events, API request logs and webhook payloads all follow it: thirty days on every self-serve plan, and a window you agree with us on Enterprise. A job runs nightly and deletes them class by class. The rows go; they are not hidden behind a flag.
A few records outlive the rest. Suppressions are kept indefinitely, because they are the record of who asked us not to write to them. Audit and usage records are kept seven years, because they are legal and billing records rather than message data. Every change to a key, a domain, a webhook, your team or your plan is written to that audit log in the same transaction as the change itself, so the two cannot disagree.
You can erase a contact yourself, from the dashboard or the API. The personal data goes and only a hashed consent record stays, because the record of an unsubscribe is the evidence that we honored it. A copy of a team's data, deletion of a team and deletion of your account are available on request today rather than as a button: write to info@rasket.com from the account's address. Deleting a team stops its sending at once and keeps the data for a seven-day grace period you can cancel inside, after which it is erased. The privacy policy lists every class and its window.
Reporting a vulnerability
If you have found a security problem in Rasket, write to info@rasket.com with enough detail for us to reproduce it: the request you sent, the response you got and what you expected instead. Put the word Security at the front of the subject line so it reaches the right person quickly.
Please test only against your own team and your own domains, do not run automated scans against the API, and do not touch data that is not yours. Give us a reasonable chance to fix the problem before you write about it publicly. We will confirm your report arrived, tell you what we found, and tell you when it is fixed.
We do not run a paid bug bounty. We would rather say so than let a page imply one.
Frequently asked questions
Where is my data stored, and is it encrypted?
In a managed database and an object store, both encrypted at rest, reached only over TLS. Secrets such as webhook signing secrets, DKIM private keys, two-factor secrets and single sign-on client secrets are encrypted a second time with AES-256-GCM, each under its own data key.
Can anyone at Rasket read my email content?
Message bodies are never written to our logs, and the logs redact credentials and cookies as well. A small number of operators can reach tenant data through an internal console to investigate an incident or an abuse report, that console needs a second factor on every sign-in, and every action taken there is written to the audit log.
What should I do if an API key leaks?
Revoke it in the dashboard and create a new one. Revocation takes effect on the next request, and the request logs show what the key called and when, so you can see whether it was used.
Do you support single sign-on, and which providers?
Yes, over OpenID Connect. Okta, Microsoft Entra ID and Google Workspace all speak it; SAML does not work. Single sign-on is an add-on on the Scale plan and included on Enterprise, and a team can require it once its email domains are proved by a DNS record.
How do I check that a webhook really came from Rasket?
Recompute the signature over the raw bytes with your endpoint's signing secret, compare it in constant time, and refuse a timestamp more than five minutes from now. Our verification package does all three in one call, and depends on nothing but the standard library.
Are you SOC 2 or ISO 27001 certified?
No, and we would rather say so plainly than imply otherwise. This page describes controls that are in the product today, nothing more. If you need a security review before you buy, write to sales@rasket.com and we will answer your questionnaire.
Check it for yourself
Create an account, verify a domain and read what a key can and cannot do before you trust it with anything.