Organization Audit Logs
Comprehensive API for accessing organization audit logs, providing detailed tracking of all administrative actions for compliance, security, and governance.
Overview
Organization audit logs track all administrative actions within an organization, providing a complete audit trail for:
- Compliance: Meet regulatory requirements (SOC 2, GDPR, HIPAA)
- Security: Detect and investigate suspicious activities
- Governance: Monitor who did what and when
- Debugging: Troubleshoot configuration changes and issues
- Accountability: Maintain transparency in team operations
Each audit log entry includes:
- Actor: User who performed the action
- Action: Type of event (e.g.,
service.created,user.invited) - Target: Resource affected (e.g., service, user, organization)
- Timestamp: When the action occurred
- Details: Additional context (JSON)
- Success: Whether the action succeeded
- IP Address: Client IP (if available)
- User Agent: Client browser/tool (if available)
Data Models
Audit Log Entry
{
"id": "uuid",
"org_id": "uuid",
"actor_user_id": "uuid",
"actor_email": "admin@acme.com",
"action": "service.created",
"target_type": "service",
"target_id": "service-uuid",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0...",
"success": true,
"details": {
"service_slug": "main-app",
"service_name": "Main Application",
"service_type": "web",
"client_id": "client-uuid"
},
"created_at": "2025-01-15T10:30:00Z"
}
Audit Log Response with User
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"org_id": "org-uuid",
"actor": {
"id": "user-uuid",
"email": "admin@acme.com",
"is_platform_owner": false
},
"action": "service.created",
"target_type": "service",
"target_id": "service-uuid",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"success": true,
"details": {
"service_slug": "main-app",
"service_name": "Main Application",
"service_type": "web",
"client_id": "client-uuid"
},
"created_at": "2025-01-15T10:30:00Z"
}
Pagination Info
{
"page": 1,
"limit": 50,
"total": 1250,
"total_pages": 25,
"has_next": true,
"has_prev": false
}
Audit Event Types
For a complete reference of all audit events including triggering actions and details payload schemas, see Audit Events Reference.
User Management Events
| Event | Description | Target Type |
|---|---|---|
user.invited |
User invited to organization | user |
user.joined |
User accepted invitation | user |
user.removed |
User removed from organization | user |
user.role_updated |
User role changed (owner/admin/member) | user |
Service Management Events
| Event | Description | Target Type |
|---|---|---|
service.created |
New service created | service |
service.updated |
Service configuration updated | service |
service.deleted |
Service deleted | service |
service.oauth_credentials.updated |
OAuth credentials configured | service |
Organization Management Events
| Event | Description | Target Type |
|---|---|---|
organization.updated |
Organization details updated | organization |
organization.smtp.configured |
SMTP settings configured | organization |
organization.smtp.removed |
SMTP settings removed | organization |
Plan Management Events
| Event | Description | Target Type |
|---|---|---|
plan.created |
Subscription plan created | plan |
plan.updated |
Plan details updated | plan |
plan.deleted |
Plan deleted | plan |
Subscription Management Events
| Event | Description | Target Type |
|---|---|---|
subscription.created |
User subscribed to plan | subscription |
subscription.updated |
Subscription modified | subscription |
subscription.canceled |
Subscription canceled | subscription |
Invitation Management Events
| Event | Description | Target Type |
|---|---|---|
invitation.accepted |
Invitation accepted | invitation |
invitation.declined |
Invitation declined | invitation |
invitation.expired |
Invitation expired | invitation |
invitation.revoked |
Invitation revoked | invitation |
Security Events
| Event | Description | Target Type |
|---|---|---|
security.mfa.enabled |
MFA enabled | user |
security.mfa.disabled |
MFA disabled | user |
security.password.changed |
Password changed | user |
API Key Management Events
| Event | Description | Target Type |
|---|---|---|
api_key.created |
API key created | api_key |
api_key.deleted |
API key deleted | api_key |
Custom Domains & Branding Events
| Event | Description | Target Type |
|---|---|---|
domain.set |
Custom domain set | organization |
domain.verified |
Custom domain verified | organization |
domain.deleted |
Custom domain removed | organization |
branding.updated |
Branding settings updated | organization |
Endpoints Summary
| Method | Path | Description | Permissions |
|---|---|---|---|
| GET | /api/organizations/:org_slug/audit-log |
Get audit logs | Owner/Admin |
| GET | /api/organizations/:org_slug/audit-log/event-types |
Get available event types | Owner/Admin |
Audit Log Operations
GET /api/organizations/:org_slug/audit-log
Retrieve paginated audit logs for an organization with optional filtering.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
page |
integer | No | Page number (default: 1, min: 1) |
limit |
integer | No | Items per page (default: 50, max: 100) |
action |
string | No | Filter by specific action (e.g., service.created) |
target_type |
string | No | Filter by target type (e.g., service, user) |
target_id |
string | No | Filter by specific target ID (requires target_type) |
Example Request (All Logs):
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?page=1&limit=50" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Request (Filter by Action):
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?action=service.created" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Request (Filter by Target):
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?target_type=service&target_id=service-uuid" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
{
"logs": [
{
"id": "log-uuid-1",
"org_id": "org-uuid",
"actor": {
"id": "user-uuid",
"email": "admin@acme.com",
"is_platform_owner": false
},
"action": "service.created",
"target_type": "service",
"target_id": "service-uuid",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"success": true,
"details": {
"service_slug": "main-app",
"service_name": "Main Application",
"service_type": "web",
"client_id": "client-uuid"
},
"created_at": "2025-01-15T10:30:00Z"
},
{
"id": "log-uuid-2",
"org_id": "org-uuid",
"actor": {
"id": "user-uuid",
"email": "admin@acme.com",
"is_platform_owner": false
},
"action": "user.invited",
"target_type": "user",
"target_id": "invited-user-uuid",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"success": true,
"details": {
"invited_email": "newuser@acme.com",
"role": "member"
},
"created_at": "2025-01-15T11:00:00Z"
},
{
"id": "log-uuid-3",
"org_id": "org-uuid",
"actor": {
"id": "user-uuid",
"email": "admin@acme.com",
"is_platform_owner": false
},
"action": "api_key.created",
"target_type": "api_key",
"target_id": "api-key-uuid",
"ip_address": "203.0.113.42",
"user_agent": "curl/7.79.1",
"success": true,
"details": {
"api_key_id": "api-key-uuid",
"service_id": "service-uuid",
"service_slug": "main-app",
"name": "Production Backend",
"permissions": ["read:users", "read:subscriptions"],
"expires_at": "2025-04-15T10:30:00Z"
},
"created_at": "2025-01-15T14:20:00Z"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 150,
"total_pages": 3,
"has_next": true,
"has_prev": false
}
}
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found500 Internal Server Error: Database error
Notes:
- Logs are returned in reverse chronological order (newest first)
- Actor information is populated with user details
detailsfield contains action-specific context (JSON)success: falseindicates failed actions (e.g., validation errors)- IP address and user agent may be null for background jobs
- Maximum 100 items per page
- Use
actionfilter for specific event types - Use
target_type+target_idto see all actions on a specific resource
Filtering Examples:
# All service-related events
GET /api/organizations/acme-corp/audit-log?target_type=service
# All actions by a specific user (as target)
GET /api/organizations/acme-corp/audit-log?target_type=user&target_id=user-uuid
# All failed actions
# (Note: filtering by success not currently supported - filter on client)
# Events from the last 7 days
# (Note: date filtering not currently supported - filter on client)
GET /api/organizations/:org_slug/audit-log/event-types
Get available audit event types for filtering.
Permissions: Owner or Admin
Headers:
| Header | Value |
|---|---|
Authorization |
Bearer {jwt} |
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
org_slug |
string | Organization slug |
Example Request:
curl -X GET https://sso.example.com/api/organizations/acme-corp/audit-log/event-types \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
Example Response (200 OK):
[
{
"value": "user.invited",
"label": "User Invited",
"category": "User Management"
},
{
"value": "user.joined",
"label": "User Joined",
"category": "User Management"
},
{
"value": "user.removed",
"label": "User Removed",
"category": "User Management"
},
{
"value": "user.role_updated",
"label": "User Role Updated",
"category": "User Management"
},
{
"value": "service.created",
"label": "Service Created",
"category": "Service Management"
},
{
"value": "service.updated",
"label": "Service Updated",
"category": "Service Management"
},
{
"value": "service.deleted",
"label": "Service Deleted",
"category": "Service Management"
},
{
"value": "service.oauth_credentials.updated",
"label": "Service OAuth Credentials Updated",
"category": "Service Management"
},
{
"value": "organization.updated",
"label": "Organization Updated",
"category": "Organization Management"
},
{
"value": "organization.smtp.configured",
"label": "Organization SMTP Configured",
"category": "Organization Management"
},
{
"value": "organization.smtp.removed",
"label": "Organization SMTP Removed",
"category": "Organization Management"
},
{
"value": "plan.created",
"label": "Plan Created",
"category": "Plan Management"
},
{
"value": "plan.updated",
"label": "Plan Updated",
"category": "Plan Management"
},
{
"value": "plan.deleted",
"label": "Plan Deleted",
"category": "Plan Management"
},
{
"value": "subscription.created",
"label": "Subscription Created",
"category": "Subscription Management"
},
{
"value": "subscription.updated",
"label": "Subscription Updated",
"category": "Subscription Management"
},
{
"value": "subscription.canceled",
"label": "Subscription Canceled",
"category": "Subscription Management"
},
{
"value": "invitation.accepted",
"label": "Invitation Accepted",
"category": "Invitation Management"
},
{
"value": "invitation.declined",
"label": "Invitation Declined",
"category": "Invitation Management"
},
{
"value": "invitation.expired",
"label": "Invitation Expired",
"category": "Invitation Management"
},
{
"value": "invitation.revoked",
"label": "Invitation Revoked",
"category": "Invitation Management"
},
{
"value": "security.mfa.enabled",
"label": "MFA Enabled",
"category": "Security"
},
{
"value": "security.mfa.disabled",
"label": "MFA Disabled",
"category": "Security"
},
{
"value": "security.password.changed",
"label": "Password Changed",
"category": "Security"
},
{
"value": "api_key.created",
"label": "API Key Created",
"category": "API Key Management"
},
{
"value": "api_key.deleted",
"label": "API Key Deleted",
"category": "API Key Management"
},
{
"value": "domain.set",
"label": "Domain Set",
"category": "Custom Domains & Branding"
},
{
"value": "domain.verified",
"label": "Domain Verified",
"category": "Custom Domains & Branding"
},
{
"value": "domain.deleted",
"label": "Domain Deleted",
"category": "Custom Domains & Branding"
},
{
"value": "branding.updated",
"label": "Branding Updated",
"category": "Custom Domains & Branding"
}
]
Error Responses:
401 Unauthorized: Invalid or missing JWT403 Forbidden: User is not an owner or admin404 Not Found: Organization not found (path parameter only, no other 404s)500 Internal Server Error: Database error
Notes:
- Returns all available event types with human-readable labels
- Categorized for UI organization (e.g., dropdown groups)
- Use
valuefield for filtering in/audit-logendpoint - Event types are static and defined in the API code
- New event types may be added in future versions
Common Use Cases
Security Investigation
Scenario: Investigate suspicious activity after detecting an anomaly.
# 1. Find all actions by a specific user
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?page=1&limit=100" \
-H "Authorization: Bearer ..." | jq '.logs[] | select(.actor.email == "suspect@acme.com")'
# 2. Check for service deletions
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?action=service.deleted" \
-H "Authorization: Bearer ..."
# 3. Review failed actions
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?page=1&limit=100" \
-H "Authorization: Bearer ..." | jq '.logs[] | select(.success == false)'
# 4. Check actions from unusual IP
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?page=1&limit=100" \
-H "Authorization: Bearer ..." | jq '.logs[] | select(.ip_address == "suspicious.ip.address")'
Compliance Audit
Scenario: Generate audit report for SOC 2 compliance.
# 1. Export all security events
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?page=1&limit=100" \
-H "Authorization: Bearer ..." | \
jq '.logs[] | select(.category == "Security")' > security_events.json
# 2. Export all user management events
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?target_type=user" \
-H "Authorization: Bearer ..." > user_management.json
# 3. Generate CSV report
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?page=1&limit=1000" \
-H "Authorization: Bearer ..." | \
jq -r '.logs[] | [.created_at, .actor.email, .action, .target_type, .target_id, .success] | @csv' \
> audit_report.csv
Configuration Change Tracking
Scenario: Track changes to a specific service.
# Get all events for a specific service
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?target_type=service&target_id=service-uuid" \
-H "Authorization: Bearer ..."
# Example response shows:
# - When service was created
# - All configuration updates
# - OAuth credential changes
# - API key creations/deletions
# - Plan additions
User Activity Report
Scenario: Review all actions performed by a team member before offboarding.
# 1. Get all audit logs
curl -X GET "https://sso.example.com/api/organizations/acme-corp/audit-log?page=1&limit=1000" \
-H "Authorization: Bearer ..."
# 2. Filter by actor email on client
jq '.logs[] | select(.actor.email == "leaving-employee@acme.com")' audit_logs.json
# 3. Group by action type
jq 'group_by(.action) | map({action: .[0].action, count: length})' filtered_logs.json
Security Considerations
Access Control:
- Only owners and admins can view audit logs
- Logs are scoped to organization (no cross-organization access)
- Deleted users still appear in logs (email preserved)
- Platform owners cannot access organization audit logs (privacy)
Data Retention:
- Audit logs are retained indefinitely by default
- Consider implementing retention policies for GDPR compliance
- Export logs regularly for archival
- Logs survive organization deletion (platform-level cleanup required)
Sensitive Information:
- Passwords and secrets are never logged
- OAuth tokens are never logged
- API keys are logged by ID only (not full key)
- Personal data (email) is logged for accountability
- Consider GDPR right to erasure when deleting users
Performance:
- Large organizations may have millions of log entries
- Use pagination to avoid timeouts
- Filter by action or target to reduce dataset size
- Consider exporting to external log management system (e.g., Splunk, Datadog)
Tamper Protection:
- Audit logs are append-only (cannot be modified)
- Deletions are not supported (immutable audit trail)
- Actor information captured at event time (not dynamic)
- Timestamps are server-side (cannot be spoofed)
Integration Examples
Export to Splunk
#!/bin/bash
# Export audit logs to Splunk
ORG_SLUG="acme-corp"
AUTH_TOKEN="your-jwt-token"
SPLUNK_URL="https://splunk.example.com:8088/services/collector"
SPLUNK_TOKEN="your-splunk-hec-token"
# Fetch logs
logs=$(curl -s -X GET "https://sso.example.com/api/organizations/$ORG_SLUG/audit-log?limit=100" \
-H "Authorization: Bearer $AUTH_TOKEN")
# Send to Splunk
echo "$logs" | jq -c '.logs[]' | while read log; do
curl -X POST "$SPLUNK_URL" \
-H "Authorization: Splunk $SPLUNK_TOKEN" \
-d "{\"event\": $log, \"sourcetype\": \"sso_audit_log\"}"
done
Slack Notifications
// Node.js: Send critical audit events to Slack
const axios = require('axios');
const CRITICAL_EVENTS = [
'service.deleted',
'user.removed',
'api_key.created',
'api_key.deleted',
'security.mfa.disabled'
];
async function pollAuditLogs() {
const response = await axios.get(
'https://sso.example.com/api/organizations/acme-corp/audit-log?limit=10',
{ headers: { Authorization: `Bearer ${process.env.SSO_JWT}` } }
);
for (const log of response.data.logs) {
if (CRITICAL_EVENTS.includes(log.action)) {
await sendSlackAlert(log);
}
}
}
async function sendSlackAlert(log) {
await axios.post(process.env.SLACK_WEBHOOK_URL, {
text: `=� Critical Action: ${log.action}`,
attachments: [{
color: 'danger',
fields: [
{ title: 'Actor', value: log.actor.email, short: true },
{ title: 'Target', value: log.target_type, short: true },
{ title: 'Time', value: log.created_at, short: true },
{ title: 'IP', value: log.ip_address || 'N/A', short: true }
]
}]
});
}
// Poll every 5 minutes
setInterval(pollAuditLogs, 5 * 60 * 1000);
Python Report Generator
import requests
import csv
from datetime import datetime
SSO_API = 'https://sso.example.com'
ORG_SLUG = 'acme-corp'
AUTH_TOKEN = 'your-jwt-token'
def export_audit_logs_to_csv(output_file='audit_logs.csv'):
"""Export all audit logs to CSV for compliance reporting."""
headers = {'Authorization': f'Bearer {AUTH_TOKEN}'}
page = 1
all_logs = []
# Paginate through all logs
while True:
response = requests.get(
f'{SSO_API}/api/organizations/{ORG_SLUG}/audit-log',
params={'page': page, 'limit': 100},
headers=headers
)
data = response.json()
all_logs.extend(data['logs'])
if not data['pagination']['has_next']:
break
page += 1
# Write to CSV
with open(output_file, 'w', newline='') as csvfile:
fieldnames = ['timestamp', 'actor_email', 'action', 'target_type',
'target_id', 'success', 'ip_address']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for log in all_logs:
writer.writerow({
'timestamp': log['created_at'],
'actor_email': log['actor']['email'],
'action': log['action'],
'target_type': log['target_type'],
'target_id': log['target_id'],
'success': log['success'],
'ip_address': log.get('ip_address', 'N/A')
})
print(f'Exported {len(all_logs)} audit logs to {output_file}')
if __name__ == '__main__':
export_audit_logs_to_csv()
Best Practices
Regular Review:
- Review audit logs weekly for unusual activity
- Set up automated alerts for critical events
- Export logs monthly for archival
- Investigate all failed actions
Filtering Strategies:
- Start broad, then narrow down with filters
- Use
actionfilter for specific event types - Use
target_type+target_idfor resource history - Export to external tools for advanced analytics
Compliance:
- Document audit log retention policy
- Export logs regularly for long-term storage
- Include audit logs in security audits
- Train team on audit log interpretation
Security Monitoring:
- Alert on critical events (service deletion, user removal)
- Monitor for unusual IP addresses
- Track failed actions for potential attacks
- Review API key creation/deletion regularly
Troubleshooting:
- Use audit logs to track configuration changes
- Compare timestamps with issue reports
- Check
detailsfield for context - Verify actor identity for change management