Consenti

Admin API Routes

Base path: /consenti/admin (default — set via basePath in createConsenti()). All routes require a valid JWT passed as Authorization: Bearer <token>.

ℹ️Obtain a token via POST /consenti/admin/auth/login. Include it in the Authorization header of every subsequent request.
💡Explore and test all routes interactively in the Swagger UI.
MethodPathDescription
POST/auth/loginAuthenticate — returns a JWT
GET/auth/meGet current authenticated user
POST/auth/logoutInvalidate session
GET/profilesList all profiles
GET/profiles?summary=1List profiles as ProfileSummary[] (lightweight, with template names)
POST/profilesCreate a profile — 422 on compliance errors; conflict detection on active group
GET/profiles/:idGet a profile
PUT/profiles/:idUpdate a profile (bumps version)
DELETE/profiles/:idDelete a profile
POST/profiles/:id/activateActivate a profile — writes locale JSONs to compliance group directory
POST/profiles/:id/deactivateDeactivate a profile — removes compliance group locale files
GET/profiles/:id/versionsList version history entries
GET/profiles/:id/versions/:nRead a specific version locale file
POST/profiles/validateValidate cookies against a compliance group (no save)
GET/compliance-coverageActive profile per compliance group
GET/cookie-templatesList cookie templates
GET/cookie-templates/:idGet a cookie template
POST/cookie-templatesCreate a cookie template
PUT/cookie-templates/:idUpdate a cookie template
DELETE/cookie-templates/:idDelete a cookie template — 422 if active profiles use it
POST/cookie-templates/:id/copyDuplicate a cookie template
GET/cookie-templates/:id/profile-usageList profiles using this template
GET/ui-templatesList UI templates
GET/ui-templates/:idGet a UI template
POST/ui-templatesCreate a UI template
PUT/ui-templates/:idUpdate a UI template
DELETE/ui-templates/:idDelete a UI template
POST/ui-templates/:id/copyDuplicate a UI template
GET/ui-templates/:id/profile-usageList profiles using this template
GET/analytics/opt-inOpt-in rate stats by locale and date
GET/consentsList consent records (paginated)
GET/consents/:visitorIdGet consent record for a visitor
GET/consents/:visitorId/historyGet consent change history for a visitor
GET/visitorsList visitor records (paginated)
GET/usersList admin users
POST/usersCreate an admin user
PUT/users/:idUpdate an admin user (including allowedTenants)
GET/rolesList roles
POST/rolesCreate a role
GET/roles/:id/permissionsGet permissions assigned to a role
POST/roles/:id/permissionsAssign a permission to a role
DELETE/roles/:id/permissions/:permIdRevoke a permission from a role
GET/permissionsList all available permissions
GET/apikeysList API keys
POST/apikeysCreate an API key
DELETE/apikeys/:idRevoke an API key
GET/auditGet audit log (paginated)
GET/stats/overviewConsent overview statistics
GET/stats/timelineDaily consent counts
GET/stats/categoriesPer-category acceptance rates
GET/stats/countriesConsents by country
GET/stats/gpcGPC detection statistics
GET/export/consentsExport consent records (CSV or JSON)
GET/export/consents/xlsxExport consent records as XLSX
GET/export/auditExport audit log (CSV or JSON)
GET/export/translations/:profileIdExport all translatable fields as CSV
GET/tenantsList tenants (multi-tenant mode)
POST/tenantsCreate a tenant
PUT/tenants/:idUpdate a tenant
DELETE/tenants/:idDelete a tenant
GET/tcf/vendorsList IAB TCF vendors
GET/tcf/purposesList IAB TCF purposes

Auth

POST /auth/login

Authenticates with email and password. Returns a JWT for use in all subsequent admin requests.

// POST /consenti/admin/auth/login HTTP/1.1
// Content-Type: application/json

{
  "email": "[email protected]",
  "password": "your-password"
}

GET /auth/me

// GET /consenti/admin/auth/me HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

POST /auth/logout

// POST /consenti/admin/auth/logout HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Profiles

GET /profiles

// GET /consenti/admin/profiles HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

POST /profiles

Creates a profile. When the profileJson.complianceGroup field is set, Consenti runs server-side compliance validation before saving. Returns 422 if there are blocking errors; returns 201 with a warnings array for soft warnings.

