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.