Audit Log Events
Canonical audit event shape, redaction rules, and retention policy for Plaidly operations
Audit Log Events
Use this page for BDT-315. It defines the immutable audit event contract for credentials, payments, wallets, spend decisions, webhooks, payouts, and export activity.
Contract goals
- append-only records only
- searchable by merchant, actor, agent, wallet, payout, session, and request
- safe for support, incident response, and compliance review
- stable event names across product surfaces
Event envelope
Every record uses the same envelope, even when the surface-specific payload changes.
| Field | Required | Notes |
|---|---|---|
event_id | Yes | Stable unique id for the audit record. |
event_name | Yes | Canonical name such as audit.payments.settled. |
event_version | Yes | Increment only when the schema changes. |
occurred_at | Yes | When the underlying action happened. |
recorded_at | Yes | When the audit log persisted the record. |
environment | Yes | sandbox or live. |
merchant_id | Yes | Merchant scope for the action. |
merchant_name | No | Human-readable merchant label. Use for display only. |
actor_type | Yes | merchant_user, merchant_service, agent, system, or support. |
actor_id | No | Stable id for the actor when one exists. |
actor_name | No | Display-only actor label. |
agent_id | No | Required when an agent initiated or approved the action. |
request_id | No | API request correlation id. |
session_id | No | Payment session correlation id. |
wallet_id | No | Wallet correlation id. |
payout_id | No | Payout correlation id. |
webhook_id | No | Webhook endpoint correlation id. |
delivery_id | No | Individual webhook delivery correlation id. |
chain | No | Chain involved in the action. |
network | No | testnet, mainnet, or chain-specific network label. |
token | No | Token symbol when relevant. |
result | Yes | success, failure, pending, approved, or rejected. |
reason_code | No | Short machine-readable explanation for the outcome. |
correlation_id | No | Link related records that share the same workflow. |
redaction_level | Yes | none, masked, or strict. |
payload_hash | Yes | Digest of the original payload or canonicalized event body. |
Surface contracts
Credentials
Use for merchant API keys, webhook secrets, signing keys, and any credential rotation or revocation flow.
Required fields:
credential_typecredential_idcredential_fingerprintoperationrotation_reasonorrevocation_reason
Event names:
audit.credentials.createdaudit.credentials.rotatedaudit.credentials.revokedaudit.credentials.exported
Payments
Use for session creation, payment detection, settlement, receipt issuance, and failure handling.
Required fields:
payment_idsession_idmerchant_idamountcurrencychainnetworkpayment_statustx_hashwhen the transaction exists
Event names:
audit.payments.createdaudit.payments.detectedaudit.payments.settledaudit.payments.failedaudit.payments.refunded
Wallets
Use for wallet creation, rotation, address assignment, balance changes, and sweep activity.
Required fields:
wallet_idwallet_rolechainnetworkaddressbalance_beforebalance_after
Event names:
audit.wallets.createdaudit.wallets.rotatedaudit.wallets.creditedaudit.wallets.debitedaudit.wallets.swept
Spend decisions
Use for approval, rejection, policy escalation, or risk-based routing decisions before funds move.
Required fields:
decision_idpolicy_iddecisionscorethresholdmerchant_idagent_idwhen an agent requested or approved the decision
Event names:
audit.spend.reviewedaudit.spend.approvedaudit.spend.rejectedaudit.spend.escalated
Webhooks
Use for endpoint registration, signature verification, delivery attempts, retries, and terminal failures.
Required fields:
webhook_iddelivery_idevent_nameattemptstatus_codesignature_verifiedretry_after
Event names:
audit.webhooks.registeredaudit.webhooks.deliveredaudit.webhooks.retriedaudit.webhooks.failedaudit.webhooks.disabled
Payouts
Use for payout requests, approval steps, transfer submission, completion, and failures.
Required fields:
payout_iddestination_addressamounttokenchainnetworkstatustx_hashwhen available
Event names:
audit.payouts.requestedaudit.payouts.approvedaudit.payouts.sentaudit.payouts.failedaudit.payouts.cancelled
Actor, merchant, and agent fields
merchant_idis the primary tenant key and is required for every merchant-scoped event.merchant_nameis display-only and may change; do not use it as the lookup key.actor_typeidentifies the initiator of the action, not the resource being changed.actor_idis the stable id for the human, service account, or support user behind the action.agent_idis only present when automation or an external agent is part of the workflow.- If both an actor and an agent exist, record both. The actor is who pressed the button; the agent is what drove the automation.
Redaction rules
Never store these values in clear text:
- API keys
- webhook secrets
- HMAC signatures
- private keys
- seed phrases
- raw request bodies
- raw response bodies
- access tokens
- signing material used for payouts or wallet control
Store these as fingerprints or masked values instead:
- credential ids plus
credential_fingerprint body_hashfor payload integrity checks- addresses as full values in the secure audit log, but masked in exported or support-facing views
- wallet public keys, tx hashes, and payout hashes only when they are already public on-chain identifiers
For structured masking, use the same rule everywhere:
- prefix plus suffix for ids and addresses, such as
abc123...wxyz - hash-only storage for secrets, such as
sha256:<hex> - explicit
redacted_fieldswhen any payload keys are dropped
Do not store model prompts, hidden reasoning, or vendor tokens in spend decision records. Record only the policy inputs, numeric score, decision, and reason code.
Event naming
Use a dot-separated naming scheme:
audit.<surface>.<verb>
Rules:
- use one of the approved surfaces:
credentials,payments,wallets,spend,webhooks, orpayouts - use a stable business verb that matches the record, such as
created,rotated,credited,debited,settled,delivered,retried,failed,revoked,cancelled,exported,registered, ordisabled - keep the same name for the same business event across sandbox and live
- add
event_versionwhen the envelope or required payload shape changes
Query keys
When support or automation needs to find an audit record, search by the narrowest stable key available.
| Lookup key | Use it for |
|---|---|
merchant_id | Tenant-wide incident review |
agent_id | Agent policy and approval traces |
wallet_id | Wallet funding, sweep, and rotation traces |
session_id | Payment lifecycle review |
payout_id | Outbound transfer review |
webhook_id / delivery_id | Delivery and retry review |
request_id | API request correlation |
event_name | Surface-specific event search |
Retention
The retention contract is:
- hot searchable storage for 90 days
- immutable archive for at least 12 months
- legal-hold preservation until the hold is released
- tombstones for deletions or redactions, never silent removal
If compliance or customer contracts require longer retention, extend the archive tier without changing the event shape.
Example records
Credential rotation
{
"event_id": "aud_01J4CREDENTIAL",
"event_name": "audit.credentials.rotated",
"event_version": 1,
"occurred_at": "2026-06-14T12:05:00Z",
"recorded_at": "2026-06-14T12:05:01Z",
"environment": "live",
"merchant_id": "mer_123",
"merchant_name": "Acme Test Co",
"actor_type": "merchant_user",
"actor_id": "usr_456",
"request_id": "req_789",
"redaction_level": "strict",
"payload_hash": "sha256:1a2b3c4d",
"credential_type": "api_key",
"credential_id": "cred_001",
"credential_fingerprint": "sha256:9f8e7d6c",
"operation": "rotate",
"rotation_reason": "routine"
}Payment settlement
{
"event_id": "aud_01JPAYMENT",
"event_name": "audit.payments.settled",
"event_version": 1,
"occurred_at": "2026-06-14T12:10:00Z",
"recorded_at": "2026-06-14T12:10:03Z",
"environment": "sandbox",
"merchant_id": "mer_123",
"actor_type": "system",
"session_id": "sess_abc123",
"payment_id": "pay_001",
"chain": "solana",
"network": "testnet",
"amount": 25,
"currency": "USDC",
"payment_status": "settled",
"tx_hash": "5Q3u...vX9",
"result": "success",
"redaction_level": "masked",
"payload_hash": "sha256:2b3c4d5e"
}Spend approval
{
"event_id": "aud_01JSPEND",
"event_name": "audit.spend.approved",
"event_version": 1,
"occurred_at": "2026-06-14T12:15:00Z",
"recorded_at": "2026-06-14T12:15:00Z",
"environment": "live",
"merchant_id": "mer_123",
"actor_type": "agent",
"actor_id": "agt_456",
"agent_id": "agt_456",
"decision_id": "dec_001",
"policy_id": "pol_10",
"decision": "approved",
"score": 12,
"threshold": 20,
"reason_code": "below_threshold",
"result": "approved",
"redaction_level": "strict",
"payload_hash": "sha256:3c4d5e6f"
}Webhook failure
{
"event_id": "aud_01JWEBHOOK",
"event_name": "audit.webhooks.failed",
"event_version": 1,
"occurred_at": "2026-06-14T12:20:00Z",
"recorded_at": "2026-06-14T12:20:02Z",
"environment": "live",
"merchant_id": "mer_123",
"actor_type": "system",
"webhook_id": "wh_001",
"delivery_id": "wd_999",
"webhook_event_type": "payment_session.completed",
"attempt": 3,
"status_code": 503,
"signature_verified": true,
"retry_after": "60s",
"result": "failure",
"reason_code": "endpoint_5xx",
"redaction_level": "strict",
"payload_hash": "sha256:4d5e6f70"
}Payout request
{
"event_id": "aud_01JPAYOUT",
"event_name": "audit.payouts.requested",
"event_version": 1,
"occurred_at": "2026-06-14T12:25:00Z",
"recorded_at": "2026-06-14T12:25:00Z",
"environment": "live",
"merchant_id": "mer_123",
"actor_type": "merchant_service",
"actor_id": "svc_777",
"payout_id": "pyt_001",
"destination_address": "0x1234...beef",
"amount": 1250,
"token": "USDC",
"chain": "ethereum",
"network": "mainnet",
"status": "requested",
"result": "pending",
"redaction_level": "masked",
"payload_hash": "sha256:5e6f7081"
}