// POST /consenti/admin/profiles HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Content-Type: application/json

{
  "name": "GDPR Profile",         // required
  "defaultLocale": "en",          // required
  "isDefault": false,             // optional
  "profileJson": {                // required
    "complianceGroup": "opt-in",  // optional — enables compliance validation + geo-routing
    "isActive": true,             // optional — marks this profile active for the complianceGroup
    "gpcCompliance": "ignore",    // optional — "ignore" | "honor" | "strict"
    "cookies": [
      {
        "id": "necessary",
        "label": "Necessary",
        "legalBasis": "mandatory",
        "required": true,
        "defaultGranted": true
      },
      {
        "id": "analytics",
        "label": "Analytics",
        "legalBasis": "consent",
        "required": false,
        "defaultGranted": false
      }
    ],
    "translations": {
      "en": {
        "bannerTitle": "We use cookies",
        "bannerDescription": "This site uses cookies to improve your experience.",
        "acceptAllLabel": "Accept All",
        "rejectAllLabel": "Reject Optional",
        "customizeLabel": "Customize"
      }
    }
  }
}

GET /profiles/:id

// GET /consenti/admin/profiles/my-profile HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

PUT /profiles/:id

All fields are optional — only supplied fields are applied. Version is bumped automatically.

// PUT /consenti/admin/profiles/my-profile HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Content-Type: application/json

{
  "name": "Updated Profile Name",   // optional
  "defaultLocale": "fr",            // optional
  "isDefault": false,               // optional
  "profileJson": { ... }            // optional — replaces entire profileJson
}

DELETE /profiles/:id

// DELETE /consenti/admin/profiles/my-profile HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

GET /profiles?summary=1

Returns a lightweight ProfileSummary[] — no blob, includes template names. Used by the dashboard profile list view. Without ?summary=1, the full profile including profileJson blob is returned.

// GET /consenti/admin/profiles?summary=1 HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

POST /profiles/:id/activate

Activates a profile for its complianceGroup. Consenti copies locale JSON files from ${profileId}/${version}/ to ${complianceGroup}/ — the static hot-serve path. Only one profile per compliance group can be active at a time; activating a new one deactivates the previous one automatically.

// POST /consenti/admin/profiles/profile-uuid/activate HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

POST /profiles/:id/deactivate

Deactivates a profile. Removes ${complianceGroup}/ locale files from disk so the profile is no longer served on the hot path. The versioned files under ${profileId}/${version}/ are preserved.

// POST /consenti/admin/profiles/profile-uuid/deactivate HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

GET /profiles/:id/versions

Returns the version history for a profile. Each entry corresponds to an immutable snapshot written to disk when the profile was saved.

// GET /consenti/admin/profiles/profile-uuid/versions HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

GET /profiles/:id/versions/:n

Reads the locale JSON for a specific version. Accepts an optional ?locale=en query param; defaults to defaultLocale.

// GET /consenti/admin/profiles/profile-uuid/versions/2?locale=fr-FR HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Profile save conflict detection

Only one profile per complianceGroup can be active at a time. When creating or updating a profile with isActive: true and another active profile already exists for the same compliance group, the backend returns 200 with:

{
  "conflict": { "id": "existing-uuid", "name": "GDPR Profile v1" },
  "requiresChoice": true
}

POST /profiles/validate

Validates a set of cookies against a compliance group without saving anything. Returns the same errors / warnings structure that POST /profiles uses server-side. The dashboard wizard calls this at step 2 for a server-round-trip confirmation before allowing the operator to proceed.

// POST /consenti/admin/profiles/validate HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Content-Type: application/json

{
  "complianceGroup": "opt-out-strict",   // required — ComplianceGroupId
  "cookies": [                            // required — cookie array from cookie template
    { "id": "necessary", "legalBasis": "mandatory" },
    { "id": "analytics", "legalBasis": "consent" }
  ]
}

GET /compliance-coverage

Returns one entry per compliance group showing which profile is currently active for that group. Used by the dashboard's compliance coverage panel to surface groups that have no active profile.

// GET /consenti/admin/compliance-coverage HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Cookie Templates

