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:
| Field | Purpose |
|---|---|
id and type | Stable event identity and canonical event name |
eventVersion | Version of the event contract |
grammar | Registered payload grammar, such as accounting or commerce |
source and sourceSystem | Producing component and optional external system |
payload | Grammar-validated event data |
dimensionalTags | Tenant-safe dimensions for filtering and analysis |
principalChain | Ordered user, agent, service, or system principals involved |
occurredAt, receivedAt | Source and ingestion timestamps |
causalId | Immediate parent event |
correlationId | Identifier shared across a wider workflow |
idempotencyKey | Tenant-scoped retry deduplication key |
sourceEventId | Upstream 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/eventson 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.