This document is a template. It is not legal advice. Deployers must review and adapt it with qualified counsel before use.
This privacy notice describes the personal data Stellar Checkout (Quay) processes, why it processes it, with whom it shares it, how long it retains it, and the rights available to data subjects under the Nigeria Data Protection Act 2023 (NDPA).
The operator of this Quay deployment is the data controller. The deployer must fill in their legal details below before going live.
| Field | Value |
|---|---|
| Legal Name | [OPERATOR LEGAL NAME] |
| Registered Address | [OPERATOR ADDRESS] |
| Contact Email | [DPO OR PRIVACY CONTACT EMAIL] |
| Data Protection Officer | [DPO NAME AND CONTACT] |
| NDPA Registration Number | [NDPC REGISTRATION NUMBER, IF APPLICABLE] |
NOTICE_VERSION: 2026.1 — the version of this notice (NOTICE_VERSION in apps/web/app/privacy/page.tsx); increment it when the notice changes materially. It is not yet what consent records store: POST /seller/kyc/consent currently writes a fixed notice_version of 1.0 into kyc_consents (issue 4.26), which is what GET /seller/profile/export and docs/API.md show.
The table below lists every personal data item Quay stores, where it lives, the purpose, lawful basis under NDPA, recipients, retention, and how it can be erased.
| Data | Where (table.column) | Encrypted | Purpose | Lawful Basis (NDPA) | Recipients | Retention | Erasable via |
|---|---|---|---|---|---|---|---|
| Seller wallet address | sellers.wallet |
No (public key) | Unique identifier for the seller; destination for on-chain payments; used for SEP-10 auth | Contract (service provision); Legitimate interest (fraud prevention) | Anchor (SEP-10 auth), Hosting provider, DB provider | Indefinite (the sellers row is kept by erasure and by the sweep) |
Not erased by DELETE /seller/profile; deleting the account is a separate decision made with the operator |
| Seller name | sellers.name |
No | Display name in dashboard; included in receipts sent to buyers | Contract (service provision) | Buyers (in receipts), Hosting provider, DB provider | Indefinite (the sellers row is kept by erasure and by the sweep) |
Not erased by DELETE /seller/profile; deleting the account is a separate decision made with the operator |
| Profile kind | sellers.profile_kind |
No | Determines which SEP-9 field set to present | Contract (service provision) | Hosting provider, DB provider | Indefinite (the sellers row is kept by erasure and by the sweep) |
Not erased by DELETE /seller/profile; deleting the account is a separate decision made with the operator |
| Payout fields (legacy plaintext) | sellers.payout_fields_json |
No (plaintext JSON) | Reuse bank/account details on subsequent cash-outs | Contract (service provision) | Anchor (cash-out), Hosting provider, DB provider | Until erased, or until the inactivity sweep (KYC_RETENTION_DAYS, default 730 days) |
DELETE /seller/profile (dashboard "Erase my identity data"); inactivity sweep |
| Payout fields (encrypted) | sellers.payout_fields_encrypted |
Yes (AES-256-GCM, KYC_ENCRYPTION_KEY) | Reuse bank/account details on subsequent cash-outs | Contract (service provision) | Anchor (cash-out), Hosting provider, DB provider | Until erased, or until the inactivity sweep (KYC_RETENTION_DAYS, default 730 days) |
DELETE /seller/profile (dashboard "Erase my identity data"); inactivity sweep |
| KYC fields (encrypted) | seller_kyc.fields_encrypted |
Yes (AES-256-GCM, KYC_ENCRYPTION_KEY) | Satisfy anchor SEP-12 KYC requirements | Contract (service provision); Legal obligation (anchor AML) | Anchor (SEP-12), Hosting provider, DB provider | Until erased, or until the inactivity sweep (KYC_RETENTION_DAYS, default 730 days) |
DELETE /seller/profile (dashboard "Erase my identity data"); inactivity sweep; the same call also asks the anchor to erase its copy |
| Anchor customer ID | seller_kyc.customer_id |
No | Reference to seller's record at the anchor; avoids re-creation | Contract (service provision) | Anchor, Hosting provider, DB provider | Until erased, or until the inactivity sweep (KYC_RETENTION_DAYS, default 730 days) |
DELETE /seller/profile (dashboard "Erase my identity data"); inactivity sweep |
| Anchor SEP-10 session token | anchor_sessions.token_encrypted |
Yes (AES-256-GCM, WEBHOOK_SECRET_ENCRYPTION_KEY) | Bearer token for seller's KYC/withdrawal actions at anchor | Contract (service provision) | Anchor, Hosting provider, DB provider | Until expires_at (set by the anchor, capped by Quay at 7 days); an hourly sweep then deletes the row once it has been expired for 24 hours; also removed by the inactivity sweep |
Seller disconnect (DELETE /seller/anchor-auth); DELETE /seller/profile; auto-expiry sweep |
| Payer wallet address | link_payments.payer |
No | Correlates on-chain payment to the buyer | Legitimate interest (payment matching, fraud detection) | Hosting provider, DB provider | Indefinite (audit trail) | Not erasable (audit requirement) |
| Webhook URLs | webhooks.url |
No | Deliver payment/cash-out events to seller's systems | Contract (service provision) | Hosting provider, DB provider | Indefinite (while webhook active) | Seller webhook deletion |
| API key metadata | api_keys.name, api_keys.prefix, api_keys.scopes |
No (key itself never stored; only scrypt hash) | Identify and scope programmatic access | Contract (service provision) | Hosting provider, DB provider | Indefinite (while key active) | Seller API key revocation |
| Logs (redacted) | Application logs (stdout/file) | N/A | Debugging, monitoring, audit trail | Legitimate interest (operational stability, security) | Hosting provider, log aggregator | Per log retention policy (default 30 days) | Log rotation |
| Database backups | Turso/SQLite dump files | Matches source tables | Disaster recovery | Legal obligation (business continuity) | DB provider (Turso), storage provider | 30 days (configurable) | Backup expiry / manual deletion |
| KYC consent records | kyc_consents (4.26) |
No | Record seller's consent to share fields with anchor | Legal obligation (NDPA consent) | Hosting provider, DB provider | Until erased, or until the inactivity sweep (KYC_RETENTION_DAYS, default 730 days); revoking consent keeps the row with revoked_at set |
DELETE /seller/profile (dashboard "Erase my identity data"); inactivity sweep. Revocation (DELETE /seller/kyc/consent/:anchorDomain) stops future sends but does not delete the row |
| SEP-24 prefill consent | prefill_consents |
No (field names only, never values) | Record which SEP-9 fields the seller agreed to put in an anchor's SEP-24 form prefill (/seller/kyc/prefill-consent) |
Consent (NDPA) | Hosting provider, DB provider | Until erased, or until the inactivity sweep (KYC_RETENTION_DAYS, default 730 days) |
DELETE /seller/profile (dashboard "Erase my identity data"); inactivity sweep; consent can be withdrawn with an empty PUT /seller/kyc/prefill-consent |
| PII access log | pii_audit |
No (seller id, actor, action, field names and request id; never values) | Show who read or wrote a seller's identity data, for breach assessment | Legal obligation (NDPA security and breach duties); Legitimate interest (security) | Hosting provider, DB provider | 2 years, then deleted by a daily sweep; not removed by erasure | Daily sweep only; deliberately kept when the seller erases their data |
| KYC disclosure history | kyc_disclosure_fields |
No (field names and send times only, never values) | Show the seller which fields were sent to which anchor and when | Legal obligation (NDPA transparency) | Hosting provider, DB provider | Until erased, or until the inactivity sweep (KYC_RETENTION_DAYS, default 730 days) |
DELETE /seller/profile (dashboard "Erase my identity data"); inactivity sweep |
| Seller profile (4.23) | seller_profile |
Yes (AES-256-GCM, KYC_ENCRYPTION_KEY) | Reusable SEP-9 field set across anchors | Contract (service provision) | Anchor (on submit), Hosting provider, DB provider | Until erased, or until the inactivity sweep (KYC_RETENTION_DAYS, default 730 days) |
DELETE /seller/profile (dashboard "Erase my identity data"); inactivity sweep |
The inactivity sweep runs daily when KYC_RETENTION_DAYS is greater than 0 (default 730; 0 disables it). A seller's activity is their last wallet login, link creation or KYC submission; creation time is used if they have none. For a seller inactive for longer than the period it deletes, in one transaction, the reusable profile, KYC records, disclosure history, consent records and anchor sessions, and clears the saved payout fields. It skips any seller with a link in offramp_pending, never contacts the anchor, and never touches the sellers row, links, payments or cash-out jobs. See docs/RUNBOOK.md ("KYC and Identity Data Retention Policy").
| Recipient | Data Categories | Legal Basis | Safeguards |
|---|---|---|---|
Anchor (e.g., testanchor.stellar.org on testnet) |
KYC fields, payout fields, wallet address, SEP-10 session token | Contract (service provision); Legal obligation (anchor AML/KYC) | Anchor's own NDPA compliance; HTTPS; SEP-10 JWT scope limited to KYC/withdrawal |
| Hosting Provider (Render) | All data in database, logs, backups | Contract (service provision) | Render's DPA; ISO 27001; data residency in selected region |
| Database Provider (Turso/libSQL) | All data in database, backups | Contract (service provision) | Turso's DPA; encryption at rest; SOC 2 |
| Payment Sender (Buyer's Wallet) | Seller wallet address, link reference, amount, asset | Contract (service provision) | On-chain; no additional data shared |
| Buyer (receipt) | Seller name, wallet address, amount, asset, status | Contract (service provision) | Only data seller chose to display |
| Law Enforcement / Regulator | Any data subject to lawful request | Legal obligation | Only on valid legal process; documented |
Quay deployments on Render may use US-based infrastructure. Data may be processed in the United States. The deployer must ensure appropriate transfer mechanisms (e.g., Standard Contractual Clauses) are in place if personal data leaves Nigeria.
Data subjects (sellers) have the following rights:
| Right | How to Exercise |
|---|---|
| Access | Dashboard "Download my data" (GET /seller/profile/export, issue 4.27): a JSON file of your profile, KYC records, consents, disclosure history, anchor connections, saved payout details, links, payments, cash-outs, webhook URLs and API key names. Session login only (API keys are refused) and limited to 5 downloads per hour. It never contains tokens, secrets or key hashes. |
| Rectification | Update name/profile kind in dashboard; KYC fields updated via re-submission |
| Erasure | Dashboard "Erase my identity data" (DELETE /seller/profile, issue 4.28): you type your wallet address to confirm, Quay asks the anchor to erase its copy first, then deletes your profile, KYC records, consents, disclosure history and anchor sessions and clears saved payout fields. Retained: payment history (public on the Stellar ledger), links and payment records (your accounting), your seller account (your wallet login; deleting the account is a separate decision, contact the operator) and database backups (until they expire). |
| Restriction | Contact support to restrict processing of specific fields |
| Portability | "Download my data" (JSON) for identity and account data; "Export CSV" on the dashboard (GET /links/export/csv) for payment records |
| Objection | Object to processing based on legitimate interest via support |
| Withdraw Consent | Revoke KYC consent for specific anchor (DELETE /seller/kyc/consent/:anchorDomain, issue 4.26) |
Response Time: We respond within 30 days as required by NDPA Section 34.
| Data Category | Retention Period | Trigger for Deletion |
|---|---|---|
| Seller account data (wallet, name, profile kind) | Indefinite | Not removed by erasure or by the inactivity sweep; account deletion is a separate decision made with the operator |
| KYC fields, anchor customer ID, reusable profile, disclosure history | Until erased, or until the seller has been inactive for KYC_RETENTION_DAYS (default 730 days; 0 disables) |
DELETE /seller/profile; daily inactivity sweep (skips sellers with a cash-out in progress) |
| Payout fields | Until erased, or until the same inactivity period | DELETE /seller/profile; daily inactivity sweep |
| Anchor session tokens | Until expires_at (set by the anchor, capped at 7 days), deleted by an hourly sweep 24 hours after expiry |
Auto-expiry sweep; disconnect; DELETE /seller/profile; inactivity sweep |
| Payment records (payer, amount, link) | Indefinite | N/A (audit trail) |
| Webhook URLs | Indefinite while active | Webhook deletion |
| API keys | Indefinite while active | Key revocation |
| Logs | 30 days (configurable) | Log rotation |
| Backups | 30 days (configurable) | Backup rotation |
| PII access log (who read which identity field names, when; never values) | 2 years, then deleted by a daily sweep. Not removed by erasure, so a breach can still be assessed after a seller erases their data | Daily sweep |
| Consent records | Until erased, or until the same inactivity period; revocation keeps the row with revoked_at set |
DELETE /seller/profile; daily inactivity sweep |
In the event of a personal data breach:
-
Detect & Contain (immediate)
- Rotate
KYC_ENCRYPTION_KEY(see issue 4.31) - Revoke all
anchor_sessions(issue 4.26 revocation) - Assess scope: which tables, how many sellers, what data exposed
- Rotate
-
Assess Risk (within 24 hours)
- Likelihood of harm to data subjects
- Types of data involved (KYC fields = high risk)
-
Notify (within 72 hours of awareness)
- NDPC (Nigeria Data Protection Commission): via ndpc.gov.ng breach portal
- Affected Sellers: email with description, likely consequences, measures taken, contact for more info
- Anchor(s): if their data was involved
- Hosting/DB Providers: per DPA obligations
-
Document
- Record in incident log (date, scope, root cause, actions, notifications sent)
- Update this notice if material changes
See docs/RUNBOOK.md "PII Breach" section for operational steps.
- Encryption at rest: KYC fields and payout fields encrypted with AES-256-GCM (
KYC_ENCRYPTION_KEY) - Encryption in transit: All API/anchor traffic over HTTPS
- Pseudonymization: Seller wallet is pseudonymous identifier; no email/phone stored
- Data minimisation:
selectFieldsForAnchor(issue 4.25) sends only fields the anchor requests - Purpose limitation: Each data item collected for a specific, documented purpose
- Access audit: every read of a seller's identity data through an API route or a system path (such as the cash-out KYC gate) is recorded with the actor and request id, field names only. Operators query it with
GET /telemetry/pii-audit?sellerId=...to answer "whose data was read, by what, and when" during a breach assessment; sellers see their own entries in the data export - Storage limitation: Retention schedule enforced (daily inactivity sweep for identity data, hourly anchor session sweep); backups rotated
For questions or to exercise rights:
- Email:
[PRIVACY CONTACT EMAIL] - Address:
[OPERATOR ADDRESS]
Last updated: 2026. Generated from docs/PRIVACY.md. NOTICE_VERSION: 2026.1.