Define What a Complete Export Contains
A customer export should have an agreed boundary, actual records and the promised attachments. A JSON file containing temporary download links can be a useful delivery manifest, but it is not a durable copy of the attachment bytes. State which output the customer is receiving before describing the export as complete.
Begin with the requesting identity, organisation and authority to export. A user who can read one ticket may not be permitted to export the entire organisation. Decide whether the request covers a business's operational records, a particular user's personal data, or a narrower project. The following worksheet is a proposed technical procedure; it does not determine the legal scope of a data-access request.
Include the primary records and their dependent records, such as orders, invoice lines, support messages and attachment metadata. Review shared records explicitly: a contact serving two organisations or an internal support note should not be included merely because one foreign key matches. Document deliberate exclusions and who approved them.
Authorize the Export and Its Files
Enforce the export permission at request creation, job execution and final download. Use server-owned organisation membership and export entitlement, rather than trusting a customer ID supplied by the browser. OWASP recommends checking authorization on every request, including background and download paths. OWASP authorization guidance
Existing table grants and RLS policies help constrain ordinary database users. They do not universally constrain privileged roles: PostgreSQL table owners normally bypass RLS, and superusers and roles with BYPASSRLS are exceptions. Test the effective role that performs the export. PostgreSQL row security
Supabase Storage also uses RLS, while its service key can bypass those policies. A privileged export worker must independently validate its approved organisation and the server-owned attachment mapping before downloading an object. Do not treat a bucket prefix or filename supplied by the requester as sufficient authorization. Supabase Storage access control
Protect the resulting archive as carefully as the original files. Store it privately, use an unpredictable export identifier, restrict its status page and download endpoint, and remove temporary copies according to a documented retention policy. Avoid sending secret links into general application logs or analytics.
Capture a Reproducible Data Boundary
Record an export ID, organisation, scope version, database boundary, start and completion times, and attachment identifiers. The source comparison must refer to that captured boundary. Comparing an exported count with a live table that has changed since the job began can produce a false failure or conceal missing records.
For PostgreSQL, Repeatable Read gives queries in a transaction a consistent database snapshot. It does not stop other transactions writing, and it does not create an atomic snapshot of external object storage. PostgreSQL transaction isolation Keep the database transaction appropriately bounded and confirm the method against the actual database and connection path.
Capture attachment identity, expected length, version identifier where available, and a trusted checksum where the storage design supports it. If objects can change in place, a later download may differ from the metadata snapshot. Use immutable object versions or detect the mismatch and restart the affected export against a new recorded boundary. Do not silently combine yesterday's metadata with today's file bytes.
A multi-database export needs an explicit consistency policy. Record the boundary in each system and disclose any unresolved gap; calling separate reads a single atomic snapshot does not make them one. Routine broad table locks are not a default export solution.
Package Records and Actual Attachments
For a portable full export, stream authorized attachment bytes into the archive or provide durable, verified parts that the recipient can download and retain. A proposed package is:
| Item | Purpose | Verification |
|---|---|---|
| manifest.json | Export ID, scope, boundary and disclosed exceptions | Matches approved request |
| records/customer.json | Customer or organisation root record | Exact approved identity |
| records/orders.json | Orders and required dependent rows | IDs, counts and relationships verified |
| records/tickets.json | Included tickets and permitted messages | Internal exclusions documented |
| files/ | Actual authorized attachments | Bytes match captured identifiers and checksums |
| file-manifest.csv | Attachment ID, archive path, length and hash | One row per promised file |
| verification.json | Test outcomes and unresolved exceptions | No success flag with missing files |
Treat the file manifest as an index to included files, rather than a substitute for them. Split very large exports into numbered parts, record each part's checksum and provide assembly or usage instructions. Measure size and available storage before starting; bounded streaming is preferable to loading every file into memory.
A temporary signed-link manifest is a separate delivery option. Agree its download window and renewal procedure, and label it as temporary access. Supabase private files can be downloaded with authenticated requests or signed links. Signed Storage URLs use an internal signing key separate from Auth JWT keys, so Auth key changes do not revoke them. Supabase private downloads
Token expiry is also separate from response cache lifetime. A warmed Smart CDN response can remain available after the signed token expires. Short expiry alone therefore does not establish a strict final-download cutoff; test the actual CDN and browser behavior, or use an authenticated delivery path with the required checks. Supabase signed URL caching
Filled Hypothetical Export Worksheet
Suppose organisation ABC requests its approved operational records and 12 contract attachments. All quantities below are invented fixtures for this procedure, not a completed customer export.
| Decision | Proposed value | Evidence required before completion |
|---|---|---|
| Request authority | ABC owner with active export permission | Current membership and role check |
| Scope | Profile, orders, tickets and contract attachments | Product owner's boundary approval |
| Exclusions | Internal-only notes and unrelated organisations | Explicit field and record exclusions |
| Database fixture | One profile, 150 orders and 30 tickets | Captured source IDs and counts |
| Attachment fixture | 12 immutable contract versions | Server-owned mapping and version list |
| Output | ZIP or numbered archive parts with file bytes | Archive opens and every manifest path exists |
| Delivery | Private authenticated export route | Recipient identity and grant checked |
| Retention | Proposed seven-day temporary archive retention | Cleanup policy and recorded expiry |
The example seven-day retention is a proposed operational choice. It is not a legal requirement or a promise that downloaded copies disappear after seven days. Select a period that matches the customer's request and the service's obligations.
The job first freezes its approved scope record and captures the database source boundary. It extracts the agreed records, downloads each authorized attachment version, validates the file bytes and builds the package. Only after the checks pass does it make the archive available to the approved requester. Status transitions should distinguish requested, authorized, collecting, verifying, ready and failed; a worker finishing its queries does not prove the delivered export is complete.
Acceptance Checklist and Failure Tests
Use this table as the practical acceptance record. Enter the export ID, evidence location and reviewer beside each result. These are expected outcomes for the hypothetical worksheet, not claims that a live system passed.
| Test | Expected result | Failure action |
|---|---|---|
| Requester authority | Current export permission exists for ABC | Deny job creation |
| Source identity and counts | Captured IDs match one profile, 150 orders and 30 tickets | Investigate missing or duplicate IDs |
| Relationships | Every included child references the intended parent or documented shared record | Repair scope mapping and regenerate |
| File completeness | All 12 promised file versions are present | Keep export failed or explicitly incomplete |
| Byte integrity | Downloaded lengths and checksums match the captured manifest | Reject substituted or truncated bytes |
| Format and readability | Schemas validate and representative files open | Repair serialization or packaging |
| Cross-organisation isolation | A known organisation B marker is absent from records, filenames and metadata | Stop delivery and investigate |
| Recipient download | ABC requester can download; unrelated user cannot | Repair delivery authorization |
| Grant withdrawal | Revoked requester is denied by the delivery endpoint | Recheck current grants and caches |
| Archive retention | Temporary server copies are removed on the approved schedule | Escalate cleanup failure |
Record counts alone are insufficient: 150 duplicated or wrong orders can still produce a count of 150. Compare stable IDs and relevant contents as well as totals. Likewise, an HTTP HEAD returning 200 and the expected content type does not verify a file's contents. Download and inspect the bytes against the captured manifest.
Include a positive control and a negative control. A valid ABC export must succeed, while a known unrelated organisation's record and file must stay unavailable. Error responses must not disclose the other organisation's name, file path, size, count or record content.
Recover Without Hiding Missing Data
If a file fails, keep the export in a failed or incomplete state and record its attachment ID, version, attempt count and error category. Retry transient failures within a proposed bounded policy. Before retrying, confirm the same version and requester authority still apply.
Do not repair a forbidden response by automatically minting a more powerful link. A denial may indicate wrong organisation scope or withdrawn permission. Investigate authorization first. If the object changed, create a new export boundary or disclose the mismatch rather than claiming the original snapshot is intact.
When verification succeeds, record the final archive hashes, manifest, counts, approved exceptions and delivery event. A successful secure delivery provides an operational acceptance record; it does not alone establish compliance with every data-protection obligation.
Frequently asked questions
How can I ensure attachments are not accessible after export?
For new downloads, check current authorization on the export endpoint and apply a tested cache policy. Signed URLs can remain usable independently of login or membership, and warmed CDN responses may outlive token expiry. Auth key rotation does not revoke Storage links; deleting an object has separate propagation and retention consequences. You cannot retract copies the recipient already downloaded.
What if customer data spans multiple databases?
Coordinate export processes across all databases, ensuring consistent customer IDs and relationships. Export data separately and merge in a secure staging area before delivery.
How do I handle large attachments in exports?
For a promised portable full export, stream file bytes into verified archive parts and provide a file manifest and checksums. Temporary signed downloads can support delivery, but explain that the link manifest is temporary access and confirm that all promised files were actually obtained.
Can I automate the export verification?
Yes. Compare stable IDs and counts against the captured source boundary, validate relationships and file bytes, and test both authorized and unrelated recipients. Store the results against the export ID; never mark an export complete while required files are missing.
If your business needs help defining or implementing secure customer data exports, especially with attachments, consider consulting SaaS development experts. For tailored support, get in touch via our SaaS development service route.
For more on SaaS product development, see our SaaS development guide. To understand how custom builds compare with CMS platforms, visit CMS vs Custom Development. Manage ongoing costs with insights from Website Maintenance Costs. Improve user experience by mapping the User Journey.

