Skip to main content

File Formats Reference

This document is the single reference for all JSON file formats consumed or produced by the application. It covers the template file system (used to seed new organizations), the import/export envelope (used by e/org), and the field-level constraints (enums, size limits, and cross-document rules) that both formats share.

For the HTTP routes, curl examples, and CSV ZIP format see org-import-export.


1. Template Files

Template files live in templates/{name}/ and are loaded by organizationFromTemplateCreate when spinning up a new organization from a preset. Each template is a set of three JSON files.

templates/
demo/
demo-selfOrganization.json
demo-organizations.json
demo-ropa-en.json
demo-ropa-de.json
...
upf/
upf-selfOrganization.json
upf-organizations.json
upf-ropa-ca.json
...
bfdi/
bfdi-selfOrganization.json
bfdi-organizations.json
bfdi-ropa-de.json

The shortName passed to organizationFromTemplateCreate becomes the new org's slug; it does not need to match the template folder name.

1.1 {name}-selfOrganization.json

Org-level metadata and configuration. All counters (highest*) must equal the highest ID actually used across the template data — they are monotonic sequence seeds that prevent ID reuse after the org is live.

{
"licenseStart": 0, // Unix ms timestamp — 0 = unset
"licenseEnd": 0, // Unix ms timestamp — 0 = unset
"licenseCost": 0,
"shortName": "bfdi", // seed value; overwritten by caller
"isBlocked": false,
"isPublic": true,
"isDemo": false,
"highestOuId": 3, // = max ouId used in any ropa file
"highestRopaId": 1, // = number of ropa locales
"highestActivityId": 6, // = max activityId used in any ropa file
"highestPartnerId": 2, // = max organizationId used in organizations file
"highestContractId": 0, // = max contractId used (0 if no contracts)
"contracts": [],
"ropas": [
{ "locale": "de", "ropaId": null, "isDefault": true }
// one entry per locale; exactly one isDefault=true
],
"defaultActivityAttributes": {
// pre-filled values applied when a user creates a new activity
// any valid activity field can appear here
"legalbasis": ["Contract"],
"dataCategories": ["Identification"],
"securityLevel": "medium",
...
},
"createdAt": "",
"updatedAt": "",
"schemaVersion": 12 // must match current schema version
}

Notes

  • licenseStart / licenseEnd can both be 0 here because templates bypass the licenseEnd > licenseStart cross-check applied only to import envelopes.
  • ropaId inside ropas[] is always null in template files — resolved at creation time.

1.2 {name}-organizations.json

All partner organizations. organizationId: 0 is always the self-organization (the controller). All other entries are processors, joint controllers, or other partners.

{
"partners": [
{
"organizationId": 0, // 0 = self (required)
"organizationNameLong": "Musterfirma GmbH",
"organizationName": "MUSTER", // short label, 2–32 chars
"organizationColor": "#1D4ED8", // 6-digit hex
"organizationWebsite": "https://www.musterfirma.de",
"organizationPostalAddress": {
"addressLine1": "Musterstraße 1",
"addressLine2": "",
"city": "Berlin",
"stateProvince": "Berlin",
"postalCode": "10115",
"country": "DE" // ISO 3166-1 alpha-2, uppercase
},
"organizationLogo": null, // null or base64-encoded PNG
"organizationNotes": "...",
"organizationContacts": [
{
"contactId": 1,
"contactFirstname": "Datenschutzbeauftragte",
"contactLastname": "-",
"contactRole": "DPO",
"contactEmails": [
{ "email": "dpo@example.de", "description": "DPO", "isPrimary": true }
],
"contactPhoneNumbers": [
{ "countryCode": "+49", "phoneNumber": "3012345678",
"description": "Büro", "isPrimary": true }
],
"contactNotes": ""
}
],
"contractOrder": [] // ordered contractId list for this partner
},
{ "organizationId": 1, ... },
{ "organizationId": 2, ... }
],
"highestPartnerId": 2 // = max organizationId above
}

1.3 {name}-ropa-{locale}.json

One file per locale declared in selfOrganization.ropas[]. Contains the full processing activity tree.

