Audit Logs v1

Overview

SparkVault records security-relevant activity in a unified audit log. Every entry captures what happened (event_type), when (timestamp), who did it (actor_id), and from where (ip_address, user_agent), plus event-specific metadata. Events are partitioned into four scopes, each with its own list endpoint:

Scope What it records Endpoint Auth
Account Logins, user and license management, billing, API keys, custom domains, vault create/delete, SparkLink receipts, Messaging governance GET /v1/audit-logs API key or JWT
Integration / Product Per-integration and per-product event streams (Slack, HubSpot, Identity, Structured Ingots, …), called apps in the API GET /v1/audit-logs/apps/{app_slug} API key or JWT
Vault Vault settings, seal/unseal, sharing, upload portals, DVAKs, folder operations GET /v1/vaults/{vault_id}/audit-logs API key or JWT + X-Vault-Access-Token
Ingot Per-file activity: uploads, downloads, accesses, renames, sharing GET /v1/vaults/{vault_id}/ingots/{ingot_id}/audit-logs API key or JWT + X-Vault-Access-Token

Entries are returned newest-first with signed cursor pagination. Account and integration entries are stored in plaintext. Vault and ingot entry metadata is encrypted at rest with AES-256-GCM under a key derived (HKDF) from the vault's Vault Access Key (VAK) when the event is recorded through an unsealed session. It can only be decrypted with a valid Vault Access Token. Events recorded outside a vault session (such as retention_changed) store their metadata in plaintext.

Entry Fields

Audit Log Entry

FieldTypeDescription
timestamp integer Unix timestamp of the event in epoch seconds
event_type string Event identifier (see Event Types)
actor_id string? Who performed the action: a user (usr_...) or an API key (key_...). null for unattributed events; omitted on integration-scope entries.
actor_type string? user or api_key, present when the actor still exists in your account and could be resolved
actor_display string? Human-readable actor: the user's email or the API key's name, present when resolved
ip_address string? Client IP address that triggered the event
user_agent string? Client user agent
metadata object? Event-specific details. Decrypted transparently on vault/ingot endpoints using your VAT session; null when the event carries none.
vault_id string? Present on vault- and ingot-scope entries
ingot_id string? Present on ingot-scope entries
app_slug string? Present on integration-scope entries

Audit logs answer who did what, and when. For usage aggregates and analytics dashboards, see Reporting.

Event Types

Each scope has its own set of event types. The account, vault, and ingot list endpoints return the full valid set for their scope in the response's event_types field, so you can build filters without hardcoding the lists below.

Account Events

Category Event Types
User management user_invited, user_removed, user_role_changed
License management user_seat_type_set, user_license_cancel_scheduled, user_license_cancel_undone, user_license_released, license_migration_applied
Authentication login, logout, login_failed
Identity verification auth_attempt, auth_success, auth_failure
Account settings email_changed, name_changed, organization_name_changed, logo_updated
Custom domains custom_domain_added, custom_domain_removed, custom_domain_verified
Vault lifecycle vault_created, vault_deleted
Billing payment_added, payment_failed, credit_card_added, credit_card_removed
API keys api_key_created, api_key_revoked
SparkLink lifecycle sparklink_created, sparklink_deleted, sparklink_revoked
Verified-interaction receipts sparklink_accessed, sparklink_signed, sparklink_approved, sparklink_denied, sparklink_replied
Messaging governance messaging_conversation_created, messaging_participant_added, messaging_participant_removed, messaging_recipient_verified, messaging_message_sent, messaging_message_recalled, messaging_opted_out, messaging_opted_in, messaging_legal_hold_set, messaging_legal_hold_cleared, messaging_receipt_issued

The account-scope event_types response set also includes platform-admin event types (admin_impersonate, admin_account_deleted, admin_status_changed, admin_pricing_updated, admin_blocklist_source_toggled, admin_blocklist_domain_added, admin_blocklist_domain_removed). These are recorded only under the SparkVault platform admin account, so your own audit logs will never contain them.