Reusable sets of cookie/purpose definitions. A cookie template is attached to a profile in the wizard; the profile inherits the cookie list for compliance validation and consent storage.

GET /cookie-templates & /cookie-templates/:id

[
  {
    "id": "template-uuid",
    "name": "Standard Cookies",
    "tenantId": "default",
    "cookies": [
      { "id": "necessary", "legalBasis": "mandatory", "listenGpc": false, "expiry": 0 },
      { "id": "analytics", "legalBasis": "consent", "listenGpc": true, "expiry": 365 }
    ],
    "createdAt": "2026-06-01T00:00:00.000Z",
    "updatedAt": "2026-07-01T10:00:00.000Z"
  }
]

POST /cookie-templates

New templates start blank — no prefilled cookies. Use the dashboard Load Defaults button to populate a starter set, then customise.

// POST /consenti/admin/cookie-templates HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Content-Type: application/json

{
  "name": "E-commerce Cookies",
  "cookies": [
    { "id": "necessary", "legalBasis": "mandatory", "listenGpc": false, "expiry": 0 },
    { "id": "analytics", "legalBasis": "consent", "listenGpc": true, "expiry": 365 },
    { "id": "marketing", "legalBasis": "consent", "listenGpc": true, "expiry": 180 }
  ]
}

Cookie Template safety guards

Deletion guard: DELETE /cookie-templates/:id returns 422 when active profiles reference the template. Deactivate those profiles first.

{
  "error": "Template is in use by active profiles",
  "activeProfiles": [
    { "id": "profile-uuid", "name": "GDPR Profile", "complianceGroup": "opt-in" }
  ]
}

Cookie removal guard:When updating a template and cookies are removed, the backend runs compliance validation across all profiles using the template. If any profile's compliance group requires a removed cookie, the save is blocked with 422:

{
  "error": "Removing these cookies breaks compliance for affected profiles",
  "blockingProfiles": [
    { "id": "profile-uuid", "name": "GDPR Profile", "complianceGroup": "opt-in" }
  ],
  "removedCookieIds": ["analytics"]
}

GET /cookie-templates/:id/profile-usage

Returns all profiles (ProfileSummary[]) that use this template.

[
  {
    "id": "profile-uuid",
    "name": "GDPR Profile",
    "complianceGroup": "opt-in",
    "isActive": true,
    "version": 3
  }
]

UI Templates

Reusable banner and modal layout settings. A UI template is attached to a profile in the wizard; the profile inherits button arrays, position, and overlay settings.

New templates start blank. Use the dashboard Load Defaults amber callout to populate a sensible starter structure, then customise.

The same safety guards as cookie templates apply: PUT and DELETE check profile usage and block destructive operations when active profiles would be affected. Use GET /ui-templates/:id/profile-usage to preview impact before saving.

{
  "id": "template-uuid",
  "name": "Default Banner",
  "mainBanner": {
    "position": "bottom",
    "overlayOpacity": 0.4,
    "showClose": false,
    "headingTag": "h2",
    "buttons": [
      { "text": "Accept All", "type": "primary", "cookies": "*" },
      { "text": "Reject Optional", "type": "primary", "cookies": "!" },
      { "text": "Customize", "type": "manage" }
    ]
  },
  "preferenceModal": {
    "position": "center",
    "showClose": true,
    "persistent": false,
    "buttons": [
      { "text": "Accept All", "type": "primary", "cookies": "*" },
      { "text": "Save Preferences", "type": "submit" },
      { "text": "Reject Optional", "type": "primary", "cookies": "!" }
    ],
    "categories": []
  }
}

Analytics

GET /analytics/opt-in

Opt-in rate statistics aggregated by locale and date. All query params are optional.

Query paramDescription
tenantIdFilter by tenant (default: all).
profileIdFilter by profile ID.
complianceGroupFilter by compliance group.
fromStart date (ISO 8601, e.g. 2026-01-01).
toEnd date (ISO 8601).
localeFilter by BCP 47 locale.
// GET /consenti/admin/analytics/opt-in?from=2026-01-01&to=2026-07-01 HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Consents

GET /consents

// GET /consenti/admin/consents HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

// Query params (all optional):
// ?page=1&limit=50&profileId=my-profile&from=2026-01-01&to=2026-12-31

