Skip to content
AM

Commerce events

Commerce publishes registered cross-pillar events over the internal event transport. This is an internal service contract, not a customer endpoint or direct Redis subscription interface.

Canonical envelope

{
"id": "<event-uuid>",
"type": "commerce.order.fulfilled",
"tenant_id": "<tenant-id>",
"org_id": "<organization-id>",
"causation_id": "<upstream-event-id>",
"contact_hints": {"email": "buyer@example.com"},
"source": "commerce",
"payload": {},
"created_at": "2026-07-10T12:00:00Z",
"v": 1
}
FieldContract
idEvent UUID; assigned at publish time when omitted
typeRegistered dotted type in {pillar}.{entity}.{action} form
tenant_idBackward-compatible tenant partition key
org_idCanonical analytics partition key; defaults to tenant_id for legacy publishers
causation_idOptional upstream request or event ID
contact_hintsOptional CRM identity hints; resolution failure must not drop the event
sourcePublishing pillar or service
payloadEvent-specific JSON
created_atRFC 3339 timestamp; assigned at publish time when omitted
vInteger envelope version, currently 1

data, date-string version, trace_id, and attempt are not fields in the current shared event envelope.

Registered Commerce families

The executable registry includes these Commerce event groups:

GroupRegistered examples
Orders and paymentscommerce.order.created, commerce.order.fulfilled, commerce.order.status_changed, commerce.order.completed, commerce.payment.received, commerce.refund.issued
Purchasing and costscommerce.purchase_order.received, commerce.purchase_order.paid, commerce.purchase_order.updated, commerce.channel_fee.charged, commerce.shipping.charged, commerce.shipping.paid
Inventory and fulfillmentcommerce.inventory.adjustment, commerce.inventory.changed, commerce.inventory.restocked, commerce.fulfillment.shipped, commerce.assembly.completed
Returnscommerce.return.requested, commerce.return.approved, commerce.return.received, commerce.return.restocked, commerce.return.refunded
Catalog and storefrontcommerce.product.updated, commerce.brand.updated, commerce.storefront_form.submitted, commerce.customer.created
Integrations and messagingcommerce.sync.failed, commerce.channel_payout.received, commerce.contact_linkage.requested, commerce.message_send.requested
Reporting feedscommerce.email_send.recorded, commerce.email_template.updated, commerce.email_flow.updated

Publish-time validation rejects event types absent from shared/events/registry.go. Legacy unprefixed names are normalized only where the registry declares a legacy alias.

Reliability

New cross-domain publishers should append the canonical AuditCore event and enqueue the transport publication in the same PostgreSQL transaction. The shared outbox dispatcher:

  1. Claims due rows with FOR UPDATE SKIP LOCKED.
  2. Publishes them to Redis Streams.
  3. Records the Redis message ID.
  4. Retries failures with exponential backoff.
  5. Moves exhausted rows to dead_letter.

Reliable consumers use an inbox key of (consumer_name, event_id). Processed duplicates are acknowledged without rerunning the handler; failed messages remain recoverable, and exhausted failures move to a dead-letter stream.

Some existing direct publishers remain during migration. The transactional outbox is the required pattern for new canonical cross-domain writes, not a guarantee that every historical publisher already uses it.

Versioning

The registry declares a minimum and maximum supported integer version per event type. If no range is specified, only version 1 is supported.

Within a version, changes must be backward compatible: add optional fields, preserve field meaning, and retain old fields until consumers are upgraded. Breaking changes require a new registry range plus dual-read or dual-publish handling. Reliable consumers reject unsupported versions into the dead-letter path.

There is no subscriber version-pinning negotiation in this transport contract.