SendRaven

Security

What we do with your keys, your mail, and your list.

This page describes controls that exist in the code, not intentions. Where a number appears, it is the one the software enforces. Where we cannot verify a claim, we do not make it; the last section says what those are.

SHA-256
is all we keep of an API key
300s
webhook replay window
6
delivery attempts over twelve hours
30 days
before raw received mail is deleted

API keys

A key is sk_live_ followed by 24 random bytes. The plaintext is shown once, at creation, and never stored: the database holds its SHA-256 and the last four characters for display. A copy of the database is not a set of working credentials. OAuth tokens issued to MCP clients are stored the same way.

Every key carries scopes, and a key that can only send cannot mint one that can do more; creating keys through the API needs a management scope, and a key cannot revoke itself. Guardrails live on the key rather than the workspace, because a workspace quota only trips after an agent has already mailed ten thousand people: a daily send cap, a recipient allowlist of exact addresses or whole domains, and a hold that drafts every message and waits for a person to release it. Held messages expire after 72 hours by default, so a forgotten draft never sends days later out of context.

Revoking a key stops it immediately; the lookup excludes anything revoked. A key can also be deleted outright, from active or revoked, and deletion revokes first so there is no instant where the row is gone but a cached lookup could still honour it. Credential-bearing request headers are redacted at the logger, for every call site.

Credentials are never in a URL

A new API key or webhook signing secret is handed to the page that shows it in a short-lived httpOnly cookie that expires within sixty seconds, not in a query string. A query parameter would put a live credential into browser history, the Referer header of any outbound link, and whatever access log sits in front of the app — for a value that is by design shown once and cannot be recovered.

Webhooks

Every delivery is signed with the endpoint’s secret in an X-CN-Signature header, formatted t=<unix>,v1=<hmac>, where the HMAC is SHA-256 over <t>.<body>. Verification compares in constant time and rejects a timestamp more than 300 seconds from now, so a captured delivery cannot be replayed later. The signing secret is shown once when the endpoint is created.

The first attempt is made inline with a ten-second timeout. A failure is retried five more times on a fixed ladder — one minute, five minutes, thirty minutes, two hours, twelve hours after the event — and then abandoned. Delivery never blocks or fails the send that produced the event. The dashboard refuses a non-https endpoint, because the payload carries recipient addresses and bounce diagnostics; if you create endpoints through the API, use https for the same reason. Webhook docs.

Received mail and attachments

A reply arrives as raw MIME and is written to object storage, in a bucket configured to block public access and encrypt at rest. Attachment bytes are never copied into the database. Database documents cap at 16MB and mail arrives up to 40MB, so inlining a large attachment would fail the insert and lose the whole message; the message record instead describes each attachment — name, type, content id, size — and notes where the bytes are. A fetch of the raw object is capped at 30MB so one oversized message cannot pull its whole size into a shared process.

Raw mail is deleted after 30 days by a lifecycle rule on the bucket. The message record is permanent — sender, subject, body and the attachment list all stay — but a download of an older attachment answers 410 rather than 200. Thirty days covers the window in which someone acts on a reply; keeping every customer’s mail indefinitely is a liability, not a feature. A workspace that needs longer retention needs its own rule or a copy into its own bucket, not a longer default for everyone.

Attachments are served through the application, scoped to the workspace in the database query itself rather than checked afterwards, and never through a presigned link — a presigned link is a bearer token for one object that outlives the session and is scoped to no tenant. Downloads are forced to attachment disposition with nosniff, so an HTML or SVG attachment cannot render inline and run against a signed-in session. The filename comes from the sender, so quotes and line breaks are stripped before it can forge another header.

Each received message carries the SPF, DKIM and spam verdicts computed on receipt, exposed on the API as spf_verdict, dkim_verdict and spam_verdict. A reply that failed SPF or DKIM may be forged, and the thread view flags it, because a forged reply is exactly the input an agent should not treat as instructions. Event notifications from the delivery layer are authenticated by their signature and by the topic they came from before anything is ingested, and every ingestion write is idempotent.

Templates and message bodies

Template values are HTML-escaped on substitution. Values come from agents and from end users; interpolating them raw would let a name like <script> execute in whatever webmail renders it, and would let a prompt-injected value rewrite the message around it. Rendering throws on a missing variable rather than sending a visible {{name}} to a customer — a failed API call can be retried, a half-rendered email cannot be recalled.

Every header value is sanitised in the MIME builder, and a subject containing a line break is refused with 422: a CR or LF in a header would otherwise close the header block and replace the body of a message still signed by your domain. In the dashboard, sent and received HTML is previewed inside an iframe with an empty sandbox attribute — no scripts, no forms, no same-origin — because the body is whatever a caller sent us.

Your sending reputation

Transactional and marketing mail go out on separate sending identities — mail. and news. under your domain — with separate reputation. A campaign’s complaint rate cannot reach your password resets. You cannot send from a domain you have not verified by DNS.

Suppression is per workspace, so no tenant inherits or pollutes another’s list, and it is scoped by kind. A hard bounce or a complaint suppresses the address for all mail; a marketing unsubscribe suppresses marketing only, so it never blocks a receipt. Transient bounces are not suppressed. The check lives in the one send path every route, worker and campaign goes through, so no caller can bypass it, and an unsubscribe also cancels everything queued for that address.

Before a campaign starts, the workspace’s own fourteen-day bounce and complaint rates are read and the send is refused above 4% or 0.08%. Staff can suspend a workspace’s sending outright; the check sits in the dispatch step that every path shares, so it halts queued and in-flight mail as well as new requests. Staff tooling for account health excludes message bodies — diagnosing delivery does not require reading your mail.

Sub-processors

These are the third parties that process data on our behalf to run the service.

  • Amazon Web Services — email delivery and receiving, and object storage for received mail. Region us-west-2. The sending substrate is a sub-processor: mail you send passes through it, and replies arrive through it.
  • MongoDB Atlas — the database: accounts, workspaces, message log, contacts, suppressions, threads. Connections are encrypted in transit; Atlas does not accept unencrypted ones.
  • Railway — hosting for the web app, the API, the workers and the MCP server.
  • Stripe — payment and subscription billing. Card details are entered on Stripe’s checkout and billing portal, not on our pages, and we store only the subscription state they report back.

Data location and retention

The privacy policy covers where personal data is held, how long it is retained, and how to exercise data-subject rights. The retention specific to this product: received raw mail, 30 days, as above; idempotency keys, 24 hours; raw usage events, 7 days, after which only daily aggregates remain; the message log, suppressions and preferences, for as long as the workspace exists. Deleting a contact keeps their suppression and topic preferences on purpose — the record of an opt-out has to outlive the record of the person, or a re-import would mail someone who asked us not to.

Reporting a problem

If you find a vulnerability, write to security@sendraven.ai. Say what you found and how to reproduce it; we will acknowledge it and tell you what we did. Please do not test against other customers’ workspaces or mail.

What we do not claim

No SOC 2 or ISO certification, no third-party penetration test, no uptime figure and no deliverability figure. We will state each of those when we have it, and not before. Encryption at rest is configured for the bucket that holds received mail; we make no broader claim about it here.

Authentication docs · Limits on the key · Acceptable use