The audit trail.
Every mutation on your workspace writes an append-only audit row. Read it for support, export it for SOC2, query it for incident response.
1
What gets captured
The workspace audit log records:
- Member lifecycle — invites created / accepted / revoked, members added / removed / role-changed.
- Role + policy mutations — custom role create / update / delete, managed policy bindings.
- Service activation — services enabled / disabled, settings changes.
- PAT lifecycle — mints, rotations, revokes, policy edits.
- Workspace mutations — updates to display name, slug renames (admin-only), deletion.
- Workspace deletion — the last row is the deletion itself; everything before is preserved per legal- hold rules (see Section 6).
What's NOT in workspace audit: per- service mutations live in the service's own audit log. Auth app changes → Auth's audit_log. Mail message-log entries → Mail's message log. Social moderation actions → Social's moderation audit. C-6 in the coherence review notes the dispersion; a unified feed is a planned follow-up.
2
Read it
curl 'https://api.platform-auth.productcraft.co/v1/workspaces/acme/audit-logs?limit=50' \
-H 'authorization: Bearer <cookie-or-pak>'{
"data": [
{
"id": "...",
"action": "workspace.member.role_changed",
"actor_id": "<owner-account>",
"actor_type": "platform_user",
"resource": "workspace_member",
"resource_id": "<member-account>",
"metadata": { "from_role": "admin", "to_role": "owner" },
"ip": "203.0.113.4",
"created_at": "2026-05-11T..."
},
{
"id": "...",
"action": "workspace.api_key.created",
"actor_id": "<owner-account>",
"actor_type": "platform_user",
"resource": "platform_api_key",
"resource_id": "<pak-id>",
"metadata": { "name": "github-actions-staging-deploy", "scopes_count": 5 },
"ip": "192.0.2.1",
"created_at": "2026-05-11T..."
}
],
"pagination": { "next_cursor": "...", "has_more": true }
}3
Query patterns
Common filters:
action=workspace.member.role_changed— every role change.actor_id=<pat-id>— what did this PAT do?since=2026-04-01T00:00:00Z&until=2026-04-30T...— a date range for the monthly compliance review.resource=platform_api_key— every PAT- related action.- Combine:
action=workspace.api_key.* & since=...for “all PAT lifecycle in the last quarter.”
4
Export for compliance
For SOC2 / ISO27001 / annual review-style asks, paginate through the full log + cache to your own object storage. Per-call cap: 1000 rows. The cursor-paginated stream covers an arbitrary date range; throttled to a couple requests/sec so a single tab doesn't hammer.
#!/usr/bin/env bash
set -euo pipefail
WS=acme
SINCE="2026-01-01T00:00:00Z"
UNTIL="2026-12-31T23:59:59Z"
PAT="${PRODUCTCRAFT_PAT}"
CURSOR=""
PAGE=0
> audit-export.jsonl
while :; do
RESP=$(curl -fsS "https://api.platform-auth.productcraft.co/v1/workspaces/$WS/audit-logs?since=$SINCE&until=$UNTIL&limit=1000&cursor=$CURSOR" \
-H "authorization: Bearer $PAT")
echo "$RESP" | jq -c '.data[]' >> audit-export.jsonl
HAS_MORE=$(echo "$RESP" | jq -r '.pagination.has_more')
if [[ "$HAS_MORE" != "true" ]]; then break; fi
CURSOR=$(echo "$RESP" | jq -r '.pagination.next_cursor')
PAGE=$((PAGE+1))
echo "page $PAGE complete"
sleep 0.5
done
echo "Done. $(wc -l < audit-export.jsonl) rows."5
Permission-denial audit
A subtle but useful capture: every 403 from the workspace-policy gate writes an audit row. Useful for spotting under-scoped PATs.
action=authz.denied rows include the requested action + resource URN + which policy refused it. Frequent denials on the same PAT usually mean it needs to be re-scoped (or that someone is fishing for permissions they shouldn't have).
6
Retention + workspace deletion
Audit rows are append-only. We don't expose a delete endpoint, and operator-side bulk deletion requires a legal-hold-style customer-support ticket.
When you delete a workspace (Chapter 1), the audit rows are purged 30 days after the deletion event — that retention window covers most regulator+customer dispute windows. For longer retention, export before deletion.
7
Common compliance asks + how to answer them
- “Who has access to our workspace?” — list members + PATs, plus their roles + scopes. Console renders this; API:
GET .../members+GET .../api-keys. - “Show me every action taken by the user we just offboarded.” —
?actor_id=<account>. Plus their PATs: filterworkspace.api_key.created+ byactor_idto find which they minted. - “What changed in the last 30 days?” —
?since=<30d-ago>. Group byactionfor the executive view, byactor_idfor the per-person view. - “Prove we revoked the leaked PAT.” —
?action=workspace.api_key.revoked&resource_id=<pat-id>. The row carries timestamp + actor + the IP that did the revoke.
What's next
You've finished the series
From here:
- Full API reference for every endpoint with schemas
- Auth guides for the end-user identity layer
- Mail guides for sender configuration + deliverability