Data import / export and public API
Two halves of the data-movement layer. CSV export is shipped end to end — every tenant table is exportable through /api/data-export for GDPR portability, backup, and ad-hoc analysis. The public REST API is stable and in production use by integrations, but ships today without a formal OpenAPI reference document — what's there and how to work with it.
Quick reference
- CSV / JSON export — at /settings/security (or via direct call to
/api/data-export), an owner / manager can request a full tenant data export. The endpoint walks every tenant-scoped table and returns the rows in JSON for portability under GDPR and similar regulations. Rate-limited to 3 exports per minute (it's a heavy operation). - CSV templates for import — see Importing your existing data for the templates, the assisted Migration Hub at /migration, and the field-mapping flow that brings inventory, customers, repairs, and history across from another system.
- Public REST API — the routes under
/api/*(inventory, customers, repairs, appointments, sales, invoices, vouchers, marketing, integrations, …) are stable and in production use by the integrations described elsewhere in this section. Authentication uses the same Supabase session the Nexpura app uses. - Audit log on every export — each export-initiate request writes a row to
audit_logswith the requesting user, tenant, scope, and timestamp. Useful when ops needs to trace “who pulled this data and when?” - Export is owner / manager only — staff roles return 403. Same gate as the rest of the tenant-level admin operations.
Exporting your data
1. Why export
The most common reasons to pull a full data export are standard data-portability ones:
- Backup — a periodic snapshot of your inventory, customers, sales history, and workshop records, archived outside Nexpura as belt-and-braces insurance.
- GDPR / privacy compliance — if a customer requests a copy of their data, the export gives you the machine-readable artefact to send back.
- Ad-hoc analysis — spreadsheet or data-warehouse analysis on the underlying rows for questions the in-product reports don't answer.
- Migration off Nexpura — if you ever decide to move to a different system, the export is your portable copy of everything that lives in your tenant.
2. Run the export
Go to /settings/security. The Data section carries an Export tenant data action; click it. Nexpura walks every tenant-scoped table (inventory, customers, repairs, bespoke jobs, appraisals, appointments, sales, invoices, vouchers, memo, audit logs, …) and returns the rows in a JSON envelope.
The export is initiated synchronously — the request fires and the response carries the bundle. For tenants with very large datasets (years of history, tens of thousands of customers and sales), the export can take a few seconds to return; the browser holds the connection during that time. Rate-limiting caps the frequency at three exports per minute — high enough for normal use, low enough to prevent an accidental tight-loop hammering of the endpoint.
3. What's in the bundle
The JSON envelope contains an object per table — keyed by table name — with the row array as the value. Every tenant-scoped table is included; tables outside your tenant (the platform-level integrations table for Twilio, the global feature flags, etc.) are not. Customer PII is included for portability; the export path decrypts the PII columns server-side before including them in the response.
The shape matches the underlying Postgres tables — same column names, same data types, same row counts. There's no schema-translation layer between the database and the bundle, which makes the export useful for backup-and-restore-shaped scenarios. For ingest into a different system, the column names will need mapping; the data is all there.

4. Audit trail
Every export request writes an audit log row capturing the requesting user ID, tenant ID, requested-at timestamp, and the scope (today always full — partial-scope exports aren't differentiated). The audit row is what powers the “who pulled this and when” question for ops or compliance review.
The audit row lands on initiation, not on completion — so a request that times out or errors mid-execution still leaves the audit trail of having been attempted. That's the right shape for compliance (an attempted exfil is interesting even if it failed); for “did the user get their data?” the response status of the request itself is the definitive answer.
The public REST API
1. The surface
The Nexpura web app and the integrations described in this section both call the same REST endpoints under /api/*. Every domain Nexpura models — inventory, customers, repairs, bespoke jobs, appointments, sales, invoices, quotes, vouchers, memo, marketing, integrations, finance, reports — has its endpoints there. Verbs are standard REST: GET / POST / PATCH / DELETE; bodies are JSON; responses are JSON.
The endpoints are tenant-scoped — every request runs under the authenticated tenant context, so an inventory GET returns only the calling tenant's inventory. Tenant boundaries are enforced by Row-Level Security on the database side; the API layer doesn't have to filter manually.
2. Authentication
The same Supabase session the Nexpura app uses. In practice: log in to /login in a browser, and that session's cookies authorise API calls made from the same origin. Programmatic access (your own scripts hitting the API) requires passing the Supabase auth token in the request — the mechanics are the same as the Supabase JS client, and tokens refresh on the standard 1-hour cycle.
There's no separate API key system for the public API today. The session-cookie / Supabase-token model is what the integrations themselves use, so the security surface is the same. A dedicated API-key model (long-lived, revocable, per-purpose scopes) is something we'd add when the use cases warrant it — if you have a scripting need that the session model doesn't serve, talk to us.
3. Rate limiting and observability
Every endpoint is rate-limited per-tenant or per-IP (depending on the endpoint's sensitivity) — a 429 response with a Retry-After header tells the caller when to try again. Heavy operations (data export, bulk sync) have tighter limits than read-mostly endpoints.
Server-side error visibility runs through Sentry — any 5xx-shaped failure surfaces in the ops dashboard attributed to the originating tenant. For caller-side debugging, response bodies on 4xx errors carry the parsed error reason; 5xx responses carry a generic message and a request ID you can share with us for fast lookup.
Honest disclosure
Two halves of this page live at different maturity levels.
What's shipped today
The CSV / JSON export side is shipped end to end. The /api/data-export endpoint walks every tenant-scoped table — the actual coverage today is on the order of 89+ tables across the schema — and returns the rows in JSON. Rate-limited, owner / manager-only, audit-logged on every initiation, and exercised by real GDPR data-portability requests in production. The honest framing for the export side is “this works the way you'd expect; there's a settings-screen affordance, an audit trail, and a documented data shape.”
The public REST API itself is also shipped — the routes exist, they're stable, they're authenticated, they're rate-limited, and they power both the Nexpura web app and the integrations described elsewhere in this section. Every endpoint you can find by looking at what the app does is real and callable from outside.
What's in build
Formal API reference documentation hasn't shipped yet. There's no OpenAPI / Swagger spec at /api/docs, no per-endpoint reference page on this docs surface, no generated client SDK. The endpoints are stable but discoverability is by-inspection-of-the-app, which is fine if you're working on a Nexpura-side integration or asking us for the endpoint shape, but not fine if you're trying to build a third-party client without us in the loop.
The interim posture: REST endpoints are exposed under /api; full reference docs ship with the API release. For now, talk to us for endpoint inventory — we can list the endpoints that match your use case, share the request / response shapes, and point you at the authentication mechanics. The endpoints don't change in breaking ways without notice, so building against them today and switching to the reference docs when they're published is a viable path.
The dedicated API-key model — long-lived, scope- limited credentials for third-party clients — is on the same release track. The session-cookie auth that the integrations use today is fine for in-app use; an API key model would broaden the use cases (server-to- server scripts, partner integrations) that the session model doesn't naturally serve. Talk to us if these are load-bearing for your store.
Common questions
How big does a full export tend to be?
Depends entirely on tenant size. A new tenant with a few hundred customers and a couple of months of sales history exports in a few hundred kilobytes. A multi-year tenant with thousands of customers, tens of thousands of inventory and sale rows, and the full audit trail produces tens of megabytes. The export streams as a single JSON response, so your browser handles it like any large download.
For very large tenants where a synchronous request isn't practical, talk to us — we can run the export server-side and deliver the bundle via a signed download URL rather than the live request. The endpoint mechanics are the same; the delivery path is the difference.
Can I export just a subset of tables?
Not through the in-product affordance today — the export is full-tenant scope. The audit log row captures the scope as full explicitly, with the implicit allowance for future partial- scope exports (the scope field would carry something like customers_only or inventory_2026) once that path lands.
For one-off partial-scope needs (a single customer's data for a GDPR request, say, or just the inventory for a stocktake reconciliation), the public API endpoints give you targeted access today — you can call GET /api/customers/:id or query the inventory endpoints with filters, rather than pulling a full dump. Talk to us for the endpoint shapes if the manual approach is load-bearing.
Is the export PII-safe to share?
The export contains all the PII Nexpura stores about your customers — names, emails, phone numbers, addresses, purchase history, communication logs. It's your tenant's data so the export is yours to hold, but it's subject to the same privacy obligations as the data when it's in the live system.
Treat the exported bundle like a database backup — encrypt at rest, share only on a need-to-know basis, dispose of it securely when you're done with the analysis. For GDPR portability responses, the bundle is shared with the requesting customer directly; for compliance review, with the auditor under their data-handling agreement; for backup, into the same place your other production-data backups live.
How do I get a list of every API endpoint?
Today: ask us. The endpoint inventory lives in the codebase and the integrations docs (Shopify, WooCommerce, Mailchimp, etc., on the other pages in this section, all describe their specific endpoint names) — there's no generated index. When the formal API reference ships, the inventory will be the canonical answer; until then, the request shape for “do you have an endpoint for X?” gets answered quickly in our support channel.
In the interim, if you're building a Nexpura-side integration (an internal script that talks to the API, a partner integration in co-development), we'll typically send the relevant endpoint shapes inline in the implementation conversation — which is the same data the reference docs will eventually carry, just delivered conversationally.
Will the API change in breaking ways before the reference docs ship?
No — the endpoints behind the Nexpura web app and the production integrations are stable. They evolve additively (new endpoints, new optional parameters on existing endpoints) but not destructively. If you build against today's endpoints, the working contract stays valid through the reference docs release; the docs are catching up to the implementation, not changing it.
That stability is what makes the “ship code, ship reference docs later” sequencing defensible — the cost of using the API without the reference is the discoverability gap, not a moving target underneath you.
Troubleshooting
Export request returned 403 “Insufficient permissions”
Symptom: the export call returned a 403. Cause:the acting user has a role that's neither owner nor manager nor admin. Tenant data export is restricted by design — the cohort that should be able to pull a full data dump is the small one with full tenant-level admin rights. Fix: have an owner or manager run the export, or have an owner grant the calling user the manager role on /settings/team.
Export request returned 429 “Too many requests”
Symptom: the export call returned a 429 with a Retry-After header. Cause: the endpoint rate limit (3 per minute) was exceeded. Most often: someone retried a slow-running export that was still in flight, or two managers triggered an export within the same minute. Fix:wait the duration in the Retry-After header (typically under a minute) and retry. If you're hitting the limit consistently for legitimate reasons, talk to us and we'll raise the per-tenant cap or move you onto the asynchronous server-side path.
Export timed out before returning
Symptom: the export request hung and eventually timed out browser-side without returning the bundle. Cause:the tenant's dataset is large enough that walking every table and assembling the bundle takes longer than the gateway timeout allows. Fix: contact us. The server-side asynchronous export path handles arbitrarily large datasets by running the work in a background job and delivering the bundle via a signed download URL when it's ready. The synchronous front-door endpoint is the right one for normal-sized tenants; the async path is the escape hatch.
API call returned 401 even though I'm logged in
Symptom: a call to a /api/* endpoint returned 401 Unauthorised; you're definitely signed in to Nexpura in the same browser. Cause:the request didn't carry the session cookie. Most often: a cross-origin call from a different domain (browser blocks the cookie), a script using fetch without credentials: "include", or a third-party tool that needs an explicit Authorization header rather than a cookie. Fix: same-origin calls from within the Nexpura app work out-of-the-box. For programmatic clients, include the Supabase JWT in the request — that's the same path the in-app Supabase JS client uses. Talk to us if you're building something the session-cookie model doesn't accommodate.
I can't find the endpoint I need
Symptom:you're looking for the API path to do X and the integrations docs in this section don't specifically mention it. Cause:the endpoint inventory is undocumented today — the honest disclosure above covers it. The endpoints that the in-product flows use are the same ones you'd be calling, but discoverability is by inspection of those flows or by asking us. Fix: contact us with the operation you're trying to perform. We'll point you at the specific path, the expected request body, and the response shape, with the same accuracy the reference docs will eventually carry.
Related
- Importing your existing data — the inbound-direction CSV path with templates and the Migration Hub, complementary to this page's outbound focus
- Integrations & data overview — the broader data-movement model this page sits inside
- Security & 2FA — the settings surface the export action lives on, with the broader security posture