Portable Receipts Live at the Account Scope

Verified-interaction receipt events (sparklink_accessed, sparklink_signed, sparklink_approved, sparklink_denied, sparklink_replied) are recorded at the account scope in plaintext so they can be presented as third-party proof without a vault key: the signed EdDSA receipt token and action_hash ride in metadata and are JWKS-verifiable. Messaging events never contain message content: only IDs, the verified sender, and the SparkVault-signed receipt token.

Vault Events

Category Event Types
Vault operations vault_renamed, vault_settings_changed, vault_unsealed, vault_sealed
Sharing sharing_enabled, sharing_disabled, sharing_config_changed
Upload portal and widget upload_portal_enabled, upload_portal_disabled, upload_portal_repaired, upload_widget_enabled, upload_widget_disabled, upload_widget_repaired
VMK hosting vmk_hosted, vmk_removed, hvmk_key_created
Delegated Vault Access Keys dvak_created, dvak_revoked, dvak_used, dvak_auto_revoked
Folders folder_created, folder_renamed, folder_moved, folder_deleted, folder_updated
Retention retention_changed

Ingot Events

Category Event Types
Lifecycle ingot_created, ingot_renamed, ingot_deleted, ingot_moved
Sharing ingot_shared, ingot_unshared, invite_created, invite_revoked
Access ingot_accessed, ingot_downloaded, ingot_uploaded
SparkLink access sparklink_accessed
Errors upload_error, download_error

Integration and Product Events

Integration-scope event types are free-form strings: integrations and products define their own. Common patterns:

Category Event Types
Installation app_installed, app_uninstalled, app_config_changed
Identity Product passkey_created, passkey_removed
Slack Integration slack_connected, slack_disconnected, slack_message_sent
Structured Ingots Product structure_created, structure_updated, key_accessed

Pagination and Filtering

All list endpoints share the same cursor-based pagination and filtering contract:

Query Parameters

ParameterTypeRequiredDescription
limit integer Optional Maximum entries per page. Values outside 1-100 are clamped into range.Default: 25
cursor string Optional Opaque signed pagination cursor: pass back the next_cursor from the previous responseDefault: null
event_types string Optional Comma-separated event type filter, e.g. ?event_types=login,login_failedDefault: none (all events)
  • Ordering: entries are returned newest-first; timestamp is epoch seconds.
  • has_more / next_cursor: next_cursor is present only when has_more is true. Keep requesting with ?cursor= until has_more is false.
  • Signed cursors: cursors are HMAC-signed per account and bound to the exact audit scope they were minted for. A tampered cursor, a cursor from another account, or a cursor replayed against a different scope returns 400 VALIDATION_ERROR.
  • Filtered pages can run short: with an event_types filter, a page may contain fewer than limit entries (even zero) while has_more is still true. Always page by has_more, not by page size.

Account Audit Logs

GET /v1/audit-logs

List account-scope audit logs: authentication, user and license management, billing, API keys, custom domains, vault lifecycle, SparkLink receipts, and Messaging governance events.

Query Parameters

ParameterTypeRequiredDescription
limit integer Optional Maximum entries per page (1-100)Default: 25
cursor string Optional Signed pagination cursor from a previous responseDefault: null
event_types string Optional Comma-separated filter of account event typesDefault: none (all events)

Response

FieldTypeDescription
entries array Audit log entries, newest first (see Entry Fields in the Overview)
count integer Number of entries in this page
has_more boolean Whether more pages exist
next_cursor string? Signed cursor for the next page (present when has_more is true)
event_types string[] Every valid account-scope event type, useful for building filter UIs

List Recent Logins

Request
bash
curl "https://api.sparkvault.com/v1/audit-logs?limit=2&event_types=login,login_failed" \
  -H "X-API-Key: sv_live_xxx"
