# Audit and Trace

Propagate request IDs end-to-end and correlate API activity, webhook deliveries, and the audit log.

<!-- docs-source -->
Source: https://learning.monetizekit.app/docs/guides/audit-trace
<!-- /docs-source -->

When a customer disputes a charge, an integration silently breaks, or a compliance reviewer asks "who changed this and when," you need a chain of evidence that spans your logs, MonetizeKit's API responses, and the workspace's Audit Log. Every layer of that chain is connected by IDs — this guide shows how to propagate and use them.

## Every response carries a request ID

Every REST response — success or error — includes an `X-Request-Id` header. Supply your own value and it's echoed back exactly; omit it and MonetizeKit generates a UUID v4 for you. Log this value at the call site so it's the join key between your application logs and any downstream investigation.

```bash
curl -i \
  -H "Authorization: Bearer $MONETIZEKIT_API_KEY" \
  "$MONETIZEKIT_BASE_URL/plans?page=1&pageSize=1" \
  | rg -i "^x-request-id:"
# X-Request-Id: 7c2f6e1a-0e3b-4b9a-9a9f-8e2c9e6f0d21
```

```bash
curl -i \
  -H "Authorization: Bearer $MONETIZEKIT_API_KEY" \
  -H "X-Request-Id: my-trace-9f2c1a" \
  "$MONETIZEKIT_BASE_URL/plans?page=1&pageSize=1" \
  | rg -i "^x-request-id:"
# X-Request-Id: my-trace-9f2c1a  -- echoed back exactly as sent
```

```javascript
const requestId = `svc-${crypto.randomUUID()}`;

const response = await fetch(`${baseUrl}/plans?page=1&pageSize=1`, {
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "X-Request-Id": requestId,
  },
});

logger.info("monetizekit_call", { requestId, status: response.status });
```

> [!TIP] Generate one ID per logical operation, not per HTTP call
>
> If a single user action fans out into three API calls (check an entitlement, record usage, create a subscription), give all three the same `X-Request-Id` prefix or a shared correlation field in your own logs. Tracing a single MonetizeKit request ID only gets you one leg of the journey.

## Error responses include the same ID

Every error envelope — regardless of status code — nests `code`, `message`, and `request_id` under a single top-level `error` object (plus `details` for field-level validation errors). When a customer reports a failure, ask for `error.request_id` first; it's the fastest way to pull the exact request from logs without guessing at timestamps. Cross-reference `error.code` against the [Error Codes reference](/docs/troubleshooting/error-codes) for known causes and resolutions before escalating.

```json
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Customer not found: cust_dev_9999",
    "request_id": "7c2f6e1a-0e3b-4b9a-9a9f-8e2c9e6f0d21"
  }
}
```

## Correlate with the Audit Log

The workspace Audit Log (Enterprise mode, under `/audit` in the dashboard) is the compliance-grade record of security-relevant actions: CRUD on governed resources, authentication events, approval decisions, and data exports. Each entry records an actor, an actor type, an IP address, the affected resource, and free-form metadata — cross-reference the metadata against the request IDs and delivery IDs you logged on your side.

### Action categories

| Action | Meaning |
| --- | --- |
| `create / update / delete` | CRUD on a governed resource (contract, plan, API key, customer, workspace, etc.). |
| `login / logout` | Session-level authentication events for dashboard users. |
| `approve / reject` | Decisions on an approval workflow (e.g. a contract or override request). |
| `transfer` | Ownership changes — workspace transfer, credit wallet reassignment. |
| `read / export` | Sensitive reads and data exports, including audit log CSV exports themselves. |

### Actor types

| Actor | Meaning |
| --- | --- |
| `user` | A dashboard user acting through the UI. |
| `api_key` | A REST/GraphQL call authenticated with a service token. |
| `webhook` | An inbound integration (e.g. a Stripe webhook) that triggered a state change. |
| `system` | An automated internal process (scheduled jobs, reconciliation). |
| `integration` | A connected third-party integration acting on the workspace's behalf. |

