Skip to content
SarvexDocs
Esc
↑↓navigate↵open⌘Jpreview
On this page

Sarvex V1 — Sync Plane Event Contract

The sync plane is event-driven, eventually-consistent, and retryable. Its interface is therefore a message contract, not a REST API — this AsyncAPI document is the counterpart to sarvex-v1-openapi.yaml (which covers the synchronous control plane and the /connector/* ingest edge).

What this specifies

The internal event bus over which sync-plane components communicate: the normalised change events produced from store signals, the derived scoring/consistency events, the checkout lifecycle and checkout→order handoff events, and the task/journey/monitoring signals that cross into the control plane.

What this does NOT specify

  • The /connector/* HTTP ingest endpoints (in the OpenAPI doc) — they are the source of the sync.*.changed and sync.order.status_changed events here, but the wire call itself is synchronous.
  • The pull side: Sarvex’s connector calling the merchant store’s own REST API to fetch full entities. That is the store’s API, governed by the platform, not by Sarvex.
  • The serving-plane UCP/checkout endpoints (external UCP/Google schema).

Bus conventions (true for every message)

  • Naming. Events are named source.resource.something_happened (past tense) — e.g. sync.price.changed, sync.order.created, serving.snapshot.swapped. The source is the emitting plane (sync.*, serving.*), with one exception: ops.* names the endpoint-health probe/evaluator workers (internal/planes/control/endpointhealth), a cross-cutting operational concern that isn’t owned by one plane (its probes call the serving plane over HTTP, its evaluator consumes serving-plane telemetry, and its read API lives in the control plane). This document specifies events only. Imperative commands (source.resource.operation, e.g. control.catalog.import) are a separate contract — see the control→sync commands catalogue — because a command is a directed “do this”, not a fan-out “this happened”.
  • Topic-per-event-type, partitioned by store. Channels are event-type topics, not topic-per-store. Every message carries ordering_key = store_id in its headers. The bus guarantees ordering within a store; messages for different stores (and different merchants) are processed fully in parallel. (This is the messaging expression of the tenant-isolation and per-store-sequencing invariants; a store belongs to exactly one merchant per architecture §2a.)
  • Delivery is at-least-once. Every consumer must be idempotent. De-duplication is by message_id; ordering/replay protection on ingest is by the monotonic sequence carried from the originating store signal.
  • Retries with backoff, then dead-letter. A message that exhausts retries goes to the matching *.dlq channel (not enumerated per-channel here; it is a uniform convention) and raises an operator alert. Nothing is silently dropped on the derived-event side. (Lost ingest signals are the exception — the reconciliation sweeper is their safety net.)
  • Priority lane. sync.price.changed and sync.inventory.changed are compliance-critical and consumed on a priority lane so they cannot queue behind a bulk catalog import.
  • Envelope: all payloads are JSON, snake_case, and wrapped by the common message_envelope headers (id, ordering key, timestamps, sequence, trace, schema version). Money is integer minor units plus currency. Timestamps are RFC 3339 UTC.
  • One producer per event type. Each channel names exactly one producing component (x-producer) and its consumers (x-consumers) — the messaging expression of the one-writer-per-store rule.
Version 1.0.0
Serverspubsub://pubsub.sarvex.internal

sync.consistency.finding_raised

sync.reconciliation.discrepancy_found

serving.snapshot.swapped

serving.request.telemetry_recorded

serving.catalog.query_recorded

ucp.negotiation.succeeded

ucp.negotiation.failed

ops.health.probe_completed

ops.health.threshold_breached

ops.health.recovered

serving.surface_artifact.fetched

sync.surface_artifact.updated

serving.checkout.session_initiated

serving.checkout.cart_updated

serving.checkout.payment_submitted

serving.checkout.escalated

serving.checkout.failed

serving.checkout.initiated

serving.checkout.updated

serving.checkout.completed

serving.checkout.abandoned

serving.checkout.order_intent

sync.order.created

sync.order.handoff_failed

sync.order.status_changed

sync.task.raised

sync.journey.threshold_met

sync.journey.threshold_lost

control.journey.updated

control.store.activation_state_changed

control.store.surface_state_changed

sync.monitoring.alert_raised

control.task.updated

Was this page helpful?