Admin API Routes
Base path: /consenti/admin (default — set via basePath in createConsenti()). All routes require a valid JWT passed as Authorization: Bearer <token>.
POST /consenti/admin/auth/login. Include it in the Authorization header of every subsequent request.| Method | Path | Description |
|---|---|---|
| POST | /auth/login | Authenticate — returns a JWT |
| GET | /auth/me | Get current authenticated user |
| POST | /auth/logout | Invalidate session |
| GET | /profiles | List all profiles |
| GET | /profiles?summary=1 | List profiles as ProfileSummary[] (lightweight, with template names) |
| POST | /profiles | Create a profile — 422 on compliance errors; conflict detection on active group |
| GET | /profiles/:id | Get a profile |
| PUT | /profiles/:id | Update a profile (bumps version) |
| DELETE | /profiles/:id | Delete a profile |
| POST | /profiles/:id/activate | Activate a profile — writes locale JSONs to compliance group directory |
| POST | /profiles/:id/deactivate | Deactivate a profile — removes compliance group locale files |
| GET | /profiles/:id/versions | List version history entries |
| GET | /profiles/:id/versions/:n | Read a specific version locale file |
| POST | /profiles/validate | Validate cookies against a compliance group (no save) |
| GET | /compliance-coverage | Active profile per compliance group |
| GET | /cookie-templates | List cookie templates |
| GET | /cookie-templates/:id | Get a cookie template |
| POST | /cookie-templates | Create a cookie template |
| PUT | /cookie-templates/:id | Update a cookie template |
| DELETE | /cookie-templates/:id | Delete a cookie template — 422 if active profiles use it |
| POST | /cookie-templates/:id/copy | Duplicate a cookie template |
| GET | /cookie-templates/:id/profile-usage | List profiles using this template |
| GET | /ui-templates | List UI templates |
| GET | /ui-templates/:id | Get a UI template |
| POST | /ui-templates | Create a UI template |
| PUT | /ui-templates/:id | Update a UI template |
| DELETE | /ui-templates/:id | Delete a UI template |
| POST | /ui-templates/:id/copy | Duplicate a UI template |
| GET | /ui-templates/:id/profile-usage | List profiles using this template |
| GET | /analytics/opt-in | Opt-in rate stats by locale and date |
| GET | /consents | List consent records (paginated) |
| GET | /consents/:visitorId | Get consent record for a visitor |
| GET | /consents/:visitorId/history | Get consent change history for a visitor |
| GET | /visitors | List visitor records (paginated) |
| GET | /users | List admin users |
| POST | /users | Create an admin user |
| PUT | /users/:id | Update an admin user (including allowedTenants) |
| GET | /roles | List roles |
| POST | /roles | Create a role |
| GET | /roles/:id/permissions | Get permissions assigned to a role |
| POST | /roles/:id/permissions | Assign a permission to a role |
| DELETE | /roles/:id/permissions/:permId | Revoke a permission from a role |
| GET | /permissions | List all available permissions |
| GET | /apikeys | List API keys |
| POST | /apikeys | Create an API key |
| DELETE | /apikeys/:id | Revoke an API key |
| GET | /audit | Get audit log (paginated) |
| GET | /stats/overview | Consent overview statistics |
| GET | /stats/timeline | Daily consent counts |
| GET | /stats/categories | Per-category acceptance rates |
| GET | /stats/countries | Consents by country |
| GET | /stats/gpc | GPC detection statistics |
| GET | /export/consents | Export consent records (CSV or JSON) |
| GET | /export/consents/xlsx | Export consent records as XLSX |
| GET | /export/audit | Export audit log (CSV or JSON) |
| GET | /export/translations/:profileId | Export all translatable fields as CSV |
| GET | /tenants | List tenants (multi-tenant mode) |
| POST | /tenants | Create a tenant |
| PUT | /tenants/:id | Update a tenant |
| DELETE | /tenants/:id | Delete a tenant |
| GET | /tcf/vendors | List IAB TCF vendors |
| GET | /tcf/purposes | List 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 param | Description |
|---|---|
tenantId | Filter by tenant (default: all). |
profileId | Filter by profile ID. |
complianceGroup | Filter by compliance group. |
from | Start date (ISO 8601, e.g. 2026-01-01). |
to | End date (ISO 8601). |
locale | Filter 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-31GET /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-31Users
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
}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-31Stats
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-31GET /export/audit
// GET /consenti/admin/export/audit HTTP/1.1
// Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
// ?format=csv&from=2026-01-01&to=2026-12-31GET /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)
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.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...