KaireonAI includes built-in compliance infrastructure for multi-tenant data isolation, immutable audit trails, data subject access requests (DSAR), PII masking, and configurable data retention. These features support GDPR, CCPA, and SOC 2 readiness out of the box.
Tenant Isolation
Every API request is scoped to a single tenant. Cross-tenant data access is architecturally prevented at the query layer.
How it works:
- Tenant resolution — Each request resolves a
tenantId from either the JWT session (user.tenantId) or the X-Tenant-Id header (for API key auth). API key authentication binds the tenant to the key itself, ignoring any X-Tenant-Id header to prevent spoofing.
- Database validation — The resolved tenant ID is validated against the
Tenant table in the database. If the tenant does not exist, the request is rejected with 403 Forbidden. The system fails closed: if the database is unreachable during validation, the request is denied.
- Query enforcement — The
enforceTenantFilter() helper injects tenantId into every Prisma where clause. The withTenantScope() wrapper provides scoped findMany, findUnique, create, update, and delete methods that automatically include the tenant filter. Calling these helpers without a tenantId throws an error.
- Single-tenant mode — For self-hosted deployments, set
SINGLE_TENANT_MODE=true to bypass tenant resolution and use tenantId: "default" for all requests.
Every database query in every API route passes through tenant filtering. There is no “admin bypass” that returns data across tenants.
Audit Logging
All entity mutations (create, update, delete) are recorded in an immutable audit log with a cryptographic integrity chain.
What Gets Logged
Every write operation on platform entities produces an audit record with the following fields:
| Field | Description |
|---|
action | The operation: create, update, delete, or domain-specific actions like retention_config.upsert |
entityType | The type of entity (e.g., Offer, DecisionFlow, RetentionConfig, dsar_request) |
entityId | The unique ID of the affected entity |
entityName | A human-readable name for the entity |
changes | A JSON object capturing the before/after or the input data |
userId | The authenticated user who performed the action |
userName | Display name of the user (defaults to "system") |
tenantId | The tenant scope |
requestId | Correlation ID for tracing across services |
timestamp | When the event occurred |
integrityHash | SHA-256 hash of the record contents plus the previous hash (see Verification below) |
prevHash | The integrity hash of the preceding audit record in the chain |
Immutability
Audit logs cannot be modified or deleted through the API. The audit log endpoint explicitly rejects DELETE, PUT, and PATCH requests with a 405 Method Not Allowed response:
“Audit logs are immutable and cannot be deleted.”
Resilience
The audit system uses a circuit breaker pattern. If the database becomes temporarily unavailable, audit events are buffered in memory (up to 500 events) and flushed when connectivity is restored. A per-tenant mutex prevents hash chain forks from concurrent writes.
Querying Audit Logs
Headers: X-Tenant-Id or session cookie. Requires admin role.
Query parameters:
| Parameter | Default | Description |
|---|
entityType | — | Filter by entity type |
page | 1 | Page number |
limit | 50 | Results per page (max 100) |
Response:
{
"logs": [ ... ],
"total": 1234,
"page": 1,
"limit": 50
}
Audit Verification
Each audit log entry includes a SHA-256 integrity hash computed over its contents and the hash of the previous entry, forming a cryptographic chain. Tampering with any record breaks the chain from that point forward.
How the Hash Chain Works
- Each audit record’s hash is computed as:
SHA-256(action + entityType + entityId + entityName + changes + userId + userName + tenantId + requestId + prevHash)
- The first record in the chain uses
"genesis" as the previous hash.
- Each subsequent record references the
integrityHash of the record before it.
Verification Endpoint
GET /api/v1/audit-logs/verify
Headers: X-Tenant-Id or session cookie. Requires admin role.
Query parameters:
| Parameter | Default | Description |
|---|
limit | — | Only verify the last N entries (quick check mode). Omit to verify the full chain. |
Response:
{
"intact": true,
"verified": 847,
"total": 847,
"scanned": 847
}
If tampering is detected:
{
"intact": false,
"verified": 412,
"total": 847,
"scanned": 847,
"brokenAtId": "clxyz...",
"brokenReason": "hash_mismatch"
}
Broken reasons:
hash_mismatch — The recomputed hash does not match the stored integrityHash. The record’s contents were modified after creation.
chain_link_mismatch — The record’s prevHash does not match the integrityHash of the preceding record. A record was inserted or deleted in the middle of the chain.
Audit Export
Bulk export of audit logs for regulatory reporting and SOC 2 evidence collection.
Headers: X-Tenant-Id or session cookie. Requires admin role. Rate limited to 10 requests per minute.
Query parameters:
| Parameter | Default | Description |
|---|
format | json | Export format: json, csv, or soc2 |
startDate | — | ISO date string for range start |
endDate | — | ISO date string for range end |
entityType | — | Filter by entity type |
action | — | Filter by action (create, update, delete) |
limit | 10000 | Max records (capped at 10,000) |
offset | 0 | Pagination offset |
The csv format returns the export as a downloadable file with a Content-Disposition header.
You can also verify chain integrity through the export endpoint:
POST /api/v1/audit-export
Content-Type: application/json
{
"action": "verify_integrity",
"startDate": "2026-01-01T00:00:00Z",
"endDate": "2026-03-01T00:00:00Z"
}
DSAR (Data Subject Access Requests)
The DSAR endpoint supports GDPR right-of-access and right-to-erasure requests. DSAR processing is asynchronous — the API accepts the request immediately and processes it in the background via a job queue.
Submitting a DSAR
POST /api/v1/dsar
Content-Type: application/json
{
"requestType": "export",
"subjectId": "cust-12345",
"subjectType": "customer_id"
}
Headers: X-Tenant-Id or session cookie. Requires admin role. Rate limited to 10 requests per minute.
Parameters:
| Field | Required | Description |
|---|
requestType | Yes | "export" (data access) or "delete" (erasure) |
subjectId | Yes | The customer ID or email to look up |
subjectType | No | "customer_id" (default) or "email" |
Response (202 Accepted):
{
"requestId": "dsar-abc123",
"status": "queued"
}
The request is enqueued for asynchronous processing. The DSAR creation itself is audit-logged.
Checking DSAR Status
GET /api/v1/dsar?status=completed&limit=50
Query parameters:
| Parameter | Default | Description |
|---|
status | — | Filter by status: queued, processing, completed, failed |
limit | 50 | Max results (capped at 200) |
PII Masking
Data pipelines include two built-in transforms for protecting sensitive data before it reaches analytics tables or export targets.
Replaces field values with a one-way cryptographic hash. Useful for pseudonymization where you need consistent identifiers without exposing the original value.
Configuration:
- Fields to Hash — One or more source fields to hash
- Algorithm —
SHA-256 (recommended), SHA-512, or MD5
Applies pattern-based masking that preserves partial information for operational use while hiding sensitive details. Masking patterns are applied automatically based on detected data formats:
| Data Type | Before | After |
|---|
| SSN | 123-45-6789 | ***-**-6789 |
| Email | user@example.com | u***@example.com |
| Phone | (555) 123-4567 | (***) ***-4567 |
Configuration:
- Fields to Mask — One or more source fields to apply masking to
When to Use
- Before loading to analytics — Mask PII fields in your ETL pipeline before data lands in reporting tables
- Before export — Hash or mask sensitive fields before sending data to external systems
- Pseudonymization — Use the hash transform to create consistent non-reversible identifiers for analytics
Both transforms are available as pipeline nodes in the visual pipeline editor under the “Transform” category.
Data Retention
Configurable retention policies control how long different classes of data are kept. Retention configs are per-tenant and per-data-class.
Retention Config Endpoint
GET /api/v1/admin/retention-configs
Returns all retention policies for the current tenant. Requires admin role.
Creating or Updating a Retention Policy
POST /api/v1/admin/retention-configs
Content-Type: application/json
{
"dataClass": "interactions",
"retentionDays": 365,
"legalHold": false
}
Parameters:
| Field | Required | Description |
|---|
dataClass | Yes | One of: interactions, decisions, metrics, audit |
retentionDays | Yes | Number of days to retain (1 to 36,500) |
legalHold | No | When true, prevents automatic purging regardless of retention period |
Data classes:
| Class | What It Covers |
|---|
interactions | Customer interaction history and response records |
decisions | Decision trace records from the recommendation engine |
metrics | Behavioral metrics and pipeline execution metrics |
audit | Audit log entries (subject to legal hold considerations) |
Retention policy changes are themselves audit-logged with action retention_config.upsert (or retention_config.legal_hold when a legal hold is applied).
Encryption
Data in Transit
All connections to the KaireonAI platform are over HTTPS. The playground deployment at playground.kaireonai.com enforces TLS.
Data at Rest
- Connector credentials — The
authConfig field on connector records stores sensitive connection parameters (passwords, API keys, access tokens). These are stored as encrypted JSON in the database.
- API keys — Platform API keys (
krn_ prefix) are validated against hashed values in the database.
Session Security
Authentication uses JWT-based sessions via NextAuth. Session tokens are HTTP-only cookies with secure flags in production.