> For the complete documentation index, see [llms.txt](https://help.resouris.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.resouris.com/part-2-running-resouris/user-export-import.md).

# User export and import

**Who:** tenant admin or platform admin. **Perm:** `admin.users.export` (export), `admin.users.import_transfer` (import). **Time:** \~2 minutes for an export; \~5-10 minutes for a full import walkthrough.

ReSouris v1.6.3 replaces the legacy 4-column user CSV export with a **comprehensive** archive covering the user's full network of linked data — 22 sheets spanning roles, working pattern, competencies, activity + CPD ledgers, job plans, training plans, leave requests, sickness records, assignments, and the audit tail. Two format writers (XLSX + JSON) project from the same canonical dict and a three-step import wizard lets you restore the archive back into the same tenant.

## Two audiences, two use cases

### GDPR Data Subject Access Requests

A staff member asks "what data do you hold about me?" You produce a **DSAR-mode** archive: unredacted for the subject's own data, redacted where cross-references would leak other people's identities (verifiers, assessors, deciding admins). The archive is signed and export-only — the reader refuses to import DSAR archives per design.

### Restore + transfer

A user record was accidentally deleted, or a staff member is moving between departments and you want to preserve their clinical identity. Produce a **transfer-mode** archive: full-fidelity, signed for same-tenant re-import via the wizard.

## Two formats

### XLSX (recommended)

Admins edit exports in Excel. One sheet per source table, plus a `_meta` sheet carrying signature + issuer tenant + shape version + export mode. All UUIDs are text-typed cells; dates are ISO 8601 strings (never Excel date serials); numerics are strings to preserve precision. Excel is deliberately not allowed to reinterpret cells on save.

### JSON

Machine-to-machine channel. Canonical envelope `{"body": ..., "body_canonical_sha256": ..., "signature": {...}}` matches licence-body conventions.

## Export walkthrough

1. Sign in as `tenant_admin` or `platform_admin`.
2. Navigate to `/ui/admin/users`.
3. Find the subject user; open the row-level **Export ▾** menu.
4. Pick one of four variants:
   * **Transfer (XLSX)** — full-fidelity archive for restore.
   * **Transfer (JSON)** — same, but for machine consumers.
   * **DSAR (XLSX)** — redacted archive for the data subject.
   * **DSAR (JSON)** — redacted, machine-consumable.
5. The browser downloads `user-export-{user_id}-{mode}.{xlsx|json}`.

An audit event fires with actor + subject + mode + format + a signature prefix (for correlation with any later import).

## Import walkthrough

The wizard has three steps. Batch state persists across page reloads in the `import_batch` table (MIG-159), keyed by a UUID stamped into every audit event so you can trace the whole flow.

### Step 1 — Upload

1. From `/ui/admin/users`, click **Import (transfer archive)**.
2. Pick the format that matches your archive (XLSX or JSON).
3. Attach the file.
4. Click **Upload + verify**.

The reader refuses:

* **DSAR archives** — DSAR is export-only. Error: `dsar_archive_refused`.
* **Cross-tenant archives** — v1.6.3 accepts only same-tenant signatures. Error: `cross_tenant_refused`. Cross-tenant transfer waits for the per-tenant signing infrastructure enhancement (v1.7+).
* **Malformed archives** — corrupted XLSX or JSON. Error: `malformed_archive`.
* **Future shape versions** — an archive produced by a newer ReSouris. Error: `shape_version_unsupported`.

On success, the batch enters state `uploaded` and the wizard redirects to preview.

### Step 2 — Preview + resolve

The preview page shows:

* **Archive summary** — issuer tenant, subject user, export mode, issued-utc, shape version.
* **FK mapping required** — for every tenant-scoped foreign key that has no automatic match in the target tenant, the wizard lists the source values. You must supply a mapping per column before commit is enabled. Silent match-by-name is refused; every mapping is explicit.
* **Conflicts detected** — source rows whose natural key already exists in the target. Default resolution is **skip** — the wizard never auto-overwrites. You can override per-row (or bulk) to `overwrite` or `merge`.
* **Commit scope** — v1.6.3 commits `user`, `user_roles`, `staff_skills` sheets. Other sheets in the archive are marked *deferred* (documented v1.7+ scope). This is honest signalling so you're not surprised by what actually landed.

### Step 3 — Commit

Use the Advanced JSON panel to supply the FK mapping declaration + per-row conflict resolutions, then click **Commit atomic transaction**. The button is disabled while unresolved FKs remain.

The commit runs in a single Postgres transaction. If any row fails, the whole batch rolls back and the batch transitions to `aborted`. On success it transitions to `committed` and you're redirected back to the users list with the batch id in the URL.

## Audit trail

Every step emits an audit event:

* `users.export.produced` — transfer-mode export
* `users.export.dsar_produced` — DSAR-mode export
* `users.import.uploaded` — Step 1 success
* `users.import.previewed` — Step 2 view
* `users.import.committed` — Step 3 success, with per-sheet commit summary in the payload
* `users.import.aborted` — Step 3 rollback, with the error name

Filter the audit page by these action strings to see any user's export/import history.

## Limitations (v1.6.3)

* **Same-tenant signature acceptance only.** Cross-tenant transfer defers to the per-tenant signing infrastructure enhancement.
* **Bootstrap signing key.** Each process generates a fresh Ed25519 keypair on demand; a signature from one process is not verifiable in another. Production per-tenant keys land with the signing infrastructure enhancement.
* **Commit scope = 3 sheets** (`user`, `user_roles`, `staff_skills`). The other 19 sheets are reported as *deferred* and require v1.7+ committer work for full round-trip.
* **In-process archive cache.** If the API server restarts between upload and commit, the archive must be re-uploaded (error `archive_expired`).
* **No cross-user bulk import.** One user per wizard run.
* **No three-way merge.** Preview shows per-row `skip / overwrite / merge`; if the target row has diverged from the source, you pick one side.

## What comes next

* Understand the audit chain that records every step → [Audit and compliance](/part-2-running-resouris/audit-and-compliance.md).
* Understand the equity model that gives context to what's inside a user's linked-data network → [Equity and fairness model](/how-it-works/equity-model.md).
* Read the underlying architecture → [Architecture](/reference/architecture.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.resouris.com/part-2-running-resouris/user-export-import.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
