Audit Logs

Zuplo Audit Logs

Zuplo's Audit Logs feature gives you a complete audit trail of your API with a single policy: the gateway records a structured event for every request, stores the events for you, and makes them available in the Zuplo Portal and through the Zuplo API.

Enterprise Feature

Audit Logs is available as an add-on as part of an enterprise plan. If you would like to purchase this feature, please contact us at sales@zuplo.com or reach out to your account manager.

Most enterprise features can be used in a trial mode for a limited time. Feel free to use enterprise features for development and testing purposes.

How it works

Zuplo
Gateway (Audit Logs policy)
Audit log storage
Client
Portal / API / SIEM

Add the Audit Logs policy to any route and the gateway records one structured audit event per request — who made the request, what they called, and the outcome. You can also emit your own custom events (for example, "account deleted") from handlers and custom policies. Every event follows the CloudEvents 1.0 format.

Zuplo stores the events for you, grouped by environment — production, preview, and working copy environments each write to their own bucket. You don't need a database, logging service, or any other infrastructure of your own.

Recording audit events

Automatic request events

Add the policy to the routes you want audited. Typically that's any route that modifies data, though depending on your API you may want it on sensitive read operations as well (for example, retrieving a secret).

JSONconfig/policies.json
{ "name": "audit-logs", "policyType": "audit-log-inbound", "handler": { "export": "AuditLogInboundPolicy", "module": "$import(@zuplo/runtime)" } }

Each request through the policy produces an event of type com.zuplo.api.request capturing the actor, HTTP method and path, response status, caller IP address, and geolocation.

Audit events can contain personal or sensitive data. The policy's include options let you turn off individual fields (query parameters, user identity, IP address, geolocation) to satisfy your own data-handling and personally identifiable information (PII) policies, and samplingRate lets you capture only a fraction of requests on high-volume routes. See the policy reference for all options.

Custom events

Beyond the automatic per-request event, record your own domain events from a handler or custom policy with the static AuditLogInboundPolicy.log() method. Only type is required — Zuplo fills in the actor, timestamp, request ID, and other context fields automatically from the request:

TypeScriptmodules/handlers.ts
import { AuditLogInboundPolicy, ZuploContext, ZuploRequest, } from "@zuplo/runtime"; export async function deleteAccount( request: ZuploRequest, context: ZuploContext, ) { const accountId = request.params.accountId; // ...perform the delete... AuditLogInboundPolicy.log(context, { type: "com.acme.account.deleted", subject: `account|${accountId}`, resources: [{ type: "account", id: accountId }], success: true, }); return new Response(null, { status: 204 }); }

Audit logging is best-effort by design: log() never throws and never blocks or fails the request, even if an event can't be recorded.

Viewing audit logs in the portal

To browse and search your audit logs, open your project's Services tab in the Zuplo Portal, pick the environment you want to inspect with the Dev / Preview / Prod switcher at the top of the sidebar, then select Audit Logs → Events in the sidebar.

The events view lets you:

  • Filter events by time range, event type, actor, and subject
  • See the top event types and most active actors for the selected window
  • Select any event to inspect the full event payload

Querying audit logs with the API

Everything in the portal is backed by the Zuplo API, so you can query audit logs programmatically — for scripting, custom dashboards, or exporting to other systems. Authenticate with a Zuplo API key and query events by bucket ID. Copy the bucket ID from the copyable Bucket ID pill under the API Key Service heading on the Services → API Keys → Consumers page in the portal (it's the same bucket used by the API Key Service).

TerminalCode
curl "https://dev.zuplo.com/v1/audit-logs/$BUCKET_ID/events?type=com.zuplo.api.request&startDate=2026-07-01T00:00:00Z&endDate=2026-07-07T00:00:00Z" \ -H "Authorization: Bearer $ZUPLO_API_KEY"
JSONCode
{ "data": [ { "specversion": "1.0", "id": "e6c8b1f2-2c3d-4a5b-9e10-1f2a3b4c5d6e", "type": "com.zuplo.api.request", "time": "2026-07-05T18:12:04.123Z", "actorsub": "user|12356", "actortype": "user", "requestid": "b1a2c3d4-e5f6-7890-abcd-ef1234567890", "httpmethod": "GET", "httpurl": "/customers/12345?expand=orders", "httpstatus": 200, "success": true, "ipaddress": "203.0.113.7", "useragent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...", "country": "US", "region": "Washington", "city": "Seattle" } ], "pagination": { "limit": 20, "offset": 0, "total": 1, "hasMore": false } }

Two endpoints are available:

  • Query events — GET /v1/audit-logs/{bucketId}/events returns matching events, filterable by type, actorSub, subject, and time range.
  • Aggregated stats — GET /v1/audit-logs/{bucketId}/stats returns top-N event counts grouped by event type or actor.

The bucketId path parameter takes the bucket ID; these endpoints don't accept a bucket name. Each query is limited to a 30-day window between startDate and endDate. See the Audit Logs API reference for all parameters and response schemas.

Exporting audit logs to a SIEM

Many organizations centralize audit trails in a SIEM (Splunk, Microsoft Sentinel, Datadog Cloud SIEM, and so on) to correlate API activity with other security signals, drive alerting, and meet long-term retention requirements. The events API is designed for this: run a scheduled job that pulls new events since its last checkpoint and forwards them to your SIEM.

TypeScriptCode
const BASE_URL = "https://dev.zuplo.com/v1/audit-logs"; // Run on a schedule (e.g. every 5 minutes). `startDate` is the checkpoint // saved by the previous run; `endDate` is the current time. async function exportAuditLogs( bucketId: string, startDate: string, endDate: string, ) { let offset = 0; let hasMore = true; while (hasMore) { const url = new URL(`${BASE_URL}/${bucketId}/events`); url.searchParams.set("startDate", startDate); url.searchParams.set("endDate", endDate); url.searchParams.set("limit", "100"); url.searchParams.set("offset", String(offset)); const response = await fetch(url, { headers: { Authorization: `Bearer ${process.env.ZUPLO_API_KEY}` }, }); const { data, pagination } = await response.json(); await sendToSiem(data); // e.g. Splunk HEC, Datadog Logs API, S3 offset += data.length; hasMore = pagination.hasMore; } }

Because every event is a CloudEvents document, most SIEMs can ingest the payload directly without transformation.

Alternatively, if you'd rather send audit events to an external audit provider directly from the gateway instead of storing them in Zuplo, see Custom Audit Logs.

Last modified on