Skip to content
AM

The audit log (programmatic)

AuditCore is InBlack’s tenant-scoped, append-only event log. Its canonical envelope records what happened, which grammar validates the payload, who or what acted, and how the event relates to the rest of a workflow.

Event envelope

An event includes:

FieldPurpose
id and typeStable event identity and canonical event name
eventVersionVersion of the event contract
grammarRegistered payload grammar, such as accounting or commerce
source and sourceSystemProducing component and optional external system
payloadGrammar-validated event data
dimensionalTagsTenant-safe dimensions for filtering and analysis
principalChainOrdered user, agent, service, or system principals involved
occurredAt, receivedAtSource and ingestion timestamps
causalIdImmediate parent event
correlationIdIdentifier shared across a wider workflow
idempotencyKeyTenant-scoped retry deduplication key
sourceEventIdUpstream identifier, such as a provider webhook ID

Stored events also carry a gapless tenant sequence and SHA-256 row-hash chain. Sealed UTC days can be copied to a byte-faithful WORM archive and verified against the source chain.

Read events with GraphQL

The Accounting GraphQL API exposes the authenticated tenant-scoped query:

query AuditEvents($correlationId: String!) {
auditcoreEvents(correlationId: $correlationId, limit: 50) {
id
type
eventVersion
grammar
source
principalChain {
principalType
principalId
displayName
}
occurredAt
causalId
correlationId
}
}

Available filters are type, grammar, causalId, and correlationId, plus limit. The current MCP catalog does not expose an AuditCore search or event-detail tool.

Ingest an event

Most product code should emit audit events through its domain write path so the business change and audit behavior remain coordinated. Integrations that own a canonical event can use either authenticated ingestion surface:

  • GraphQL mutation ingestAuditcoreEvent(input: IngestAuditcoreEventInput!).
  • POST /api/auditcore/events on the Accounting API.

The REST body uses snake-case names:

{
"type": "commerce.order.imported",
"event_version": "1.0.0",
"grammar": "commerce",
"source": "warehouse-import",
"payload": {
"order_id": "8d5c7f41-2f80-4dda-882d-2ca4624471ee"
},
"dimensional_tags": {},
"principal_chain": [
{
"principal_type": "service",
"principal_id": "warehouse-import"
}
],
"occurred_at": "2026-07-10T12:00:00Z",
"idempotency_key": "warehouse-order-1847"
}

The caller must be authenticated and have tenant context. The service validates the envelope, registered grammar, payload, and principal chain. It also rejects event types reserved for system-owned producers. Invalid envelopes or grammar return 400; invalid payloads return 422; successful REST ingestion returns 201.

Causality and idempotency

Use causalId for the direct parent and correlationId for the complete workflow. For example, a provider webhook, its order projection, and a notification can share one correlation ID while each child points to its immediate cause.

Provide an idempotencyKey when ingestion may retry. Repeating the same tenant-scoped key returns the existing event rather than appending a duplicate.

Retention and archive status

The repository implements sealed-day WORM archive and verification, but it does not define a published customer retention duration or tier-specific retention schedule. Do not promise a number of years or a support-recovery window unless it is established in the applicable customer policy.

AuditCore and the event bus remain separate: AuditCore is durable history for investigation and compliance; the event bus propagates changes to asynchronous consumers.