Response
json
{
  "data": {
    "entries": [
      {
        "timestamp": 1702000600,
        "event_type": "login",
        "actor_id": "usr_abc123...",
        "actor_type": "user",
        "actor_display": "admin@example.com",
        "ip_address": "203.0.113.7",
        "user_agent": "Mozilla/5.0...",
        "metadata": null
      },
      {
        "timestamp": 1702000000,
        "event_type": "login_failed",
        "actor_id": null,
        "ip_address": "198.51.100.23",
        "user_agent": "Mozilla/5.0...",
        "metadata": null
      }
    ],
    "count": 2,
    "has_more": true,
    "next_cursor": "eyJrZXkiOnsi...",
    "event_types": ["user_invited", "user_removed", "user_role_changed", "..."]
  }
}

Integration and Product Audit Logs

Every installed integration and every product keeps its own event stream, addressed by its slug. The API calls this scope apps. Integration-scope entries record timestamp, event_type, metadata, and app_slug. They carry no actor or IP fields.

GET /v1/audit-logs/apps/{app_slug}

List the audit log for one integration or product stream.

Path Parameters

ParameterTypeRequiredDescription
app_slug string Required Slug of the integration or product stream, e.g. slack

Query Parameters

ParameterTypeRequiredDescription
limit integer Optional Maximum entries per page (1-100)Default: 25
cursor string Optional Signed pagination cursor from a previous responseDefault: null
event_types string Optional Comma-separated event type filterDefault: none (all events)

Response

FieldTypeDescription
entries array Audit log entries, newest first
count integer Number of entries in this page
has_more boolean Whether more pages exist
next_cursor string? Signed cursor for the next page (present when has_more is true)
app_slug string The requested stream slug

List Slack Integration Events

Request
bash
curl "https://api.sparkvault.com/v1/audit-logs/apps/slack?limit=25" \
  -H "X-API-Key: sv_live_xxx"
Response
json
{
  "data": {
    "entries": [
      {
        "timestamp": 1702000000,
        "event_type": "slack_connected",
        "metadata": null,
        "app_slug": "slack"
      }
    ],
    "count": 1,
    "has_more": false,
    "app_slug": "slack"
  }
}
POST /v1/audit-logs/apps

Append an event to an integration or product stream. Event types are free-form, so your own automations can write their own audit trail. Returns 201 Created.

Request Body

ParameterTypeRequiredDescription
app_slug string Required Stream to log under, e.g. slack
event_type string Required Free-form event identifier
metadata object Optional Arbitrary event details stored with the entry (plaintext; never put secrets here)Default: null

Response

FieldTypeDescription
logged boolean true: the entry was written
app_slug string Stream the entry was written to
event_type string The recorded event type

Log a Custom Event

Request
bash
curl -X POST https://api.sparkvault.com/v1/audit-logs/apps \
  -H "X-API-Key: sv_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "app_slug": "hubspot",
    "event_type": "export_completed",
    "metadata": { "record_count": 42 }
  }'
Response
json
{
  "data": {
    "logged": true,
    "app_slug": "hubspot",
    "event_type": "export_completed"
  }
}
Append-Only

Audit streams are append-only: entries cannot be edited or deleted through the API, and each POST writes a new entry. SparkVault's own integrations and products log to these streams via internal service calls.

Vault Audit Logs

GET /v1/vaults/{vault_id}/audit-logs

List vault-scope audit logs: settings, seal/unseal, sharing, upload portals, DVAKs, and folder operations. Requires an unsealed vault session (VAT).

This endpoint requires the X-Vault-Access-Token header with a valid VAT obtained by unsealing the vault. Entry metadata is stored encrypted and is decrypted transparently with the VAK held by your VAT session. Vault-scope events cover the vault itself. Per-file events live on the per-ingot endpoint.

Path Parameters

ParameterTypeRequiredDescription
vault_id string Required Vault identifier (vlt_...)

Query Parameters

