We never see a card number.
ESDvalidator works on the order data that sits next to the payment: line items, tax, freight and commodity codes. Here is exactly what we store, what we refuse, and who processes it.
We never receive full card numbers. A request that contains one is rejected and nothing from its body is stored.
TLS 1.2 or higher in transit. AES 256 at rest. API keys are stored as a keyed hash and shown once.
Row level security on every table, scoped to your organization. Staff access is limited to the people who run the service.
Request bodies are off in live. In test they expire after 7 days. Metadata is kept 13 months, then deleted.
What we store
Only what your dashboard needs.
- Line items, amounts, tax, freightValidated in memory. In live, we keep the sale amount, merchant name and result for 13 months, never the line items.
- Score, findings, level reachedKept 13 months for your dashboard.
- Card brand, product, issuer countryFrom the card object you send, to find the rate.
- BINOnly if you send it, as the first 6 digits.
- Batch filesEach line only until its item is processed; batches and results for 30 days. A line with a card number is rejected.
- Webhook endpointsThe URL until you delete it. The signing secret only encrypted, shown once. Events and deliveries for 30 days, with no card data.
What we never store
If a request carries a full card number, or a field named for the CVV or expiry date, we reject it.
- Card numbers (PAN)
- Fields named for CVV or expiry
- Full request bodies from live keys are never stored.
A card number never gets past the front door
Every field is scanned for 13 to 19 digit sequences that pass the Luhn check before we parse the payload. A match returns 422 pan_detected with a pointer to the field. Nothing is logged, cached or stored.
{
"type": "https://esdvalidator.com/errors/pan_detected",
"title": "Request contains a card number",
"status": 422,
"detail": "Remove card numbers from the request. Send only the card brand, product and BIN.",
"errors": [{ "pointer": "/metadata/note" }]
}Fee report uploads
How the Diagnostic handles the Adyen or Stripe report you upload.
- Card numbers stop the fileA full card number anywhere stops the read and rejects the file.
- Buyer columns dropped on readCard, shopper and email columns never leave the parser.
- Never written to diskWe read each file in memory. It is never saved to a disk or a bucket.
- Summary kept, detail for 90 daysThe summary stays until your organization deletes it. Per-sale detail expires 90 days after the diagnostic runs.
Signing in
Who can reach your organization and how.
- Email and passwordPasswords of at least 12 characters. Email confirmation is mandatory.
- Optional two-factorAnyone can turn on an authenticator app. The owner can require it for everyone in the organization.
- Confirmation for sensitive changesCreating a live key, revoking keys and changing members asks for a fresh authenticator code, or the password for people without two-factor. With two-factor on, test keys ask too. All of it is written to the audit log.
- Sessions you can endA session ends after 12 hours, or after 30 days with “Keep me signed in”. Your account lists each one with its browser and city, and signs out any of them.
- Trusted devicesSkipping the code at sign-in lasts 30 days on that browser. We keep only a hash of the device token, and sensitive changes still ask for the code.
Subprocessors
We tell customers 30 days before adding one.
| Company | Purpose | Location |
|---|---|---|
| Vercel | Hosting for the site, portal and API | United States |
| Supabase | Database, encrypted at rest | United States |
| Resend | Transactional email | United States |
Report a vulnerability
Report vulnerabilities to hi@esdvalidator.com.