Source: src/models/organization.ts
The Organization document is the root of all per-tenant data. Every other document either references or is embedded inside it. One document exists per tenant; it is looked up by shortName on every request.
Top-level fields
| Field | Type | Required | Default | Notes |
|---|
shortName | String | ✓ | — | Hostname identifier, 2–32 chars. Extracted from the subdomain on every request. |
clerkOrganizationId | String | — | — | Clerk org ID (e.g. org_xxxx). Sparse-indexed; used to match Clerk webhook events. |
licenseStart | Number | ✓ | 0 | Unix timestamp (ms). |
licenseEnd | Number | ✓ | 0 | Unix timestamp (ms). |
licenseCost | Number | ✓ | 0 | Amount in € paid for the license. |
isBlocked | Boolean | ✓ | false | Blocked orgs return 403 on all public routes. |
isPublic | Boolean | ✓ | true | Whether the ROPA is publicly accessible without sign-in. |
isDemo | Boolean | ✓ | false | Marks shared demo orgs. |
schemaVersion | Number | ✓ | 13 | Incremented on each migration. |
defaultActivityAttributes | Mixed | — | {} | Attribute defaults applied when creating new activities. |
highestOuId | Number | ✓ | 4 | Auto-increment counter; incremented when a new OU is created. |
highestActivityId | Number | ✓ | 6 | Auto-increment counter; incremented when a new activity is created. |
highestPartnerId | Number | ✓ | 0 | Auto-increment counter; 0 is reserved for self. |
highestContractId | Number | ✓ | 0 | Auto-increment counter for contracts. |
createdAt / updatedAt | Date | — | Mongoose auto | Managed by timestamps: true; updatedAt is also reset in a pre('save') hook. |
Virtuals
| Virtual | Returns |
|---|
selfOrganization | The partners[] entry where organizationId === 0. |
organizationName | organizationNameLong from selfOrganization; falls back to shortName. |
name | Same as organizationName. |
Embedded arrays
partners[]
Stores the org's own data and all external parties (processors, joint controllers, etc.). At least one entry with organizationId === 0 is required by the validator. See Partner sub-schema below.
ropas[]
One entry per configured locale. Each entry is a lightweight reference to a separate Ropa document.
| Field | Type | Notes |
|---|
locale | String | 2-letter lowercase code, e.g. "en". Matches /^[a-z]{2}$/. |
ropaId | ObjectId | Reference to the Ropa document for this locale. |
isDefault | Boolean | Exactly one entry should be true; determines which locale the root path redirects to. |
templates[]
Org-level and activity-specific template references. The activityId + type pair identifies a unique slot.
| Field | Type | Notes |
|---|
activityId | Number | 0 = org-level template; > 0 = activity-specific. |
type | String | activityDeclaration | activityInformation | ropaFrontPage | ropaTOC. Default: activityDeclaration. |
templateId | ObjectId | Reference to a Template document. |
contracts[]
Data-processing contracts, embedded since the v13 schema migration (previously a separate Contract collection).
| Field | Type | Notes |
|---|
contractId | Number | Required; unique per org. |
contractName | String | Default "". |
contractUrl | String | Default "". |
contractExpirationDate | String | Default null. |
contractDescription | String | Default "". |
activityIds | Number[] | Activity IDs associated with this contract. |
partnerIds | Number[] | Partner IDs associated; must be non-negative integers. |
Partner sub-schema
Source: src/models/partner.ts — embedded in Organization.partners[].
| Field | Type | Required | Default | Notes |
|---|
organizationId | Number | ✓ | — | 0 = this org; > 0 = external party. |
organizationName | String | ✓ | — | Short name, 2–32 chars. |
organizationNameLong | String | ✓ | — | Full legal name, 2–100 chars. |
organizationColor | String | — | "#2196F3" | Hex color #RRGGBB. |
organizationWebsite | String | — | "" | URL; validated by regex. |
organizationPostalAddress | PostalAddress | ✓ | — | See below. |
organizationLogo | Buffer | — | null | Max 256 KB. |
organizationVatNumber | String | — | "" | Format: CC-XXXXXXXX (2-letter country code, dash, up to 14 alphanumeric chars). |
organizationNotes | String | — | "" | Max 200 chars. |
organizationContacts | Contact[] | ✓ | — | At least one required. |
contractOrder | Number[] | — | [] | Ordered list of contractId values for display. |
Virtual: displayName → "organizationNameLong (organizationName)"
Method: getPrimaryContact() → returns organizationContacts[0]
| Field | Type | Required | Default | Notes |
|---|
contactId | Number | ✓ | — | |
contactFirstname | String | ✓ | — | 1–50 chars. |
contactLastname | String | ✓ | — | 1–50 chars. |
contactRole | String | ✓ | "Primary" | 3–24 chars. |
contactEmails | Email[] | ✓ | — | At least one required; at most one isPrimary: true. |
contactPhoneNumbers | PhoneNumber[] | — | [] | At most one isPrimary: true. |
contactNotes | String | — | "" | Max 200 chars. |
Email (_id: false)
| Field | Type | Default | Notes |
|---|
email | String | — | Lowercased; validated by [^\s@]+@[^\s@]+\.[^\s@]+ regex. |
description | String | "Work" | Max 50 chars. |
isPrimary | Boolean | false | |
PhoneNumber (_id: false)
| Field | Type | Default | Notes |
|---|
countryCode | String | "+1" | Format: + followed by 1–4 digits. |
phoneNumber | String | — | 6–15 digits. |
description | String | "Office" | Max 50 chars. |
isPrimary | Boolean | false | |
PostalAddress (_id: false)
| Field | Type | Required | Notes |
|---|
addressLine1 | String | ✓ | Max 100 chars. |
addressLine2 | String | — | Max 100 chars; default "". |
city | String | ✓ | Max 50 chars. |
stateProvince | String | — | Max 50 chars; default "". |
postalCode | String | ✓ | Max 20 chars. |
country | String | ✓ | 2 uppercase letters; ISO 3166-1 alpha-2 (e.g. "ES", "DE"). |