ParameterTypeRequiredDescription
limit integer Optional Maximum entries per page (1-100)Default: 25
cursor string Optional Signed pagination cursor from a previous responseDefault: null
event_types string Optional Comma-separated filter of vault event typesDefault: none (all events)

Response

FieldTypeDescription
entries array Audit log entries, newest first; each includes vault_id
count integer Number of entries in this page
has_more boolean Whether more pages exist
next_cursor string? Signed cursor for the next page (present when has_more is true)
vault_id string The requested vault
event_types string[] Every valid vault-scope event type

List Vault Events

Request
bash
curl "https://api.sparkvault.com/v1/vaults/vlt_abc123/audit-logs?limit=25" \
  -H "X-API-Key: sv_live_xxx" \
  -H "X-Vault-Access-Token: vat_xyz789..."
Response
json
{
  "data": {
    "entries": [
      {
        "timestamp": 1702000000,
        "event_type": "vault_unsealed",
        "actor_id": "usr_abc123...",
        "actor_type": "user",
        "actor_display": "admin@example.com",
        "ip_address": "203.0.113.7",
        "user_agent": "Mozilla/5.0...",
        "metadata": { "ttl_seconds": 3600 },
        "vault_id": "vlt_abc123..."
      }
    ],
    "count": 1,
    "has_more": false,
    "vault_id": "vlt_abc123...",
    "event_types": ["vault_renamed", "vault_settings_changed", "..."]
  }
}
VAT Context Checks

The VAT must belong to the requested vault and to your account: a mismatch, or a token that has expired, returns 403 FORBIDDEN. A missing, malformed, or unknown token returns 401 AUTHENTICATION_ERROR, and a vault that does not exist (or is not owned by your account) returns 404 NOT_FOUND.

Ingot Audit Logs

GET /v1/vaults/{vault_id}/ingots/{ingot_id}/audit-logs

List per-ingot audit logs: uploads, downloads, accesses, renames, moves, and sharing on a single file. Requires an unsealed vault session (VAT).

The per-ingot endpoint follows the same auth, pagination, and filtering contract as the vault endpoint above: X-Vault-Access-Token required, encrypted metadata decrypted via your VAT session, and a response carrying entries (each with vault_id and ingot_id), count, has_more, next_cursor, and the valid ingot-scope event_types. Full documentation lives on the Ingots page.

Retention

Every audit entry is written with an expiry and is removed automatically when it lapses. The default retention window is 90 days (7,776,000 seconds).

Each vault additionally carries a configurable access-log retention setting (GET/PUT /v1/vaults/{vault_id}/access-log-retention, see the Vaults docs) that governs how long ingot access events recorded through public sharing surfaces (public portal uploads and SparkLink downloads) are kept. Setting it to 0 (Disabled) turns that logging off entirely: no entries are written. The change itself is always recorded as a retention_changed vault event under the default window, so the configuration trail survives even when access logging is disabled.

Value (seconds) Window
0Disabled: no access-log entries are written
8640024 hours
6048007 days
25920001 month
51840002 months
77760003 months (default)
155520006 months
233280009 months
315360001 year
630720002 years
946080003 years
Deletion Cascades to Audit History

Audit logs are owned by the entity they describe. Deleting an ingot, a vault, an integration installation, or your account permanently deletes the audit history recorded under it. Export anything you need for compliance before deleting.

Error Reference

Error Responses

StatusCodeDescription
400 VALIDATION_ERROR Invalid or tampered pagination cursor (wrong account or scope), or missing app_slug/event_type when creating an entry
401 AUTHENTICATION_ERROR Missing or invalid API key/JWT; missing, malformed, or unknown X-Vault-Access-Token on vault and ingot endpoints
403 FORBIDDEN The VAT belongs to a different vault or account than the one requested, or the token has expired
404 NOT_FOUND Vault does not exist or is not owned by your account
429 RATE_LIMIT_EXCEEDED Too many requests