Zoza Auth is an authentication oracle: when we say "approved", a bank transfers money. The single most important safety property we can offer is: the bank can verify every event we've ever signed off on, on its own account. This page documents exactly what's logged, who reads it, and how long we keep it.
Auth maintains two complementary logs:
auth_audit table, readable by each customer bank via the authenticated API. One row per security-relevant event for that bank's app. Retained 90 days.activity_logs in zoza-admin, readable by Zoza operators. One row per admin action on any app (approve, reject, revoke, configuration change). Retained indefinitely.Both logs pull from the same source events — one is customer-scoped, the other is platform-scoped. A breach or tamper on one cannot hide from the other.
| action | when | visible to | retention |
|---|---|---|---|
device_registered | User enrols a new device with bank X | Bank X | 90 days |
device_revoked | Bank X revokes a user's device (theft, replacement) | Bank X | 90 days |
challenge_issued | Bank X calls POST /v1/challenges | Bank X | 90 days |
challenge_approved | User's device signs + verify succeeds | Bank X | 90 days |
challenge_denied | User taps Deny, or verify fails (wrong key, mismatch) | Bank X | 90 days |
challenge_expired | TTL elapsed without a response | Bank X | 90 days |
application_submit | Prospective customer submits POST /v1/applications | Zoza ops | Indefinite (abuse signal) |
application_approve / application_reject | Zoza admin reviews | Zoza ops | Indefinite |
Each row stores: app_id, user_id, device_id, challenge_id, action, success, error, created_at. Plaintext payloads, proofs, private keys, and biometric data are never logged.
Authenticated to the API with your bearer API key:
# Fetch the latest 100 audit events for your app
curl https://auth-api.zoza.world/v1/apps/{app_id}/audit -H "Authorization: Bearer $ZOZA_AUTH_API_KEY"
# Example response
{
"app_id": "app_...",
"count": 100,
"audits": [
{
"id": "aud_...",
"action": "challenge_approved",
"user_id": "user_rahul",
"device_id": "iphone_15_pro",
"challenge_id": "ch_...",
"success": true,
"created_at": "2026-04-17T14:22:18Z"
},
...
]
}
We recommend banks pull their log every 5 minutes into their own SIEM (Splunk / Chronicle / ELK). If an attacker compromises our API, the bank's local copy remains tamper-evident.
Zoza operators: yes, for platform operation (debugging, abuse triage, rate-limit tuning).
Every operator read is itself logged in activity_logs on zoza-admin — a read of the audit
log creates an audit log entry. Meta-logging, so to speak.
Law enforcement: only under a DPDP-compliant legal request addressed to the customer bank, not to Zoza directly. We are a data-processor, not a data-fiduciary. In the rare case of compelled disclosure targeting Zoza (e.g. if the bank itself is under investigation), the warrant canary at /about/auth-canary reflects that.
The public: no — unlike Shield, Auth's audit log is customer-scoped (private). What's public is the existence of the log, the field schema, the retention period, and this policy.
The auth_audit table is append-only at the application layer. Direct database DELETE or UPDATE
is possible with Postgres superuser — in that scenario, the customer bank's own scraped copy (see above)
is the ground truth. On the roadmap: ed25519-signed hash-chain, same pattern as Shield's audit log. ETA ~20 dev days.
In the meantime, every admin action in zoza-admin leaves a second row in activity_logs with a
separate write path — a tamper on one without the other is detectable.
user_id as the bank labels it, nothing more.