{
"orgId": "", // blank — filled at creation time
"orgShortName": "bfdi", // template name
"locale": "de",
"ous": [
{
"ouName": "Personalwesen",
"ouColor": "#C084FC", // any CSS color string
"ouId": 1, // unique within this file, ≤ highestOuId
"activities": [
{
"activityId": 1, // unique within the OU, ≤ highestActivityId
"activityName": "Personalverwaltung",
"purposeShort": "...",
"purposeLong": "...",
"legalbasis": ["LegalObligation", "Contract"],
"legalbasisLong": "...",
"legalbasisSpecial": ["Employment"],
"dataCategories": ["Identification", "Professional", "Financial"],
"datasubjectCategories": "Beschäftigte",
"activityCategories": ["DataCollection", "DataStorage"],
"dataOrigin": "...",
"timeLimit": "...",
"profiling": false,
"communications": "...",
"communicationsLong": "...",
"controllers": [0], // organizationId references
"processors": [1], // organizationId references
"transfers": false,
"transfersLong": "...",
"securityLevel": "high",
"securityMeasuresLong": "...",
"active": true,
"createdAt": 0, // stamped at creation; 0 in templates
"updatedAt": 0
}
]
}
],
"createdAt": "",
"updatedAt": ""
}

2. Import / Export Envelope

The envelope format is used when uploading via e/org (the admin import route). It is a single self-contained JSON file.

{
"exportVersion": 1, // literal 1
"exportedAt": "2026-05-18T00:00:00.000Z", // ISO 8601 with timezone offset
"organization": {
"shortName": "bfdi",
"licenseStart": 0,
"licenseEnd": 4102444800000, // must be strictly > licenseStart
"licenseCost": 0,
"isBlocked": false,
"isPublic": true,
"isDemo": false,
"highestOuId": 3,
"highestActivityId": 6,
"highestPartnerId": 2,
"highestContractId": 0,
"schemaVersion": 12,
"defaultActivityAttributes": { ... },
"partners": [ ... ], // same shape as organizations.json
"contracts": [],
"ropas": [
{ "locale": "de", "isDefault": true }
// ropaId is ABSENT here — resolved at import time
],
"templates": [] // template refs ABSENT — rebuilt at import
},
"ropas": [
{
"orgShortName": "bfdi", // must match organization.shortName
"locale": "de",
"ous": [ ... ] // same ous shape as ropa template files
}
],
"templates": [] // Handlebars templates, empty if none
}

Key differences from template files

AspectTemplate filesImport envelope
licenseEnd > licenseStartNot enforcedRequired (cross-check fails if equal)
organization.ropas[].ropaIdnull presentField absent
organization.templates[]Not presentEmpty array []
ropas[].orgShortNameNot present (top-level field)Required in each ROPA document
contactFirstname min charsNot validated2 chars minimum
exportVersion / exportedAtAbsentRequired

3. Field Reference

3.1 Enum values

All enum fields must use one of the listed string values. Empty string "" means "not specified" and is valid everywhere it appears.

legalbasis — Art. 6 DSGVO

ValueGDPR basis
"Consent"Art. 6(1)(a) — Einwilligung
"Contract"Art. 6(1)(b) — Vertragserfüllung / vorvertragliche Maßnahmen
"LegalObligation"Art. 6(1)(c) — rechtliche Verpflichtung
"VitalInterests"Art. 6(1)(d) — lebenswichtige Interessen
"PublicTask"Art. 6(1)(e) — öffentliches Interesse / hoheitliche Aufgabe
"LegitimateInterest"Art. 6(1)(f) — berechtigte Interessen

legalbasisSpecial — Art. 9 DSGVO (special categories)

ValueGDPR basis
"ExplicitConsent"Art. 9(2)(a) — ausdrückliche Einwilligung
"Employment"Art. 9(2)(b) — Beschäftigung / Sozialschutz
"VitalInterests"Art. 9(2)(c) — lebenswichtige Interessen
"NonProfit"Art. 9(2)(d) — nicht gewinnorientierte Einrichtungen
"PublicInformation"Art. 9(2)(e) — offensichtlich öffentlich gemachte Daten
"LegalClaims"Art. 9(2)(f) — Geltendmachung von Rechtsansprüchen
"SubstantialPublicInterest"Art. 9(2)(g) — erhebliches öffentliches Interesse
"Healthcare"Art. 9(2)(h) — Gesundheitsvorsorge / medizinische Versorgung
"PublicHealth"Art. 9(2)(i) — öffentliche Gesundheit
"Archiving"Art. 9(2)(j) — Archivzwecke / Forschung / Statistik

dataCategories

