Audit logs
your customers can read themselves
Emit an event with one call. Santati hash-chains it, scopes it to your customer's organization, and serves it back through one read API — so your customers can answer their own audit questions.
Free up to 1,000 events a day. No credit card required.
- SHA-256
- hash-chained writes
- 1
- API call to read it back
- per-org
- scoping and retention
- HMAC
- signed webhook streams
Every event, receipted
Writes are append-only and hash-chained. Reads are filtered by actor, target and organization. Your customer's compliance team self-serves the evidence.
- Hash-chained, append-only writes
- Actor, target and organization indexing
- Streaming export to your webhooks
Emit an event in one call
Drop an SDK in wherever the action happens. One call, typed payload, no queue to run.
import { Santati } from '@santati/node';
const santati = new Santati({ apiKey: process.env.SANTATI_API_KEY, trail: 'billing' });
await santati.events.emit({
event: 'invoice.voided',
organizationId: 'org_acme_logistics',
actor: { type: 'user', id: 'usr_123', name: 'Valentine Michael Smith' },
targets: [{ type: 'invoice', id: 'inv_555', name: 'INV-555' }],
data: { amount_cents: 4200, currency: 'usd', status: 'void' },
});
no queue, no schema to register — the API accepts the event and indexes actor, target and organization
One call to read a customer's log
Filter by organization and hand the page straight to your customer's screen. Team-scoped API keys mean a key can only ever see its own team's events.
curl -G https://api.santati.io/api/v0/events/ \ -H "Authorization: Api-Key $SANTATI_API_KEY" \ -d organization_id=org_acme_logistics -d limit=50
{
"next": "https://api.santati.io/api/v0/events/?organization_id=org_acme_logistics&limit=50&cursor=cD0yMDI2LTA5LTI5VDE0JTNBMDMlM0ExMS40ODIwMDAlMkIwMA",
"previous": null,
"results": [
{
"id": "01J9Z6K3M4QX8RT2VN5B7HCWDA",
"trail": "billing",
"event": "invoice.voided",
"created_at": "2026-09-29T14:03:11.482000Z",
"received_at": "2026-09-29T14:03:11.913000Z",
"organization_id": "org_acme_logistics",
"actor": { "type": "user", "id": "usr_123", "name": "Valentine Michael Smith", "metadata": {} },
"targets": [{ "type": "invoice", "id": "inv_555", "name": "INV-555", "metadata": {} }],
"metadata": {},
"data": { "amount_cents": 4200, "currency": "usd", "status": "void" },
"context": { "location": "203.0.113.4", "user_agent": "acme-billing/1.4" },
"hash": "9f2c1d4a5b6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708",
"schema_version": null
}
]
}
| Occurred | Event | Actor | Target |
|---|---|---|---|
| 2026-09-29 14:03:11 | invoice.voided | Valentine Michael Smith | inv_555 |
| 2026-09-29 14:02:48 | payment.refunded | Valentine Michael Smith | pay_9931 |
| 2026-09-29 13:58:02 | member.role_changed | Jill Boardman | usr_44c1 |
| 2026-09-29 13:51:37 | login.succeeded | Valentine Michael Smith | sso/okta |
Built for audit evidence
01 / Integrity
Tamper-evident chain
Every live write is hashed into a per-trail append-only chain. Verification is one endpoint your customer's auditor can call without you.
02 / Scoping
Organization-scoped reads
API keys are bound to one team and every read is filtered by organization, so one customer can never see another customer's events.
03 / Export
Streaming export
Relay every event to your own HTTPS webhook as it arrives, signed so you can verify it. Failed deliveries retry and can be replayed.