Security
Transport, signing, key management, idempotency, PII handling, and the contracts Paperless relies on. Read this with your security team before go-live.
Transport
- TLS 1.2 minimum. TLS 1.3 preferred. Sandbox and production both enforce TLS — plain HTTP is rejected at the load balancer.
- HSTS with a 1-year max-age is set on every Paperless response.
- Certificate pinning is optional but supported on request. If you pin, we will give you 90 days notice before any certificate rotation.
- mTLS is optional and supported. If you want it, exchange CSRs with your solutions engineer at onboarding.
Signing scheme
All webhooks and push requests are signed with HMAC-SHA256. The canonical string is
timestamp + "." + raw_body; see the
webhook contract page for the
complete spec. Notes specific to security review:
- Algorithm: HMAC-SHA256. No fallback to weaker algorithms is permitted.
- Comparison: Paperless uses constant-time comparison server-side. Your verifier must do the same.
- Skew window: ±5 minutes against the receiving side's NTP-disciplined clock.
- Replay protection: the timestamp is part of the canonical string, so a captured signature is only valid within the skew window. We also cache
external_event_idfor 24 hours and reject re-use. - Constant time on both sides: use
hmac.compare_digestin Python,crypto.timingSafeEqualin Node,subtle.ConstantTimeComparein Go.
Key management
Inventory of secrets
| Secret | Lives on | Used for | Pattern |
|---|---|---|---|
| Paperless OAuth client secret | Paperless servers | Authenticating Paperless to your /oauth/token. | A |
| OAuth access token (per merchant) | Paperless DB, encrypted at rest | Bearer on calls to your APIs. | A |
| OAuth refresh token (per merchant) | Paperless DB, encrypted at rest | Mint new access tokens. | A |
| Webhook signing secret | Both sides | HMAC-sign webhook events. | A, B |
| Push API key | Both sides | Authenticate push requests. | B |
Encryption at rest
Paperless encrypts every OAuth token before it touches durable storage. Default implementation uses Fernet (AES-128-CBC + HMAC-SHA256); AWS KMS is supported behind the same interface and selected per deployment. Plaintext tokens never leave RAM on the worker.
On your side, we expect equivalent controls. Concretely:
- Webhook signing secret and Paperless API key live in a managed secret store that encrypts at rest and restricts access. A dedicated vault (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager) is recommended; your platform's own managed secret store is acceptable.
- No secrets in source code, config files, environment variables checked into version control, container images, or build logs.
- Access is restricted to named individuals, and audited where your secret store supports it.
Rotation
| Secret | Default cadence | Rotation procedure |
|---|---|---|
| Webhook signing secret | 12 months | Dual-key window (see below). |
| Push API key | 12 months | Dual-key window. |
| OAuth client secret | On demand | Paperless re-onboards; merchants do not need to re-consent. |
| OAuth access token | Per your TTL (≤ 30 days recommended) | Paperless refreshes automatically when ≤ 7 days left. |
| OAuth refresh token | Per your policy | If you rotate on every refresh, return the new value in the refresh response. |
Dual-key rotation window
For signing secrets and API keys, both sides honor two keys at once during rotation:
- Day 0: Paperless issues
SECRET_NEWalongside the existingSECRET_OLD. - Days 0–7: receiver accepts a signature that validates under either secret.
- You cut over: start signing with
SECRET_NEWwhenever you are ready. - Day 7:
SECRET_OLDis retired. Signatures under it are rejected.
If you need a longer window, request it; 30 days is the maximum.
Idempotency & replay
- Every webhook carries a unique
external_event_id. Paperless dedupes by it. - Every order has a unique
external_id. Paperless writes oneReceiptperexternal_id, ever. - Replays of the same signed payload within the 5-minute skew window succeed but produce no side effects.
- Replays outside the 5-minute window are rejected at the signature layer.
PII handling
| Data | Allowed? | Notes |
|---|---|---|
| Full PAN | ❌ | Never sent. Sending a 13–19 digit value as last4 is treated as a possible PAN leak and the receipt is rejected. |
Card last4 | ✅ | Exactly 4 digits. |
| Card brand | ✅ | Plain text (VISA, MC, …). |
| Cardholder name | ❌ | Do not send. |
| CVV / expiry | ❌ | Never. PCI-DSS prohibits this anyway. |
| Customer email | ✅ | Used for matching. Stored encrypted at rest. |
| Customer phone | ✅ | E.164. Stored encrypted at rest. |
| Government ID numbers | ❌ | Tax number for the merchant only — never for the customer. |
Network egress and IP allowlists
- If your platform allowlists outbound destinations, Paperless will provide a stable set of IPs / hostnames at onboarding. Notify ops 30 days before changing yours.
- Paperless does not allowlist your IPs by default. If you want IP-based restrictions in front of
/webhooks/{provider}, request it.
Logging and audit
- Every webhook is stored in raw form (including headers, signature, body) for 90 days for forensic purposes.
- Every order fetch and token refresh is logged with timestamp, merchant ID, status code, latency.
- You should keep equivalent logs on your side. Ops will reconcile these during incident response.
Incident response
If you suspect a signing-key or API-key compromise:
- Contact your Paperless solutions engineer immediately. They will rotate the affected key within 1 hour.
- Paperless invalidates the old key and rejects any signature that validates under it.
- You re-sign in-flight retries under the new key.
- Paperless will provide a forensic export of the last 30 days of webhook traffic for that merchant on request.
Never log the webhook signing secret, the push API key, or any OAuth token. Log
signatures (header value) only at DEBUG level and scrub them at
INFO and above.
Compliance posture
- Paperless is SOC 2 Type II. Letter of attestation available under NDA.
- No PAN / CVV ever transits or is stored — Paperless is not in PCI scope by data minimization.
- For EU merchants, data is processed in EU regions; data-processing agreement (DPA) available on request.