ValueCategory
"Identification"Namen, Adressen, Ausweisdaten
"Characteristics"Geburtsdatum, Staatsangehörigkeit, persönliche Merkmale
"Professional"Beruf, Qualifikationen, Arbeitsverhältnis
"Social"Familienstand, Hobbys, soziale Netzwerke
"Financial"Bankdaten, Gehalt, Steuerdaten
"Commercial"Verträge, Transaktionen, Kaufhistorie
"SpecialEthnic"Art. 9 — ethnische Herkunft
"SpecialHealth"Art. 9 — Gesundheitsdaten
"SpecialBiometric"Art. 9 — biometrische Daten
"SpecialGenetic"Art. 9 — genetische Daten
"SpecialSexual"Art. 9 — sexuelle Orientierung
"SpecialUnion"Art. 9 — Gewerkschaftszugehörigkeit
"SpecialPolitical"Art. 9 — politische Meinungen
"SpecialReligious"Art. 9 — religiöse Überzeugungen
"SpecialCriminal"Art. 10 — strafrechtliche Verurteilungen

activityCategories

ValueActivity type
"DataCollection"Erhebung
"DataGeneration"Generierung / Ableitung
"DataAccess"Zugriff / Abfrage
"DataStorage"Speicherung
"DataCommunication"Übermittlung / Weitergabe
"DataDissemination"Veröffentlichung / Verbreitung
"DataErasure"Löschung
"DataRetention"Archivierung / Aufbewahrung

securityLevel

ValueMeaning
"high"Hoch — sensible oder besondere Kategorien
"medium"Mittel — personenbezogene Standarddaten
"low"Niedrig — geringes Risiko
"NA"Nicht anwendbar

Template types (for organization.templates[] and templates[])

ValuePurpose
"activityDeclaration"Per-activity declaration page
"activityInformation"Privacy information sheet
"ropaFrontPage"ROPA cover / front page
"ropaTOC"ROPA table of contents

3.2 Size constraints

Source of truth: src/constants/ropaAttributes.js. Enforced at three layers: Mongoose model, Zod import schema, and client-side form.

Activity fields

FieldMinMaxNotes
activityName80ACTIVITY_NAME_MAX_LENGTH
purposeShort512PURPOSE_SHORT_MAX_LENGTH
purposeLong2048PURPOSE_LONG_MAX_LENGTH
All other text fields512TEXT_ATTRIBUTE_MAX_LENGTH (default)

Note: PURPOSE_SHORT_MAX_LENGTH is 512, not 80.

Organization / partner fields (import envelope only)

FieldMinMaxNotes
organization.shortName232
organizationName232Short label
organizationNameLong2100Full legal name
organizationWebsite200
organizationNotes200
addressLine11100
addressLine2100
city150
stateProvince50
postalCode120
country22ISO 3166-1 alpha-2, uppercase

Contact fields (import envelope only)

FieldMinMaxNotes
contactFirstname250"-" (1 char) fails — use "--" or a real name
contactLastname150"-" is acceptable
contactRole324"DPO" is exactly the minimum
contactNotes200
email.description50
phone.countryCodeFormat: + followed by 1–4 digits
phone.phoneNumber615Digits only
phone.description50

4. Cross-Document Validation Rules

These rules are checked by organizationCheck (src/lib/schemas/importSchema.js) after Zod structural validation. Violations cause the import to fail before any database writes.

#Rule
1Every locale in organization.ropas[] must have a matching document in the top-level ropas[] array.
2Every document in the top-level ropas[] array must be declared in organization.ropas[].
3No duplicate locales in ropas[].
4Every ropas[].orgShortName must equal organization.shortName.
5Every templates[].orgShortName must equal organization.shortName.
6activityId values must be unique within each OU (per ROPA document).
7ouId values must be unique within each ROPA document.
8All controllers[] and processors[] IDs referenced in activities must exist in organization.partners[].
9organization.licenseEnd must be strictly greater than organization.licenseStart.
10Exactly one entry in organization.ropas[] must have isDefault: true.
11organization.partners[] must contain an entry with organizationId: 0.
12Each contact may have at most one isPrimary: true email and at most one isPrimary: true phone.

5. Source Files

FileRole
src/constants/ropaAttributes.jsEnum values and size-constraint constants
src/lib/schemas/importSchema.jsZod schemas + cross-document validation
src/services/exportService.jsSerializes org + ROPAs + templates to envelope
src/services/importService.jsDeserializes envelope and writes to MongoDB
src/lib/mongoose/organizationFromTemplateCreate.jsLoads template files and creates org
templates/demo/Reference template (multi-locale)
templates/upf/Reference template (Catalan/Spanish/English)
templates/bfdi/German BFDI Art. 30 DSGVO template (German only)