Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Enterprise Compliance

Legal hold, retention policies, DLP, eDiscovery, and audit. The features that matter when your data is subject to regulation — financial services, healthcare, government — or when you need to preserve content for litigation.

For endpoint-level details, see Enterprise Reference.

Freezes content for litigation or investigation. Held content cannot be deleted — by anyone, including admins, even if deletion is attempted through the API, through retention policy, or via a bulk cleanup. Deletion attempts fail with LEGAL_HOLD_BLOCKED_DELETION.

A hold scopes to any combination of:

  • Users — all content they created or own.
  • Groups — aggregate of all members.
  • Shares — an entire share.
  • Folders — specific subtrees.
  • Include/exclude patterns — glob patterns within the scope.

Creating a hold#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X POST $SCAIDRIVE_URL/api/v1/enterprise/legal-holds \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Matter 2026-003",
    "description": "Dispute with ACME Corp",
    "matter_id": "LEGAL-2026-003",
    "custodian_ids": ["usr_01J4M", "usr_01J4N"],
    "legal_counsel": "outside-counsel@example.com"
  }'

Then add scopes:

bash
1
2
3
4
5
6
7
8
curl -X POST $SCAIDRIVE_URL/api/v1/enterprise/legal-holds/hld_01J7B/items \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scope_type": "user",
    "scope_id": "usr_01J4M",
    "include_pattern": "**/*.docx"
  }'

Holds can be indefinite or have an expiration_date. Releasing a hold (DELETE) allows retention and normal deletion to proceed.

What's preserved#

  • Current file versions.
  • All prior versions (even those that would have been pruned by retention).
  • Metadata — permissions, path, timestamps.
  • Change log entries referencing held resources.

What's not preserved: audit events (kept per tenant's audit retention, separately), chunks referenced only by unrelated content.

Retention policies#

Retention automates deletion of content older than a configured age. Common use cases:

  • Regulatory deletion ("delete financial records 7 years after creation").
  • Housekeeping ("clear project scratch folders after 90 days of inactivity").
  • Privacy compliance ("delete user data 30 days after account closure").

Policy structure#

json
1
2
3
4
5
6
7
8
{
  "name": "7-year financial retention",
  "retention_days": 2555,
  "trigger": "creation",
  "action": "delete",
  "scope_type": "share",
  "scope_ids": ["shr_finance"]
}

trigger: creation or modification — which timestamp starts the clock. action: delete (permanent), archive (move to archive store), quarantine (lock read-only pending review).

Legal hold overrides retention. A policy saying "delete after 30 days" does nothing to content under legal hold — retention silently skips held items. When the hold releases, any content already past its retention age becomes immediately eligible for the policy's action.

Inspection#

Every file and folder has an effective retention computed from all applicable policies. Currently surfaced via:

bash
1
2
curl -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
     $SCAIDRIVE_URL/api/v1/files/fil_01J3K/retention

Returns {eligible_at, policy_id, action} or null if no policy applies.

DLP (Data Loss Prevention)#

Rules that match file content or metadata and take action before content leaks.

Rule types#

Rule type Matches
regex Pattern against file content (text or extracted text from documents)
keyword Any of a list of keywords
file_type MIME type or extension

Actions#

Action Effect
block Upload fails with DLP_RULE_BLOCKED
quarantine File is uploaded but moved to quarantine; not accessible until reviewed
notify File is uploaded normally; admin notified
log Upload allowed; violation recorded for later review

Example#

A rule that blocks US Social Security numbers:

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X POST $SCAIDRIVE_URL/api/v1/enterprise/dlp/rules \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "US SSN",
    "rule_type": "regex",
    "pattern": "\\b\\d{3}-\\d{2}-\\d{4}\\b",
    "action": "block",
    "severity": "high"
  }'

Where rules run#

DLP evaluates at:

  • Upload time — new files and new versions.
  • Link creation — creating an external link triggers re-evaluation; blocked rules prevent link creation.
  • Download via external link — DLP can block downloads, not just uploads.

Rules do not retroactively evaluate existing content. For backfill, run an eDiscovery search with the same pattern.

Violations#

bash
1
2
3
4
curl -G $SCAIDRIVE_URL/api/v1/enterprise/dlp/violations \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  --data-urlencode "status=detected" \
  --data-urlencode "severity=high"

Admins review and remediate. Remediation options: delete file, remove violating content, dismiss (false positive), elevate to legal hold.

eDiscovery#

For formal evidence-gathering. Cases, searches, and exports.

Case#

A case groups related searches and exports. It's the unit of access control — only users added to the case can run searches or view results.

A search scopes across custodians, date ranges, and optional query patterns:

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
curl -X POST $SCAIDRIVE_URL/api/v1/enterprise/ediscovery/searches \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "case_id": "cas_01J7C",
    "name": "All 2026 Q1 email attachments about ACME",
    "custodians": ["usr_01J4M", "usr_01J4N"],
    "date_from": "2026-01-01T00:00:00Z",
    "date_to": "2026-03-31T23:59:59Z",
    "query": "ACME OR \"Acme Corp\""
  }'

Searches run asynchronously. Poll for completion:

bash
1
2
curl -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
     $SCAIDRIVE_URL/api/v1/enterprise/ediscovery/searches/sch_01J7D

Result counts, hit list, and links to download hits.

Export#

Package search results as a ZIP with a manifest for review in dedicated eDiscovery tools:

bash
1
2
3
4
curl -X POST $SCAIDRIVE_URL/api/v1/enterprise/ediscovery/exports \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"case_id": "cas_01J7C", "search_id": "sch_01J7D", "format": "edrm-xml"}'

Formats supported: edrm-xml (EDRM XML), concordance (Concordance load files), standard (ZIP + JSON manifest).

Audit log#

Every security-relevant action generates an audit event. Events are retained per tenant's configured audit retention (default 7 years).

Categories#

The category field is one of:

  • authentication — login success/failure, token refresh
  • authorization — ACL changes, inheritance changes, ownership transfer
  • file_access — read, download, preview
  • file_modification — file and folder create/update/delete/move/copy/rename/restore
  • sharing — share CRUD, member add/remove, invitation create/revoke, external link create/update/revoke/delete, ownership transfer
  • admin — user role change, identity mapping resolve/clear, quota set/delete/exempt, SIEM integration create/delete
  • security — DLP violation, access denied, suspicious activity
  • compliance — legal hold, retention policy, DLP rule lifecycle, eDiscovery, label CRUD

Event type families#

Each event has an event_type like file.create, permission.ace_add, compliance.legal_hold_release. The grouping:

  • auth.*login, logout, token_refresh, mfa_challenge
  • file.*create, update, delete, move, rename, copy, restore, download, read, preview
  • folder.*create, rename, delete, move, restore
  • sharing.*share_create, share_update, share_delete, member_add, member_update, member_remove, invitation_create, invitation_revoke, external_link_create, external_link_update, external_link_revoke, external_link_delete
  • permission.*ace_add, ace_remove, inheritance_change, ownership_transfer
  • admin.*user_role_change, identity_mapping_resolve, identity_mapping_clear, quota_set, quota_delete, quota_exempt, siem_integration_create, siem_integration_delete
  • compliance.*legal_hold_create, legal_hold_update, legal_hold_release, legal_hold_item_add, legal_hold_item_remove, retention_policy_create, retention_policy_delete, retention_policy_apply, dlp_rule_create, dlp_rule_update, dlp_rule_delete, ediscovery_case_create, ediscovery_case_close, ediscovery_custodian_add, ediscovery_custodian_remove, ediscovery_search_create, ediscovery_export_create, label_create, label_delete, label_apply, label_remove
  • security.*dlp_violation, access_denied, suspicious_activity

Severity escalation#

Most events emit at INFO. Three actions are auto-escalated to WARNING because they're high-impact and worth flagging in your SIEM rules:

  • compliance.legal_hold_release — content protection coming off
  • compliance.retention_policy_delete — retention rule disappearing
  • compliance.ediscovery_export_create — data leaving the platform

Failed logins (auth.login with outcome=failure) also emit at WARNING. Denied authorization attempts emit at ERROR.

Delegation provenance (service_account)#

Every audit event has a service_account field. When a token reached ScaiDrive through a token-exchange flow — e.g., a ScaiSpeak save_to upload acting on a user's behalf — the RFC 8693 act.client_id claim is recorded here. For direct user logins, service_account is null.

This lets you answer "who performed this action" and "what service delegated it" in a single audit query. SIEM exports include the field, so downstream consumers see the delegation chain across all event types.

bash
1
2
3
4
curl -G $SCAIDRIVE_URL/api/v1/enterprise/audit/events \
  -H "Authorization: Bearer $SCAIDRIVE_TOKEN" \
  --data-urlencode "event_type=file.create" \
  --data-urlencode "since=2026-04-01T00:00:00Z"
python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
events = httpx.get(
    f"{url}/api/v1/enterprise/audit/events",
    headers={"Authorization": f"Bearer {token}"},
    params={"event_type": "file.create", "since": "2026-04-01T00:00:00Z"},
).json()

for e in events:
    actor = e["user_id"]
    via = e.get("service_account")
    print(f"{e['event_time']} {actor}" + (f" via {via}" if via else ""))
typescript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const params = new URLSearchParams({
  event_type: "file.create",
  since: "2026-04-01T00:00:00Z",
});
const resp = await fetch(
  `${url}/api/v1/enterprise/audit/events?${params}`,
  { headers: { Authorization: `Bearer ${token}` } },
);
const events = await resp.json();
for (const e of events) {
  const via = e.service_account ? ` via ${e.service_account}` : "";
  console.log(`${e.event_time} ${e.user_id}${via}`);
}

Querying#

The /audit/events endpoint accepts filtering on category, severity, user_id, share_id, resource_type, event_type, since/until, and outcome. See the Enterprise reference for the full parameter list.

SIEM integration#

For continuous streaming to a SIEM (Splunk, Elastic, syslog, generic webhook), configure a SIEM integration via POST /api/v1/enterprise/siem/integrations. Events stream in near-real-time as they're written; the service_account field is included in the exported payload so downstream rules can fire on delegation patterns (e.g., "alert when ScaiSpeak generates >100 files in 1 hour on behalf of the same user").

Best practices#

  • Scope aggressively. Don't put "all content" on legal hold; use custodians and patterns. Small scopes are cheaper to maintain and audit.
  • Retention before DLP. If you shouldn't keep it, delete it. Retention is cheaper to enforce than DLP rules that catch sensitive content.
  • Review DLP violations weekly. detected status is a queue — let it grow and you'll miss the signal.
  • Test DLP rules before enabling block. Start with log, check the violation rate, then tighten.
  • Keep audit retention aligned with legal requirements. Most regulated industries require 7+ years.

What's next#

Updated 2026-05-18 15:04:19 View source (.md) rev 2