A short list of well-run places. Boring on purpose.
Sign-in, sessions, and OAuth — credentials never stored by us.
The application and its APIs, deployed atomically.
Testimonials, projects, and settings live here.
R2 object storage for media; Workers serve embeds and walls.
Video testimonial hosting, streaming, and transcripts.
Outbound notifications and testimonial invites.
Subscriptions and billing — card data stays on their pages.
Redis-backed request limits on public endpoints.
On encryption, precisely: traffic is TLS in transit everywhere, with a Strict-Transport-Security policy so browsers refuse to downgrade. At rest, our database and storage providers encrypt customer data as part of their platforms — that's their guarantee, and we name it as theirs rather than dressing it up as our own engineering. The privacy policy carries the authoritative sub-processor commitments.
Deliveries you can verify. Destinations we refuse.
Webhooks are where a SaaS reaches out and touches your infrastructure — so this is where we spent the paranoia. Every event your endpoints receive is signed; every URL you configure is treated as hostile until proven public.
Four lines to verify any delivery
Each endpoint gets its own whsec_ signing secret — 32 random bytes. The header your server receives is X-Signature: t=…,v1=…, and verifying it takes four steps:
Signing secrets are versioned. Rotate one and any in-flight delivery still carrying the old version is canceled before send — nothing ever goes out signed with a secret you've revoked.
A URL is a claim. We check it twice.
A webhook URL could point anywhere — including at infrastructure that was never meant to be reachable. So before any delivery leaves, the destination has to survive this list:
The connection is pinned to the exact IP address that passed validation, so a DNS answer can't change between the check and the connect — the classic rebinding trick, closed. Checks cover IPv4 and IPv6, including IPv4-mapped forms.
Rolling-hour delivery caps per endpoint and per project keep a misconfigured integration from becoming a firehose. Stored delivery logs strip cookies and authorization headers, and bodies are truncated.
Deletion that actually deletes. Retention that actually expires.
When a testimonial is deleted — say, a GDPR erasure request — the person's name, email, and content are scrubbed from webhook delivery logs in the same database transaction as the delete itself. Not on the next cleanup run. The only thing left behind is a no-PII audit record saying a deletion happened.
Video assets enter a durable deletion queue and are removed from the video host once nothing references them — a crash between steps retries rather than leaking.
Testimonial invite emails carry RFC 8058 one-click unsubscribe headers — the kind mail clients render as a native button. An unsubscribe lands on a per-project suppression list that is checked before every send, so a suppressed address is never emailed again by that project.
Old operational records don't wait for someone to remember them — scheduled jobs purge on these windows. For rights requests (access, erasure, portability), the privacy policy is the operative document.
Scoped by default. Denied by default.
Google OAuth or email/password with email verification codes — credentials live with a dedicated auth provider, not in our database. The dashboard, billing, and settings sit behind authentication middleware; public pages are public by design, and nothing else is.
Team access is role-based with granular permission keys, and every role can be overridden per project — down to NO_ACCESS. Invitations expire after 7 days if unaccepted.
Application queries are scoped by project in addition to the record's own id — a leaked or guessed id from another workspace resolves to nothing. This is application-level scoping enforced in our data layer, and we call it that plainly — not database row-level security.
The edge workers that serve embeds and walls call internal APIs with bearer keys, and every scheduled job authenticates with a cron secret. Unauthorized attempts are rejected, logged, and alerted — and the auth checks themselves are covered by an automated test suite, because a gate you don't test is a gate you don't have.
Nothing user-shaped is trusted. Nothing unsanitized gets through.
User-submitted content passes a strict allowlist sanitizer: scripts, event handlers, and unsafe URL schemes are stripped; links are forced to safe protocols and rel attributes.
Customer-supplied widget styling is validated value-by-value — colors, pixel sizes, font weights, font names — before it reaches rendered CSS. Malformed values degrade to defaults instead of injecting.
Collection forms run honeypot fields, per-IP hourly limits, duplicate-content hashing, blocklists, and weighted heuristics — abuse is scored and quarantined before it reaches your inbox.
URLs your customers type — custom-field links, testimonial links — only ever render as clickable when they parse as plain http or https. Anything else displays as inert text.
Where we do hold third-party credentials — G2 integration tokens — they're encrypted with AES-256-GCM before storage, with per-value random IVs.
Error monitoring runs with default PII collection disabled. Unauthorized internal-API and cron attempts raise alerts — throttled, so a spoofed flood can't drown the signal.
Your card never meets our servers. By design.
Checkout and billing management happen on Stripe's hosted, PCI-compliant pages — card numbers are entered there and never pass through ReTestimonial. Incoming billing events are trusted only after their Stripe signature verifies against the raw request body.
Gift-card reward webhooks (Tremendous) are verified with HMAC-SHA256 using constant-time comparison, and every event carries idempotency — a replayed notification can't double-fulfill a reward.
What we don't claim. Yet.
Security pages fail in one direction: overclaiming. So here is the list a diligent buyer would dig for anyway, stated up front.
No certificate, no auditor's letterhead. We won't rent a badge to imply one.
Documented internal security reviews for substantial new features, automated security test suites in CI, and this page — where each claim traces to code.
None commissioned yet.
Engineering controls you can reason about — signed deliveries, pinned sockets, scoped queries — with adversarial internal review and tests around the sharpest edges.
No SAML, no enforced authenticator-app MFA today.
Clerk-managed sign-in with Google OAuth, email/password, and email verification codes. If SSO is what's blocking your team — tell us. Demand moves roadmaps.
Tenant isolation is not enforced by Postgres RLS.
Application-level scoping: every query carries the project id alongside the record id, in the data layer we own and test. We name it what it is.
A checklist you can verify beats a badge you have to take on faith.
Tell us. We'll listen like it matters — because it does.
If you believe you've found a vulnerability, email privacy@retestimonial.com — the same contact published in /.well-known/security.txt. We read every report and acknowledge as fast as we honestly can.
Straight answers, no asterisks.
Certifications, signatures, sub-processors, deletion — the questions security reviews actually ask, answered the way we'd want them answered.
01Are you SOC 2 or ISO 27001 certified?
Not yet — and we won't imply otherwise with a badge wall. What exists instead: documented internal security reviews for substantial new features, automated security test suites in CI, and the specific engineering controls on this page, each of which maps to real code. If your procurement process needs something specific answered, email privacy@retestimonial.com and we'll answer plainly.
02How do I verify webhook signatures?
Every outbound webhook carries an X-Signature header of the form t=<unix-timestamp>,v1=<hex-digest>, where the digest is HMAC-SHA256 over "timestamp.payload" using your endpoint's signing secret. Recompute the HMAC over the raw request body, compare digests with a constant-time function, and reject anything whose timestamp falls outside your tolerance window — the timestamp is part of the signed content precisely so your receiver can refuse replays.
03Where is my data stored, and who are your sub-processors?
The application runs on Vercel; data lives in a Supabase-hosted Postgres database; media lives in Cloudflare R2, and video with Mux; email goes out through Resend; payments run through Stripe; authentication is handled by Clerk. These providers encrypt data at rest as part of their platforms. The privacy policy carries the authoritative, always-current sub-processor list.
04What actually happens when I delete a testimonial?
The record is deleted, and in the same database transaction the person's data is scrubbed from webhook delivery logs rather than waiting for retention to expire — the only thing left behind is a no-PII audit record saying a deletion happened. Video assets enter a durable deletion queue and are removed from the video host once nothing references them.
05Do you support SSO or enforced MFA?
Not today. Sign-in is handled by Clerk with Google OAuth and email/password plus email verification codes. There's no SAML or enterprise SSO and no enforced authenticator-app MFA yet — if that's what's blocking your team, tell us. That's exactly the kind of demand signal that moves a roadmap.
06Does my card data ever touch your servers?
No. Checkout and billing management happen on Stripe's hosted, PCI-compliant pages — card numbers are entered there and never pass through ReTestimonial. On our side, we verify Stripe's webhook signatures on the raw request body before trusting any billing event.
07How do I report a security vulnerability?
Email privacy@retestimonial.com — the same contact listed in our security.txt at /.well-known/security.txt. We read every report and acknowledge as fast as we honestly can. Please don't access data that isn't yours while demonstrating an issue. There's no public bounty program yet, but we credit reporters who want it.