## Correlate webhook deliveries

Webhook-triggered changes appear in the Audit Log with `actorType: "webhook"`. Log the delivery's `X-MonetizeKit-Delivery` ID (see the [Webhooks guide](/docs/guides/webhooks) ) alongside the audit entry's timestamp and resource ID — together they let you prove exactly which delivery attempt caused which downstream state change, which is essential when disputing whether an event was processed once or twice. The same correlation strategy applies to disputed usage events — see the [Usage Metering Best Practices guide](/docs/guides/metering) for the reconciliation job that catches drift before an invoice does.

## Exporting for external review

Pull audit entries programmatically with `GET /audit` (scope `audit:view`) — the same API-key auth, rate limiting, and `X-Request-Id`/error-envelope handling as every other REST call in this guide.

```bash
curl -s \
  -H "Authorization: Bearer $MONETIZEKIT_API_KEY" \
  "$MONETIZEKIT_BASE_URL/audit"
# { "data": [ { "id": "...", "timestamp": "...", "actor": "...", "actorType": "...",
#              "actorIp": "...", "action": "...", "resource": "...", "resourceId": "...",
#              "description": "...", "metadata": { ... } }, ... ] }
```

### REST API (programmatic, most recent 500)

`GET /audit` returns the workspace's 500 most recent entries, newest first — there's no query-param filtering or pagination yet, so page further back or filter by actor/action/date in the dashboard instead. It isn't listed in the [API Reference](/docs/api-reference) explorer because of that limited surface, but the endpoint itself is fully functional today.

### CSV export (bulk, filtered)

The Audit Log page's "Export CSV" action exports the currently filtered entries — narrow the filters first (actor, action, date range) so the export matches the scope of your review, then hand the file to a compliance team or archive it externally.

> [!NOTE] No self-serve SIEM streaming yet
>
> If your compliance workflow needs continuous, real-time ingestion into a SIEM rather than periodic polling of `GET /audit` or manual CSV export, that's not a built-in feature today — drive interim alerting off request IDs and webhook delivery correlation as described above.

## Common pitfalls

### Not logging the request ID at the call site

If you only discover you need a request ID after an incident, it's too late — log it on every call, success and failure alike, from day one.

### Reusing one request ID across retries

A retried call is a distinct request. Reusing the same `X-Request-Id` across retries makes it impossible to tell, from the ID alone, whether a request succeeded on the first or third attempt.

### Exporting unfiltered audit data

CSV exports are themselves an `export` action recorded in the log. Filter to the minimum necessary scope before exporting, both for review clarity and for data-minimization compliance requirements.

### Assuming the audit API paginates

`GET /audit` has no `page`/`cursor` params — it always returns the 500 most recent entries. Don't build automation that assumes it can page through the full history; use the dashboard's date-range filters and CSV export for anything older.

## FAQ

### Can I set my own X-Request-Id and have it accepted?

Yes — any non-empty string is accepted and echoed back verbatim. If you omit the header, MonetizeKit generates a UUID v4 for you and returns it, so the header is always present either way.

### Does the audit log capture read-only API calls?

Sensitive reads (like revealing a webhook signing secret) are recorded as `read` actions. Routine, high-volume reads (entitlement checks, usage lookups) are not, to keep the log focused on security- and governance-relevant events rather than every API call.

### How long is audit log data retained?

It's a per-workspace setting (Settings → Security → `auditRetentionDays`), defaulting to 365 days if unset. Whatever you configure is clamped to between 90 and 730 days — export data you need to retain longer than that via CSV export rather than assuming indefinite retention.

## Related guides

### Webhook Verification and Retries

Delivery IDs are the join key for webhook-triggered audit entries.

[Open guide](/docs/guides/webhooks)

### Migration Guide

Use request IDs to isolate errors introduced during a migration rollout.

[Open guide](/docs/guides/migration)

### Entitlement Evaluation Patterns

Overrides are auditable exceptions — see how they resolve.

[Open guide](/docs/guides/entitlement-patterns)