Plaidly Docs

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.

FieldRequiredNotes
event_idYesStable unique id for the audit record.
event_nameYesCanonical name such as audit.payments.settled.
event_versionYesIncrement only when the schema changes.
occurred_atYesWhen the underlying action happened.
recorded_atYesWhen the audit log persisted the record.
environmentYessandbox or live.
merchant_idYesMerchant scope for the action.
merchant_nameNoHuman-readable merchant label. Use for display only.
actor_typeYesmerchant_user, merchant_service, agent, system, or support.
actor_idNoStable id for the actor when one exists.
actor_nameNoDisplay-only actor label.
agent_idNoRequired when an agent initiated or approved the action.
request_idNoAPI request correlation id.
session_idNoPayment session correlation id.
wallet_idNoWallet correlation id.
payout_idNoPayout correlation id.
webhook_idNoWebhook endpoint correlation id.
delivery_idNoIndividual webhook delivery correlation id.
chainNoChain involved in the action.
networkNotestnet, mainnet, or chain-specific network label.
tokenNoToken symbol when relevant.
resultYessuccess, failure, pending, approved, or rejected.
reason_codeNoShort machine-readable explanation for the outcome.
correlation_idNoLink related records that share the same workflow.
redaction_levelYesnone, masked, or strict.
payload_hashYesDigest 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_type
  • credential_id
  • credential_fingerprint
  • operation
  • rotation_reason or revocation_reason

Event names:

  • audit.credentials.created
  • audit.credentials.rotated
  • audit.credentials.revoked
  • audit.credentials.exported

Payments

Use for session creation, payment detection, settlement, receipt issuance, and failure handling.

Required fields:

  • payment_id
  • session_id
  • merchant_id
  • amount
  • currency
  • chain
  • network
  • payment_status
  • tx_hash when the transaction exists

Event names:

  • audit.payments.created
  • audit.payments.detected
  • audit.payments.settled
  • audit.payments.failed
  • audit.payments.refunded

Wallets

Use for wallet creation, rotation, address assignment, balance changes, and sweep activity.

Required fields:

  • wallet_id
  • wallet_role
  • chain
  • network
  • address
  • balance_before
  • balance_after

Event names:

  • audit.wallets.created
  • audit.wallets.rotated
  • audit.wallets.credited
  • audit.wallets.debited
  • audit.wallets.swept

Spend decisions

Use for approval, rejection, policy escalation, or risk-based routing decisions before funds move.

Required fields:

  • decision_id
  • policy_id
  • decision
  • score
  • threshold
  • merchant_id
  • agent_id when an agent requested or approved the decision

Event names:

  • audit.spend.reviewed
  • audit.spend.approved
  • audit.spend.rejected
  • audit.spend.escalated

Webhooks

Use for endpoint registration, signature verification, delivery attempts, retries, and terminal failures.

Required fields:

  • webhook_id
  • delivery_id
  • event_name
  • attempt
  • status_code
  • signature_verified
  • retry_after

Event names:

  • audit.webhooks.registered
  • audit.webhooks.delivered
  • audit.webhooks.retried
  • audit.webhooks.failed
  • audit.webhooks.disabled

Payouts

Use for payout requests, approval steps, transfer submission, completion, and failures.

Required fields:

  • payout_id
  • destination_address
  • amount
  • token
  • chain
  • network
  • status
  • tx_hash when available

Event names:

  • audit.payouts.requested
  • audit.payouts.approved
  • audit.payouts.sent
  • audit.payouts.failed
  • audit.payouts.cancelled

Actor, merchant, and agent fields

  • merchant_id is the primary tenant key and is required for every merchant-scoped event.
  • merchant_name is display-only and may change; do not use it as the lookup key.
  • actor_type identifies the initiator of the action, not the resource being changed.
  • actor_id is the stable id for the human, service account, or support user behind the action.
  • agent_id is 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_hash for 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_fields when 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, or payouts
  • use a stable business verb that matches the record, such as created, rotated, credited, debited, settled, delivered, retried, failed, revoked, cancelled, exported, registered, or disabled
  • keep the same name for the same business event across sandbox and live
  • add event_version when 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 keyUse it for
merchant_idTenant-wide incident review
agent_idAgent policy and approval traces
wallet_idWallet funding, sweep, and rotation traces
session_idPayment lifecycle review
payout_idOutbound transfer review
webhook_id / delivery_idDelivery and retry review
request_idAPI request correlation
event_nameSurface-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"
}

On this page