GET /consents/:visitorId

// GET /consenti/admin/consents/visitor-uuid HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

GET /consents/:visitorId/history

// GET /consenti/admin/consents/visitor-uuid/history HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Visitors

GET /visitors

IPs are never stored raw — only a SHA-256 hash. The ipHash field is included for audit purposes only.

// GET /consenti/admin/visitors HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

// Query params (all optional):
// ?page=1&limit=50&from=2026-01-01&to=2026-12-31

Users

GET /users

// GET /consenti/admin/users HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

POST /users

The optional allowedTenants array scopes the user to specific tenants — they can only view and manage data for those tenants. An empty array (or omitting the field) grants access to all tenants. Users with role superadmin always have access to all tenants regardless of this field.

// POST /consenti/admin/users HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Content-Type: application/json

{
  "name": "Jane Smith",                           // required
  "email": "[email protected]",                    // required
  "password": "secure-password",                  // required
  "roleId": "role-uuid",                          // optional
  "allowedTenants": ["tenant-uuid-1", "tenant-uuid-2"]  // optional — empty = all tenants
}
ℹ️Tenant scoping enforcement: list routes (profiles, consents, visitors, audit) silently return empty results for out-of-scope tenants. Individual resource routes (GET /consents/:visitorId, etc.) return 403 Forbidden instead.

Roles

GET /roles

// GET /consenti/admin/roles HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

POST /roles

// POST /consenti/admin/roles HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Content-Type: application/json

{
  "name": "editor",               // required
  "description": "Can edit profiles but not manage users"  // optional
}

GET /roles/:id/permissions

// GET /consenti/admin/roles/role-uuid/permissions HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

API Keys

API keys allow server-to-server access to the public API without user credentials. The raw key is only returned on creation — store it immediately.

GET /apikeys

// GET /consenti/admin/apikeys HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

POST /apikeys

// POST /consenti/admin/apikeys HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Content-Type: application/json

{
  "name": "Production Widget"   // required
}

DELETE /apikeys/:id

// DELETE /consenti/admin/apikeys/key-uuid HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Audit Log

GET /audit

// GET /consenti/admin/audit HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

// Query params (all optional):
// ?page=1&limit=50
// &action=profile.created
// &resourceType=profile
// &from=2026-01-01&to=2026-12-31

Stats

GET /stats/overview

// GET /consenti/admin/stats/overview HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

GET /stats/timeline

// GET /consenti/admin/stats/timeline HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

// ?days=30   (default: 30)

GET /stats/categories

// GET /consenti/admin/stats/categories HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

GET /stats/countries & /stats/gpc

// GET /consenti/admin/stats/countries HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

// GET /consenti/admin/stats/gpc HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Export

GET /export/consents

Streams all consent records. Use format=json for JSON or omit for CSV (default). The XLSX endpoint (/export/consents/xlsx) requires the optional xlsx peer dependency.

// GET /consenti/admin/export/consents HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

// Query params (all optional):
// ?format=csv           — "csv" (default) | "json"
// &profileId=my-profile
// &from=2026-01-01
// &to=2026-12-31

GET /export/audit

// GET /consenti/admin/export/audit HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

// ?format=csv&from=2026-01-01&to=2026-12-31

GET /export/translations/:profileId

Exports all translatable string fields for every locale defined on the profile as a CSV file — one row per locale. Useful for bulk-editing translations in a spreadsheet and re-importing them.

// GET /consenti/admin/export/translations/my-profile HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Tenants (Multi-tenant)

ℹ️Tenant management is only active when multiTenant.enabled: true is set in createConsenti(). In single-tenant mode these routes still exist but operate on the implicit "default" tenant.

GET /tenants

// GET /consenti/admin/tenants HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

POST /tenants

// POST /consenti/admin/tenants HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// Content-Type: application/json

{
  "name": "Partner Site",  // required
  "slug": "partner-site"   // required — used in host-based tenant resolution
}

IAB TCF

ℹ️TCF routes are only active when tcf.enabled: true is set in createConsenti().

GET /tcf/vendors

// GET /consenti/admin/tcf/vendors HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

GET /tcf/purposes

// GET /consenti/admin/tcf/purposes HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...