ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference
Introduction
Getting Started
    Develop on the web portal
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
    Develop locally with the CLI
      1 - Setup Your Gateway2 - Rate Limiting3 - API Key Auth4 - Deploy5 - Dynamic Rate LimitingDynamic MCP Server - Quickstart
Concepts
Development
Policies
Handlers
API Keys
Rate Limiting
MCP Server
MCP Gateway
AI Gateway
Developer Portal
Monetization
GraphQL
Deploying & Source Control
Analytics
Observability
    Audit Logs
      OverviewZuplo Audit LogsCustom Audit Logs
    Logging
    Data & Security
    Metrics PluginsOpenTelemetryProactive monitoring
    Guides
Networking & Infrastructure
Account Management
Programming API
Build with AI
Zuplo CLI
Migration Guides
Platform LimitsSecuritySupportTrust & ComplianceChangelog
powered by Zudoku
Audit Logs

Custom Audit Logs

Zuplo Audit Logs stores your audit trail in Zuplo and gives you a portal viewer and query API out of the box. If your organization uses an external audit logging service instead, you can send events to it directly from the gateway with a custom policy that uses runtime hooks. This approach works on any plan — it's just custom code — and gives you full control over which events are recorded, the event format, and the destination.

Example: WorkOS Audit Logs

WorkOS provides various services that help enable enterprise features on your service such as SSO and Audit Logs. The following custom policy logs API calls to the WorkOS Audit Logs API after each response is sent:

Code
import { ZuploContext, ZuploRequest, environment } from "@zuplo/runtime"; export async function auditLogPlugin( request: ZuploRequest, context: ZuploContext, policyName: string, ) { // Clone the request so the body can be read in the hook // note: remove this if you don't need content from the body const cloned = request.clone(); context.addResponseSendingFinalHook(async (response) => { const incomingBody = await cloned.json(); // This is an example event. Extract any additional data needed from the // request. const body = { organization_id: "org_01EHWNCE74X7JSDV0X3SZ3KJNY", event: { action: "user.signed_in", occurred_at: "2022-09-02T16:35:39.317Z", version: 1, actor: { type: "user", // Use the user sub for authenticated users id: request.user.sub, metadata: { role: "user", }, }, targets: [ { type: "user", id: "user_98432YHF", name: "Jon Smith", }, { type: "team", id: "team_J8YASKA2", metadata: { owner: "user_01GBTCQ2", }, }, ], context: { location: request.headers.get("True-Client-IP"), user_agent: request.headers.get("User-Agent"), }, metadata: { extra: incomingBody.extra, }, }, }; await fetch("https://api.workos.com/audit_logs/events", { method: "POST", body: JSON.stringify(body), headers: { Authorization: `Bearer ${environment.WORKOS_API_KEY}`, "Content-Type": "application/json", // Optional idempotency key. // See: https://workos.com/docs/reference/idempotency // "Idempotency-Key": "YOUR_KEY_HERE" }, }); }); return request; }

The same pattern works for any provider with an HTTP API — swap the event shape and endpoint for your provider's, and store credentials in environment variables.

Related resources

  • Audit logging overview — why audit logging matters and how the options compare
  • Zuplo Audit Logs — the built-in feature with portal viewing and a query API
Edit this page
Last modified on July 9, 2026
Zuplo Audit LogsOverview
On this page
  • Example: WorkOS Audit Logs
  • Related resources
TypeScript