Tenant / Admin API Overview
Administrative endpoints for managing a Tenant's settings, license, mail
configuration, App resource catalog, per-principal AI access, and (for managers)
the Tenant lifecycle itself. All routes are prefixed with /api.
These endpoints split into two auth tiers:
- Per-Tenant settings (settings, mail, App settings/resources, license
read/test/sync, AI access) require Superuser on the caller's OWN Tenant
(
permission.tenant.superuser/permission.tenant.write). A small number of reads are public within the Tenant. - Tenant lifecycle (create, delete, search-cluster reassignment, App-database
reassignment, job-history purge) requires the root manager superuser (the
adminuser on themanagerTenant). These operate across Tenants.
PUT /api/settings edits the caller's own Tenant, resolved from credentials.
PUT /api/tenants/{id} resolves an explicit Tenant by id and edits it. Because
the latter opens a route transaction that sets the DB role before the lookup
runs, the Tenant row-level security policy (id = CURRENT_USER) prevents a
manager from resolving a different Tenant through it. Cross-Tenant edits that
must bypass that trap use the dedicated transaction-free routes
(search-cluster, app-database).
GET /api/tenants/{id}
Read a single Tenant's non-sensitive fields.
Authentication: Any authenticated session (resolved through tenant.lookup
under the public scope)
Response:
The Tenant body includes its id, name, the decoded license claims, the
mail config, AI/usage knobs (aiEnabled, aiMonthlyLimit, defaultAiProfileId,
aiSettings), session/security policy, export settings, and a computed
permissions block for the caller.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/tenants/acme"
}
},
"activated": false,
"permissions": {
"edit": true,
"delete": true,
"write": true,
"superuser": true,
"import": true,
"manager": false
},
"aiMonthlyLimit": 0,
"id": "acme",
"name": "acme",
"systemId": "00000000-0000-4000-8000-000000000001",
"license": {
"exp": 1800376947.68,
"iat": 1779287915,
"byok": false,
"type": "enterprise",
"status": "full",
"modules": {
"apps": true,
"salesforce": true
},
"product": "i5",
"features": {
"token": true,
"visuals": true,
"database": true,
"discover": true,
"drilldown": true,
"templates": true,
"dashboards": true,
"datasource": true
},
"issuedTo": "Entrinsik (Informer Testing - Tenant)",
"systemId": "00000000-0000-4000-8000-000000000001",
"developer": false,
"enterprise": true,
"multiTenant": false,
"renewalDate": 1797784947.68,
"integrations": [],
"usageBilling": {
"agent_run": false,
"ai_tokens": false,
"app_compute": false,
"app_storage": false,
"job_compute": false,
"file_storage": false,
"es_index_count": false,
"workspace_storage": false
}
},
"maxConcurrentJobs": 5,
"mail": {
"host": "smtp.example.com",
"pool": true,
"port": 587,
"secure": false,
"authType": "basic",
"requireTLS": true,
"maxMessages": 100,
"maxConnections": 5,
"alwaysUseDefault": false,
"defaultFromAddress": "noreply@example.com"
},
"helpUrl": "https://informer5.zendesk.com/hc/en-us",
"helpEmail": "",
"passwordStrength": "Weak",
"queryDatasetTTL": 60,
"jobHistorySize": 200,
"sanitizeDataHtml": true,
"jobsEnabled": true,
"secureDatasetFields": false,
"logLevelSettings": {},
"loginAttemptsAllowed": 5,
"lockoutTime": 60,
"passwordResetTime": 90,
"queryAuditEnabled": false,
"requestAuditEnabled": false,
"sessionExpireMinutes": 720,
"allowSessionBump": true,
"maxUploadFileSizeMB": 20,
"defaultLetterheadId": null,
"defaultTeamId": null,
"defaultDomainId": null,
"crossTenantAccess": true,
"allowQuerySharing": false,
"aiEnabled": false,
"defaultAiProfileId": "00000000-0000-4000-8000-000000000002",
"aiSettings": {},
"excelExportSettings": {
"locked": {},
"headers": {
"showFilters": true,
"showExportName": true,
"showDescription": true,
"showInputParams": true,
"showGeneratedDate": true,
"showOrganizationName": true
},
"styling": {},
"includeHeaders": false,
"organizationName": null
},
"restrictExportTypes": false,
"allowedExportTypes": [],
"scheduleInJobTimeZone": false,
"cloudEnabled": false,
"cloudUrl": null,
"cloudToken": null,
"jwtPublicKey": null,
"frameAncestors": null,
"searchClusterId": "00000000-0000-4000-8000-000000000003",
"appDatabaseId": null,
"allowTclCommands": false,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
GET /api/tenants
List Tenants as a HAL collection under _embedded["inf:tenant"].
Authentication: Required
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
crossTenantAccessOnly | boolean | Filter to Tenants with the crossTenantAccess flag (only meaningful for a manager view) |
Response:
For a non-manager caller the where clause pins the result to the caller's own
Tenant, so the collection returns exactly one row regardless of the
crossTenantAccessOnly query param. Only a root manager superuser on the
manager Tenant sees the full cross-Tenant list.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/tenants"
}
},
"start": 0,
"count": 1,
"total": 1,
"_embedded": {
"inf:tenant": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/tenants/acme"
}
},
"activated": false,
"permissions": {
"edit": true,
"delete": true,
"write": true,
"superuser": true,
"import": true,
"manager": false
},
"aiMonthlyLimit": 0,
"id": "acme",
"name": "acme",
"systemId": "00000000-0000-4000-8000-000000000001",
"license": {
"exp": 1800376947.68,
"iat": 1779287915,
"byok": false,
"type": "enterprise",
"status": "full",
"modules": {
"apps": true,
"salesforce": true
},
"product": "i5",
"features": {
"token": true,
"visuals": true,
"database": true,
"discover": true,
"drilldown": true,
"templates": true,
"dashboards": true,
"datasource": true
},
"issuedTo": "Entrinsik (Informer Testing - Tenant)",
"systemId": "00000000-0000-4000-8000-000000000001",
"developer": false,
"enterprise": true,
"multiTenant": false,
"renewalDate": 1797784947.68,
"integrations": [],
"usageBilling": {
"agent_run": false,
"ai_tokens": false,
"app_compute": false,
"app_storage": false,
"job_compute": false,
"file_storage": false,
"es_index_count": false,
"workspace_storage": false
}
},
"maxConcurrentJobs": 5,
"mail": {
"host": "smtp.example.com",
"pool": true,
"port": 587,
"secure": false,
"authType": "basic",
"requireTLS": true,
"maxMessages": 100,
"maxConnections": 5,
"alwaysUseDefault": false,
"defaultFromAddress": "noreply@example.com"
},
"helpUrl": "https://informer5.zendesk.com/hc/en-us",
"helpEmail": "",
"passwordStrength": "Weak",
"queryDatasetTTL": 60,
"jobHistorySize": 200,
"sanitizeDataHtml": true,
"jobsEnabled": true,
"secureDatasetFields": false,
"logLevelSettings": {},
"loginAttemptsAllowed": 5,
"lockoutTime": 60,
"passwordResetTime": 90,
"queryAuditEnabled": false,
"requestAuditEnabled": false,
"sessionExpireMinutes": 720,
"allowSessionBump": true,
"maxUploadFileSizeMB": 20,
"defaultLetterheadId": null,
"defaultTeamId": null,
"defaultDomainId": null,
"crossTenantAccess": true,
"allowQuerySharing": false,
"aiEnabled": false,
"defaultAiProfileId": "00000000-0000-4000-8000-000000000002",
"aiSettings": {},
"excelExportSettings": {
"locked": {},
"headers": {
"showFilters": true,
"showExportName": true,
"showDescription": true,
"showInputParams": true,
"showGeneratedDate": true,
"showOrganizationName": true
},
"styling": {},
"includeHeaders": false,
"organizationName": null
},
"restrictExportTypes": false,
"allowedExportTypes": [],
"scheduleInJobTimeZone": false,
"cloudEnabled": false,
"cloudUrl": null,
"cloudToken": null,
"jwtPublicKey": null,
"frameAncestors": null,
"searchClusterId": "00000000-0000-4000-8000-000000000003",
"appDatabaseId": null,
"allowTclCommands": false,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z"
}
]
}
}
POST /api/tenants/_id
Suggest a unique Tenant id from a proposed name.
Authentication: Required
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The proposed Tenant name to slugify |
The route slugifies the name and appends a numeric suffix until the id is unique across all Tenants. The response body is the suggested id string.
Example Request:
{ "name": "New Corporation" }
{
"name": "New Corporation"
}
"new-corporation"
GET /api/settings
Read the caller's own Tenant settings.
Authentication: Required (the route is open: true)
Response:
The broad settings object (letterhead, export options, cloud, AI/usage knobs,
security policy, and more). The route eager-loads defaultTeam, defaultDomain,
defaultLetterhead, and mailIntegration, and projects a derived byok boolean
from the signed license.
The AI/usage-related fields are:
| Field | Type | Description |
|---|---|---|
aiEnabled | boolean | Master switch for AI features on the Tenant |
defaultAiProfileId | UUID | null | The usage profile applied to users with no team-assigned profile |
aiMonthlyLimit | number | Tenant-wide monthly credit ceiling |
aiSettings | object | Per-feature AI configuration/instructions |
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings"
}
},
"activated": false,
"permissions": {
"edit": true,
"delete": true,
"write": true,
"superuser": true,
"import": true,
"manager": false
},
"aiMonthlyLimit": 0,
"id": "acme",
"name": "acme",
"systemId": "00000000-0000-4000-8000-000000000001",
"license": {
"exp": 1800376947.68,
"iat": 1779287915,
"byok": false,
"type": "enterprise",
"status": "full",
"modules": {
"apps": true,
"salesforce": true
},
"product": "i5",
"features": {
"token": true,
"visuals": true,
"database": true,
"discover": true,
"drilldown": true,
"templates": true,
"dashboards": true,
"datasource": true
},
"issuedTo": "Entrinsik (Informer Testing - Tenant)",
"systemId": "00000000-0000-4000-8000-000000000001",
"developer": false,
"enterprise": true,
"multiTenant": false,
"renewalDate": 1797784947.68,
"integrations": [],
"usageBilling": {
"agent_run": false,
"ai_tokens": false,
"app_compute": false,
"app_storage": false,
"job_compute": false,
"file_storage": false,
"es_index_count": false,
"workspace_storage": false
}
},
"maxConcurrentJobs": 5,
"mail": {
"host": "smtp.example.com",
"pool": true,
"port": 587,
"secure": false,
"authType": "basic",
"requireTLS": true,
"maxMessages": 100,
"maxConnections": 5,
"alwaysUseDefault": false,
"defaultFromAddress": "noreply@example.com"
},
"helpUrl": "https://informer5.zendesk.com/hc/en-us",
"helpEmail": "",
"passwordStrength": "Weak",
"queryDatasetTTL": 60,
"jobHistorySize": 200,
"sanitizeDataHtml": true,
"jobsEnabled": true,
"secureDatasetFields": false,
"logLevelSettings": {},
"loginAttemptsAllowed": 5,
"lockoutTime": 60,
"passwordResetTime": 90,
"queryAuditEnabled": false,
"requestAuditEnabled": false,
"sessionExpireMinutes": 720,
"allowSessionBump": true,
"maxUploadFileSizeMB": 20,
"defaultLetterheadId": null,
"defaultTeamId": null,
"defaultDomainId": null,
"crossTenantAccess": true,
"allowQuerySharing": false,
"aiEnabled": false,
"defaultAiProfileId": "00000000-0000-4000-8000-000000000002",
"aiSettings": {},
"excelExportSettings": {
"locked": {},
"headers": {
"showFilters": true,
"showExportName": true,
"showDescription": true,
"showInputParams": true,
"showGeneratedDate": true,
"showOrganizationName": true
},
"styling": {},
"includeHeaders": false,
"organizationName": null
},
"restrictExportTypes": false,
"allowedExportTypes": [],
"scheduleInJobTimeZone": false,
"cloudEnabled": false,
"cloudUrl": null,
"cloudToken": null,
"jwtPublicKey": null,
"frameAncestors": null,
"searchClusterId": "00000000-0000-4000-8000-000000000003",
"appDatabaseId": null,
"allowTclCommands": false,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"defaultTeam": null,
"defaultDomain": null,
"defaultLetterhead": null,
"mailIntegration": null,
"byok": false
}
PUT /api/settings
Update the caller's own Tenant settings.
Authentication: Superuser for any field; billing managers (billing.manage)
may set only aiEnabled, aiSettings, aiMonthlyLimit, and defaultAiProfileId
Request Body:
The payload is stripUnknown, so only whitelisted fields persist. Notably
searchClusterId is not one of them: use PUT /api/tenants/{id}/search-cluster
to reassign a search cluster.
The AI/usage fields a billing manager may set:
| Field | Type | Description |
|---|---|---|
aiEnabled | boolean | Enable/disable AI for the Tenant |
defaultAiProfileId | UUID | null | Set or clear the Tenant default usage profile |
aiMonthlyLimit | number (≥ 0) | Tenant-wide monthly credit ceiling |
aiSettings | object | Per-feature AI configuration |
Example Request:
{ "sessionExpireMinutes": 120, "allowSessionBump": true }
Response:
Responds 200 with the updated Tenant.
{
"sessionExpireMinutes": 120,
"allowSessionBump": true
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings"
}
},
"activated": false,
"permissions": {
"edit": true,
"delete": true,
"write": true,
"superuser": true,
"import": true,
"manager": false
},
"aiMonthlyLimit": 0,
"id": "acme",
"name": "acme",
"systemId": "00000000-0000-4000-8000-000000000001",
"license": {
"byok": false,
"modules": {
"salesforce": true,
"apps": true
},
"features": {
"token": true,
"discover": true,
"dashboards": true,
"visuals": true,
"drilldown": true,
"templates": true,
"datasource": true,
"database": true
},
"developer": false,
"enterprise": true,
"multiTenant": false,
"integrations": [],
"usageBilling": {
"agent_run": false,
"ai_tokens": false,
"app_compute": false,
"job_compute": false,
"app_storage": false,
"es_index_count": false,
"file_storage": false,
"workspace_storage": false
},
"exp": 1800376947.68,
"renewalDate": 1797784947.68,
"systemId": "00000000-0000-4000-8000-000000000001",
"status": "full",
"type": "enterprise",
"product": "i5",
"issuedTo": "Entrinsik (Informer Testing - Tenant)",
"iat": 1779287915
},
"maxConcurrentJobs": 5,
"mail": {
"host": "smtp.example.com",
"pool": true,
"port": 587,
"secure": false,
"authType": "basic",
"requireTLS": true,
"maxMessages": 100,
"maxConnections": 5,
"alwaysUseDefault": false,
"defaultFromAddress": "noreply@example.com"
},
"helpUrl": "https://informer5.zendesk.com/hc/en-us",
"helpEmail": "",
"passwordStrength": "Weak",
"queryDatasetTTL": 60,
"jobHistorySize": 200,
"sanitizeDataHtml": true,
"jobsEnabled": true,
"secureDatasetFields": false,
"logLevelSettings": {},
"loginAttemptsAllowed": 5,
"lockoutTime": 60,
"passwordResetTime": 90,
"queryAuditEnabled": false,
"requestAuditEnabled": false,
"sessionExpireMinutes": 120,
"allowSessionBump": true,
"maxUploadFileSizeMB": 20,
"defaultLetterheadId": null,
"defaultTeamId": null,
"defaultDomainId": null,
"crossTenantAccess": true,
"allowQuerySharing": false,
"aiEnabled": false,
"defaultAiProfileId": "00000000-0000-4000-8000-000000000002",
"aiSettings": {},
"excelExportSettings": {
"locked": {},
"headers": {
"showFilters": true,
"showExportName": true,
"showDescription": true,
"showInputParams": true,
"showGeneratedDate": true,
"showOrganizationName": true
},
"styling": {},
"includeHeaders": false,
"organizationName": null
},
"restrictExportTypes": false,
"allowedExportTypes": [],
"scheduleInJobTimeZone": false,
"cloudEnabled": false,
"cloudUrl": null,
"cloudToken": null,
"jwtPublicKey": null,
"frameAncestors": null,
"searchClusterId": "00000000-0000-4000-8000-000000000003",
"appDatabaseId": null,
"allowTclCommands": false,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"mailIntegration": null
}
GET /api/settings/default-domain
Read just the Tenant's default domain id.
Authentication: Required (the route is open: true, tenant auth strategy)
Response:
Returns the defaultDomainId as { id } (which may be null).
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/default-domain"
}
},
"id": null
}
GET /api/settings/mail
Read the Tenant's SMTP configuration (the mail JSON column).
Authentication: Required
Mail settings live at /api/settings/mail, not /api/mail-settings.
Response:
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/mail"
}
},
"host": "smtp.example.com",
"pool": true,
"port": 587,
"secure": false,
"authType": "basic",
"requireTLS": true,
"maxMessages": 100,
"maxConnections": 5,
"alwaysUseDefault": false,
"defaultFromAddress": "noreply@example.com"
}
PUT /api/settings/mail
Update the Tenant's SMTP configuration.
Authentication: permission.tenant.superuser
Request Body:
The payload is stripUnknown and applies Joi defaults. authType must be one of
the registered mail-auth driver ids.
| Field | Type | Description |
|---|---|---|
authType | string | One of basic, oauth, gmail, microsoft-mail, none (default none). basic is SMTP username/password auth |
host | string | SMTP host (default localhost) |
port | integer | SMTP port (default 587) |
secure | boolean | Use TLS on connect (default false) |
ignoreTLS | boolean | Ignore TLS (default false) |
requireTLS | boolean | Require TLS (default false) |
pool | boolean | Use connection pooling (default true) |
maxConnections | integer | Max connections (default 5, min 1) |
maxMessages | integer | Max messages per connection (default 100, min 1) |
rateLimit | integer | Messages per rateDelta (optional) |
rateDelta | integer | Rate-limit window in ms (default 1000, min 1) |
defaultFromAddress | string | Default from address |
alwaysUseDefault | boolean | Always use the default from address (default false) |
auth | object | Driver-specific authentication credentials |
Example Request:
{
"authType": "basic",
"host": "smtp.example.com",
"port": 587,
"secure": false,
"requireTLS": true,
"defaultFromAddress": "noreply@example.com"
}
Response:
Responds 200 with the saved mail object.
{
"authType": "basic",
"host": "smtp.example.com",
"port": 587,
"secure": false,
"requireTLS": true,
"defaultFromAddress": "noreply@example.com"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/mail"
}
},
"authType": "basic",
"host": "smtp.example.com",
"port": 587,
"secure": false,
"requireTLS": true,
"defaultFromAddress": "noreply@example.com",
"pool": true,
"ignoreTLS": false,
"alwaysUseDefault": false,
"maxConnections": 5,
"maxMessages": 100,
"rateDelta": 1000
}
DELETE /api/settings/mail
Clear the Tenant's mail configuration.
Authentication: permission.tenant.superuser
Response:
Resets mail to {} and returns the cleared object. Like all Informer DELETE
routes it answers 200 (never 204), and it swallows errors (always 200).
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/mail"
}
}
}
GET /api/settings/apps
Read the Tenant's App CDN/CSP configuration.
Authentication: Required
Response:
The App settings (cdns, csp, customOrigins) merged with defaults, plus a
_defaults block the UI uses for its reset affordance. New Tenants default to
the tightest CSP mode (approved-only).
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps"
}
},
"cdns": {
"d3": {
"enabled": true
},
"cdnjs": {
"enabled": true
},
"unpkg": {
"enabled": false
},
"google": {
"enabled": false
},
"plotly": {
"enabled": true
},
"jsdelivr": {
"enabled": true
}
},
"csp": {
"mode": "approved-only",
"requireSri": false
},
"customOrigins": [],
"_defaults": {
"cdns": {
"jsdelivr": {
"enabled": true
},
"cdnjs": {
"enabled": true
},
"unpkg": {
"enabled": false
},
"google": {
"enabled": false
},
"d3": {
"enabled": true
},
"plotly": {
"enabled": true
}
},
"csp": {
"mode": "approved-only",
"requireSri": false
},
"customOrigins": []
}
}
PUT /api/settings/apps
Update the Tenant's App settings.
Authentication: permission.tenant.superuser
Request Body:
cdns and csp are deep-merged (partial update). customOrigins is
replaced wholesale.
Example Request:
{ "csp": { "mode": "any-enabled-cdn" } }
Response:
Responds 200 with the merged settings (no _defaults block).
{
"csp": {
"mode": "any-enabled-cdn"
}
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps"
}
},
"cdns": {
"d3": {
"enabled": true
},
"cdnjs": {
"enabled": true
},
"unpkg": {
"enabled": false
},
"google": {
"enabled": false
},
"plotly": {
"enabled": true
},
"jsdelivr": {
"enabled": true
}
},
"csp": {
"mode": "any-enabled-cdn",
"requireSri": false
},
"customOrigins": []
}
GET /api/settings/apps/resources
List the curated CDN library catalog for Apps.
Authentication: Required
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
enabled | boolean | Filter by enabled state |
featured | boolean | Filter by featured flag |
category | string | Filter by category (e.g. visualization, ui, data, utility, maps) |
Response:
A HAL collection under _embedded["inf:app-resource"], sorted featured-first,
then by name, then version. Each row carries its resources (script/style/asset
URLs with SRI hashes) and a default set of common libraries ships pre-seeded
(Chart.js, D3.js, Highcharts, and others).
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps/resources"
},
"inf:app-resource": {
"href": "https://informer.example.com/api/settings/apps/resources/"
},
"inf:refresh-sri": {
"href": "https://informer.example.com/api/settings/apps/resources//_refresh-sri"
}
},
"_embedded": {
"inf:app-resource": [
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000001"
},
"inf:refresh-sri": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000001/_refresh-sri"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "AG Grid Community",
"version": "34.3.1",
"slug": "ag-grid-community",
"description": "The Best JavaScript Data Table for Building Enterprise Applications",
"category": "data",
"aiHint": "Use for large datasets with sorting, filtering, grouping, aggregation, and virtual scrolling. Best enterprise-grade data grid for performance and features.",
"resources": {
"assets": [],
"styles": [
{
"url": "https://cdn.jsdelivr.net/npm/ag-grid-community@34.3.1/styles/ag-grid.min.css",
"integrity": "sha384-13U4UwTfomIc71W7P5GUq+AJx9emviNMsM0bNEFXvta0eHOzShlVom4hueraQHct"
},
{
"url": "https://cdn.jsdelivr.net/npm/ag-grid-community@34.3.1/styles/ag-theme-quartz.min.css",
"integrity": "sha384-IvPmV7O3nZI9BmOHg6IO6h52homvd9dy6+olMn01hI29QRK+LZonwq3+LMToIyMg"
}
],
"scripts": [
{
"url": "https://cdn.jsdelivr.net/npm/ag-grid-community@34.3.1/dist/ag-grid-community.min.js",
"module": false,
"integrity": "sha384-1m5FM32cqWvkVPC2kdiP5dLsQUO3OCeEAGgZ4RqzCWlsWcCVIM9HXAjD1A99GPs3"
}
]
},
"licenseKey": null,
"enabled": true,
"featured": true,
"capturedAt": "2026-01-15T16:20:00.000Z",
"capturedBy": "admin",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000002"
},
"inf:refresh-sri": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000002/_refresh-sri"
}
},
"id": "00000000-0000-4000-8000-000000000002",
"name": "Animate.css",
"version": "4.1.1",
"slug": "animate-css",
"description": "A cross-browser library of CSS animations",
"category": "ui",
"aiHint": "Use for ready-to-use CSS animations. Add class \"animate__animated\" plus animation class like \"animate__bounce\", \"animate__fadeIn\", \"animate__slideInUp\", etc.",
"resources": {
"assets": [],
"styles": [
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css",
"integrity": "sha384-Gu3KVV2H9d+yA4QDpVB7VcOyhJlAVrcXd0thEjr4KznfaFPLe0xQJyonVxONa4ZC"
}
],
"scripts": []
},
"licenseKey": null,
"enabled": true,
"featured": true,
"capturedAt": "2026-01-15T16:20:00.000Z",
"capturedBy": "admin",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
},
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000003"
},
"inf:refresh-sri": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000003/_refresh-sri"
}
},
"id": "00000000-0000-4000-8000-000000000003",
"name": "AOS",
"version": "2.3.4",
"slug": "aos",
"description": "Animate On Scroll library for scroll-triggered animations",
"category": "ui",
"aiHint": "Use for scroll-triggered animations. Add data-aos=\"fade-up\" (or other animation) to elements. Initialize with AOS.init(). Options: duration, easing, delay, offset.",
"resources": {
"assets": [],
"styles": [
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.css",
"integrity": "sha384-/rJKQnzOkEo+daG0jMjU1IwwY9unxt1NBw3Ef2fmOJ3PW/TfAg2KXVoWwMZQZtw9"
}
],
"scripts": [
{
"url": "https://cdnjs.cloudflare.com/ajax/libs/aos/2.3.4/aos.js",
"module": false,
"integrity": "sha384-n1AULnKdMJlK1oQCLNDL9qZsDgXtH6jRYFCpBtWFc+a9Yve0KSoMn575rk755NJZ"
}
]
},
"licenseKey": null,
"enabled": true,
"featured": true,
"capturedAt": "2026-01-15T16:20:00.000Z",
"capturedBy": "admin",
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
},
"… 26 more items (29 total) — omitted from docs"
]
}
}
POST /api/settings/apps/resources
Add an App resource to the catalog.
Authentication: permission.tenant.superuser
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name |
version | string | Yes | Library version |
slug | string | No | URL-safe id; auto-derived from name when omitted |
description | string | No | Description |
category | string | No | Catalog category |
aiHint | string | No | Guidance the AI uses when choosing/using the library |
resources | object | No | { scripts, styles, assets } URL lists |
licenseKey | string | No | License key for commercial libraries |
enabled | boolean | No | Whether the resource is selectable |
featured | boolean | No | Whether the resource is featured |
The catalog enforces a unique (slug, version) per Tenant. Each resources
entry must pin a version in its URL and carry exactly one of integrity (an
SRI hash) or skipIntegrity: true. The route enforces SRI so a CDN swap cannot
silently inject new bytes. When resources are supplied, capturedBy is set to
the caller and capturedAt is stamped.
Example Request:
{
"name": "Acme Charts",
"version": "2.1.0",
"category": "visualization",
"aiHint": "Example custom charting library, registered in the App resource catalog so Apps can load it.",
"resources": {
"scripts": [{
"url": "https://cdn.example.com/acme-charts@2.1.0/dist/acme-charts.min.js",
"integrity": "sha384-exampleSRIhashReplaceWithTheRealHashFromYourCDNorBuildTool"
}],
"styles": [],
"assets": []
}
}
Response:
Responds 200 (not 201) with the created resource.
{
"name": "Acme Charts",
"version": "2.1.0",
"category": "visualization",
"aiHint": "Example custom charting library, registered in the App resource catalog so Apps can load it.",
"resources": {
"scripts": [
{
"url": "https://cdn.example.com/acme-charts@2.1.0/dist/acme-charts.min.js",
"integrity": "sha384-exampleSRIhashReplaceWithTheRealHashFromYourCDNorBuildTool"
}
],
"styles": [],
"assets": []
}
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps/resources"
},
"inf:app-resources": {
"href": "https://informer.example.com/api/settings/apps/resources"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"tenant": "acme",
"name": "Acme Charts",
"version": "2.1.0",
"slug": "acme-charts",
"description": null,
"category": "visualization",
"aiHint": "Example custom charting library, registered in the App resource catalog so Apps can load it.",
"resources": {
"assets": [],
"styles": [],
"scripts": [
{
"url": "https://cdn.example.com/acme-charts@2.1.0/dist/acme-charts.min.js",
"module": false,
"integrity": "sha384-exampleSRIhashReplaceWithTheRealHashFromYourCDNorBuildTool"
}
]
},
"licenseKey": null,
"enabled": true,
"featured": false,
"capturedAt": "2026-01-15T16:20:00.000Z",
"capturedBy": "admin",
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z"
}
GET /api/settings/apps/resources/{id}
Read a single App resource.
Authentication: Required
Pre-blocks: appResource.lookup (404 if missing)
Response:
The catalog row with its resource URLs and SRI hashes.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000001"
},
"inf:app-resources": {
"href": "https://informer.example.com/api/settings/apps/resources/resources"
},
"inf:refresh-sri": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000001/_refresh-sri"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Chart.js",
"version": "4.4.1",
"slug": "chartjs",
"description": "Simple yet flexible JavaScript charting library.",
"category": "visualization",
"aiHint": "Use for bar, line, and pie charts in App widgets.",
"resources": {
"assets": [],
"styles": [],
"scripts": [
{
"url": "https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"
}
]
},
"licenseKey": null,
"enabled": true,
"featured": true,
"capturedAt": "2026-01-15T16:20:00.000Z",
"capturedBy": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
PUT /api/settings/apps/resources/{id}
Update an App resource.
Authentication: permission.tenant.superuser
Request Body:
Any subset of the resource fields (see POST). When resources change,
capturedAt/capturedBy are re-stamped.
Example Request:
{ "description": "Simple yet flexible JavaScript charting (updated).", "featured": false }
Response:
Responds 200 with the updated resource.
{
"description": "Simple yet flexible JavaScript charting (updated).",
"featured": false
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000001"
},
"inf:app-resources": {
"href": "https://informer.example.com/api/settings/apps/resources/resources"
}
},
"id": "00000000-0000-4000-8000-000000000001",
"name": "Chart.js",
"version": "4.4.1",
"slug": "chartjs",
"description": "Simple yet flexible JavaScript charting (updated).",
"category": "visualization",
"aiHint": "Use for bar, line, and pie charts in App widgets.",
"resources": {
"assets": [],
"styles": [],
"scripts": [
{
"url": "https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"
}
]
},
"licenseKey": null,
"enabled": true,
"featured": false,
"capturedAt": "2026-01-15T16:20:00.000Z",
"capturedBy": null,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"tenant": "acme"
}
DELETE /api/settings/apps/resources/{id}
Delete an App resource.
Authentication: permission.tenant.superuser
Response:
Removes the catalog row and responds 200 with { success: true } (not 204).
{
"_links": {
"self": {
"href": "https://informer.example.com/api/settings/apps/resources/00000000-0000-4000-8000-000000000001"
}
},
"success": true
}
GET /api/license
Read the current license.
Authentication: permission.tenant.superuser
Response:
Returns the locally-decoded license claims plus a licenseManager connectivity
block. When the License Manager is unreachable (offline installs) the handler
swallows the error and falls back to just the decoded license, still 200.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/license"
}
},
"byok": false,
"modules": {
"salesforce": true,
"apps": true
},
"features": {
"token": true,
"discover": true,
"dashboards": true,
"visuals": true,
"drilldown": true,
"templates": true,
"datasource": true,
"database": true
},
"developer": false,
"enterprise": true,
"multiTenant": false,
"integrations": [],
"usageBilling": {
"agent_run": false,
"ai_tokens": false,
"app_compute": false,
"job_compute": false,
"app_storage": false,
"es_index_count": false,
"file_storage": false,
"workspace_storage": false
},
"exp": 1800376947.68,
"renewalDate": 1797784947.68,
"systemId": "00000000-0000-4000-8000-000000000001",
"status": "full",
"type": "enterprise",
"product": "i5",
"issuedTo": "Entrinsik (Informer Testing - Tenant)",
"iat": 1779287915,
"activated": false
}
POST /api/license/_read
Decode a license token without installing it.
Authentication: permission.tenant.superuser
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
token | string | One of token / upload | The license JWT to verify and decode |
upload | string | One of token / upload | An uploaded license-file id |
Verifies the JWT against Entrinsik's public key and returns the decoded claims so
the admin UI can preview a token before applying it. Answers 400 when the token
is invalid.
Example Request:
{ "token": "eyJhbGciOiJIUzI1NiJ9.example-token.signature" }
{
"token": "eyJhbGciOiJIUzI1NiJ9.example-token-1.signature"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/license/_read"
}
},
"byok": false,
"modules": {
"salesforce": true,
"apps": true
},
"features": {
"token": true,
"discover": true,
"dashboards": true,
"visuals": true,
"drilldown": true,
"templates": true,
"datasource": true,
"database": true
},
"developer": false,
"enterprise": true,
"multiTenant": false,
"integrations": [],
"usageBilling": {
"agent_run": false,
"ai_tokens": false,
"app_compute": false,
"job_compute": false,
"app_storage": false,
"es_index_count": false,
"file_storage": false,
"workspace_storage": false
},
"exp": 1800376947.68,
"renewalDate": 1797784947.68,
"systemId": "00000000-0000-4000-8000-000000000001",
"status": "full",
"type": "enterprise",
"product": "i5",
"issuedTo": "Entrinsik (Informer Testing - Tenant)",
"iat": 1779287915
}
POST /api/license/_test
Validate a license token (or uploaded file).
Authentication: permission.tenant.superuser
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
token | string | One of token / upload | The license token to validate |
upload | string | One of token / upload | An uploaded license-file id |
Validates without throwing the 423 a normal license check would. Used by the
install UI to validate before committing.
Response:
Responds 200 with an empty body when the token is valid; 422 when it is not.
{
"token": "eyJhbGciOiJIUzI1NiJ9.example-token-1.signature"
}
POST /api/license/_sync
Sync the Tenant's license with the License Manager.
Authentication: permission.tenant.superuser
Payload: empty
Round-trips to the License Manager to refresh the Tenant's license, activation
token, and AI entitlement cache. On an offline install the call short-circuits
with a 503 the handler swallows, so the route still answers 200 (empty)
without changing anything.
{}
GET /api/ai-access
List per-principal AI access rows.
Authentication: permission.tenant.superuser
Response:
A HAL collection of the per-principal AI access rows (principalId +
monthlyCostLimit).
{
"_links": {
"self": {
"href": "https://informer.example.com/api/ai-access"
}
},
"items": [],
"start": 0,
"count": 0,
"total": 0
}
PUT /api/ai-access/_upsert
Grant or update per-principal AI access in bulk.
Authentication: permission.tenant.superuser
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
principals | string[] | Yes | Principal ids (a single value is coerced to an array) |
monthlyCostLimit | number | Yes | The monthly credit limit applied to every listed principal |
Bulk-upserts AI access for the given principals, each with the same
monthlyCostLimit. Note this is PUT, not POST.
Example Request:
{ "principals": ["admin"], "monthlyCostLimit": 25 }
Response:
Responds 200 with an empty body.
{
"principals": [
"admin"
],
"monthlyCostLimit": 25
}
PUT /api/ai-access/_revoke
Revoke per-principal AI access in bulk.
Authentication: permission.tenant.superuser
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
principals | string[] | Yes | Principal ids whose AI access rows to delete |
Deletes the AI access rows for the given principals. A no-op for principals with no row.
Example Request:
{ "principals": ["admin"] }
Response:
Responds 200 with an empty body.
{
"principals": [
"admin"
]
}
GET /api/password-settings
Read the Tenant's configured minimum password strength.
Authentication: None (the route is auth: false)
Response:
Returns { passwordStrength }. This is a read of the configured policy, not a
password scorer.
{
"_links": {
"self": {
"href": "https://informer.example.com/api/password-settings"
}
},
"passwordStrength": "Weak"
}
POST /api/tenants
Create a Tenant.
Authentication: permission.tenants.create (Superuser plus a multiTenant
license)
Pre-blocks: permission.tenants.create, tenant.provision
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tenant display name |
licenseToken | string | Yes | License token for the new Tenant |
id | string | No | Tenant id; slugified from name when omitted |
adminPassword | string | No | Initial admin password (default 123) |
adminEmail | string | No | Initial admin email |
progress | string | No | Progress-tracking id |
copyFromId | string | No | Copy content from an existing Tenant |
bundleId | string | No | Deploy from a bundle template |
jwtPublicKey | string | No | JWT public key for authentication |
activate | boolean | No | Auto-activate the license (root manager superuser only; default false) |
searchClusterId | string | No | Elasticsearch cluster id |
appDatabaseId | string (UUID) | null | No | Manager-curated App Database profile that hosts the Tenant's App workspace data. null (default) uses the deployment-level default; change it later via PUT /api/tenants/{id}/app-database |
tenant.provision creates the Tenant, an admin user, and the seed copy.
activate: true auto-activates only for a root manager superuser.
Example Request:
{
"id": "docs-newcorp",
"name": "New Corporation",
"licenseToken": "license-token-xyz"
}
Response:
Responds 201 Created with the new Tenant and a Location header.
{
"id": "docs-newcorp",
"name": "New Corporation",
"licenseToken": "eyJhbGciOiJIUzI1NiJ9.example-token-1.signature"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/tenants/docs-newcorp"
}
},
"activated": false,
"permissions": {
"edit": true,
"delete": true,
"write": true,
"superuser": true,
"import": true,
"manager": true
},
"aiMonthlyLimit": 0,
"maxConcurrentJobs": 5,
"mail": {},
"helpUrl": "https://informer5.zendesk.com/hc/en-us",
"helpEmail": "",
"passwordStrength": "Weak",
"queryDatasetTTL": 60,
"jobHistorySize": 200,
"sanitizeDataHtml": true,
"jobsEnabled": true,
"secureDatasetFields": true,
"logLevelSettings": {},
"queryAuditEnabled": true,
"crossTenantAccess": true,
"aiEnabled": false,
"aiSettings": {},
"excelExportSettings": {
"locked": {},
"headers": {
"showFilters": true,
"showExportName": true,
"showDescription": true,
"showInputParams": true,
"showGeneratedDate": true,
"showOrganizationName": true
},
"styling": {},
"includeHeaders": false,
"organizationName": null
},
"restrictExportTypes": false,
"allowedExportTypes": [],
"scheduleInJobTimeZone": false,
"cloudEnabled": false,
"allowTclCommands": false,
"id": "docs-newcorp",
"name": "New Corporation",
"searchClusterId": "00000000-0000-4000-8000-000000000001",
"appDatabaseId": null,
"systemId": "00000000-0000-4000-8000-000000000002",
"jwtPublicKey": null,
"updatedAt": "2026-01-15T16:20:00.000Z",
"createdAt": "2026-01-15T16:20:00.000Z",
"license": {
"exp": 1800376947.68,
"iat": 1779287915,
"byok": false,
"type": "enterprise",
"status": "full",
"modules": {
"apps": true,
"salesforce": true
},
"product": "i5",
"features": {
"token": true,
"visuals": true,
"database": true,
"discover": true,
"drilldown": true,
"templates": true,
"dashboards": true,
"datasource": true
},
"issuedTo": "Entrinsik (Informer Testing - Tenant)",
"systemId": "00000000-0000-4000-8000-000000000002",
"developer": false,
"enterprise": true,
"multiTenant": false,
"renewalDate": 1797784947.68,
"integrations": [],
"usageBilling": {
"agent_run": false,
"ai_tokens": false,
"app_compute": false,
"app_storage": false,
"job_compute": false,
"file_storage": false,
"es_index_count": false,
"workspace_storage": false
}
},
"defaultTeamId": null,
"defaultDomainId": null,
"loginAttemptsAllowed": 5,
"lockoutTime": 60,
"passwordResetTime": 90,
"requestAuditEnabled": null,
"sessionExpireMinutes": 720,
"allowSessionBump": true,
"maxUploadFileSizeMB": 20,
"defaultLetterheadId": null,
"allowQuerySharing": false,
"cloudUrl": null,
"cloudToken": null,
"aiAllUserAccess": false,
"frameAncestors": null,
"defaultAiProfileId": null
}
PUT /api/tenants/{id}
Update a Tenant resolved by id.
Authentication: Superuser
Pre-blocks: tenant.lookup(params.id), permission.tenant.write(pre.tenant)
Request Body:
Tenant configuration fields (the writable subset).
Example Request:
{ "name": "Acme Analytics" }
Response:
Responds 200 with the updated Tenant. The example below is captured as a Tenant
editing itself.
{
"name": "Acme Analytics"
}
{
"_links": {
"self": {
"href": "https://informer.example.com/api/tenants/acme"
}
},
"activated": false,
"permissions": {
"edit": true,
"delete": true,
"write": true,
"superuser": true,
"import": true,
"manager": false
},
"aiMonthlyLimit": 0,
"id": "acme",
"name": "Acme Analytics",
"systemId": "00000000-0000-4000-8000-000000000001",
"license": {
"byok": false,
"modules": {
"salesforce": true,
"apps": true
},
"features": {
"token": true,
"discover": true,
"dashboards": true,
"visuals": true,
"drilldown": true,
"templates": true,
"datasource": true,
"database": true
},
"developer": false,
"enterprise": true,
"multiTenant": false,
"integrations": [],
"usageBilling": {
"agent_run": false,
"ai_tokens": false,
"app_compute": false,
"job_compute": false,
"app_storage": false,
"es_index_count": false,
"file_storage": false,
"workspace_storage": false
},
"exp": 1800376947.68,
"renewalDate": 1797784947.68,
"systemId": "00000000-0000-4000-8000-000000000001",
"status": "full",
"type": "enterprise",
"product": "i5",
"issuedTo": "Entrinsik (Informer Testing - Tenant)",
"iat": 1779287915
},
"maxConcurrentJobs": 5,
"mail": {},
"helpUrl": "https://informer5.zendesk.com/hc/en-us",
"helpEmail": "",
"passwordStrength": "Weak",
"queryDatasetTTL": 60,
"jobHistorySize": 200,
"sanitizeDataHtml": true,
"jobsEnabled": true,
"secureDatasetFields": false,
"logLevelSettings": {},
"loginAttemptsAllowed": 5,
"lockoutTime": 60,
"passwordResetTime": 90,
"queryAuditEnabled": false,
"requestAuditEnabled": false,
"sessionExpireMinutes": 120,
"allowSessionBump": true,
"maxUploadFileSizeMB": 20,
"defaultLetterheadId": null,
"defaultTeamId": null,
"defaultDomainId": null,
"crossTenantAccess": true,
"allowQuerySharing": false,
"aiEnabled": false,
"defaultAiProfileId": "00000000-0000-4000-8000-000000000002",
"aiSettings": {},
"excelExportSettings": {
"locked": {},
"headers": {
"showFilters": true,
"showExportName": true,
"showDescription": true,
"showInputParams": true,
"showGeneratedDate": true,
"showOrganizationName": true
},
"styling": {},
"includeHeaders": false,
"organizationName": null
},
"restrictExportTypes": false,
"allowedExportTypes": [],
"scheduleInJobTimeZone": false,
"cloudEnabled": false,
"cloudUrl": null,
"cloudToken": null,
"jwtPublicKey": null,
"frameAncestors": null,
"searchClusterId": "00000000-0000-4000-8000-000000000003",
"appDatabaseId": null,
"allowTclCommands": false,
"createdAt": "2026-01-15T16:20:00.000Z",
"updatedAt": "2026-01-15T16:20:00.000Z",
"mailIntegration": null
}
This route opens a route transaction that sets the DB role before the pre-blocks
run. Under the Tenant row-level security policy (id = CURRENT_USER), a manager
cannot resolve a different Tenant through this route (the lookup 404s). Use
the transaction-free search-cluster and app-database routes for cross-Tenant
edits.
PUT /api/tenants/{id}/search-cluster
Reassign a Tenant's Elasticsearch search cluster.
Authentication: Root manager superuser
The cluster assignment is PUT /api/tenants/{id}/search-cluster, not
GET/PUT /api/search-cluster.
Request Body:
| Field | Type | Description |
|---|---|---|
searchClusterId | string | The search cluster to assign (payload is stripUnknown) |
A single transaction-free Tenant.update.
Example Request:
{ "searchClusterId": "00000000-0000-4000-8000-000000000001" }
Response:
Responds 200 with an empty body.
{
"searchClusterId": "00000000-0000-4000-8000-000000000001"
}
PUT /api/tenants/{id}/app-database
Reassign a Tenant's App database.
Authentication: Root manager superuser
Request Body:
| Field | Type | Description |
|---|---|---|
appDatabaseId | string (UUID) | null | The App Database profile to assign; null unassigns and falls back to the deployment default |
Existing App data is not migrated. The route runs without a transaction so
the tenant.lookup is not trapped under RLS.
Example Request:
{ "appDatabaseId": null }
Response:
Responds 200 with a null body.
{
"appDatabaseId": null
}
DELETE /api/tenants/{id}/job-history
Purge every job-history row for a Tenant.
Authentication: Superuser
This is a purge (DELETE /api/tenants/{id}/job-history), not a list read of
GET /api/job-history.
Pre-blocks: auth.assertSuperuser, tenant.lookup
Response:
Deletes every JobHistory row for the Tenant and responds 200 with an empty
body. The example below is captured as a Tenant purging its own history.
Like the update route, this opens a route transaction, so a manager cannot purge
a different Tenant's history through it (the lookup 404s under the Tenant
RLS policy id = CURRENT_USER).
DELETE /api/tenants/{id}
Delete a Tenant and cascade-delete all of its data.
Authentication: Root manager superuser
Pre-blocks: auth.assertRootManagerSuperuser, tenant.lookup(params.id)
Response:
Responds 200 with an empty body (Informer's db.remove convention), not
204.
Deleting a Tenant cascades to all of its data. The id manager is rejected by
Joi (forbidden) and cannot be deleted.
Endpoints documented without captured examples
The following routes move binary content, call live external services, or are otherwise non-deterministic, so they are documented here without captured request/response examples.
POST /api/settings/mail/_ping
Send a test email to verify the SMTP configuration. Superuser. Reaches a live mail server, so it is not captured.
POST /api/settings/_cloud-ping
Verify connectivity to the configured Informer Cloud endpoint. Superuser. Reaches a live external service, so it is not captured.
DELETE /api/settings/mail/connection
Clear the Tenant's stored mail-integration connection. Superuser. Not captured.
POST /api/settings/apps/resources/_preview
Preview an App resource by fetching its bytes and computing an SRI hash before it is saved. Superuser. Performs a live CDN fetch, so it is not captured.
POST /api/settings/apps/resources/{id}/_refresh-sri
Re-fetch a resource's URLs and recompute their SRI hashes. Superuser. Performs a live CDN fetch, so it is not captured.
GET /api/orphan-indices
List Elasticsearch indices with no corresponding Dataset. Superuser. Reaches a live Elasticsearch cluster, so it is not captured.
POST /api/_purge-indices
Delete the orphaned Elasticsearch indices. Superuser. Reaches a live Elasticsearch cluster, so it is not captured.
POST /api/_export
Export the Tenant's configuration and content as a downloadable package. Superuser.
The system export is POST /api/_export, not POST /api/tenants/{id}/_export.
This route streams a binary package, so it is not captured.
PUT/POST /api/license
Install or activate a license. Superuser. Mutates installed-license state and reaches the License Manager, so it is not captured.
POST /api/license/_sync-tenant-licenses
Sync every Tenant's license with the License Manager (root manager superuser). A cross-Tenant live-external operation, so it is not captured.
POST /api/tenants/{id}/_copy
Copy content from one Tenant into another. Root manager superuser. A long-running cross-Tenant operation, so it is not captured.