---
seo:
  description: >-
    The sync plane is event-driven, eventually-consistent, and retryable. Its
    interface is therefore a message contract, not a REST API — this AsyncAPI
    document…
sidebar:
  label: Overview
title: 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

Servers: `pubsub://pubsub.sarvex.internal`

## sync.consistency.finding_raised

- [`RECEIVE sync.consistency.finding_raised`](/docs/api/events/sync-consistency-finding-raised/on-consistency-finding) — Cross-source consistency finding

## sync.reconciliation.discrepancy_found

- [`RECEIVE sync.reconciliation.discrepancy_found`](/docs/api/events/sync-reconciliation-discrepancy-found/on-reconciliation-discrepancy) — Sweeper discrepancy

## serving.snapshot.swapped

- [`RECEIVE serving.snapshot.swapped`](/docs/api/events/serving-snapshot-swapped/on-snapshot-swapped) — Serving snapshot swapped

## serving.request.telemetry_recorded

- [`RECEIVE serving.request.telemetry_recorded`](/docs/api/events/serving-request-telemetry-recorded/on-telemetry-recorded) — Per-request telemetry (signature/negotiation outcome, status, latency)

## serving.catalog.query_recorded

- [`RECEIVE serving.catalog.query_recorded`](/docs/api/events/serving-catalog-query-recorded/on-catalog-query-recorded) — An agent's catalog search/lookup request was recorded

## ucp.negotiation.succeeded

- [`RECEIVE ucp.negotiation.succeeded`](/docs/api/events/ucp-negotiation-succeeded/on-negotiation-succeeded) — UCP capability negotiation succeeded

## ucp.negotiation.failed

- [`RECEIVE ucp.negotiation.failed`](/docs/api/events/ucp-negotiation-failed/on-negotiation-failed) — UCP capability negotiation failed

## ops.health.probe_completed

- [`RECEIVE ops.health.probe_completed`](/docs/api/events/ops-health-probe-completed/on-health-probe-completed) — Synthetic endpoint-health probe result

## ops.health.threshold_breached

- [`RECEIVE ops.health.threshold_breached`](/docs/api/events/ops-health-threshold-breached/on-health-threshold-breached) — Endpoint-health check breached (alert opened)

## ops.health.recovered

- [`RECEIVE ops.health.recovered`](/docs/api/events/ops-health-recovered/on-health-recovered) — Endpoint-health check recovered (alert auto-resolved)

## serving.surface_artifact.fetched

- [`RECEIVE serving.surface_artifact.fetched`](/docs/api/events/serving-surface-artifact-fetched/on-surface-artifact-fetched) — Surface artifact fetched

## sync.surface_artifact.updated

- [`RECEIVE sync.surface_artifact.updated`](/docs/api/events/sync-surface-artifact-updated/on-surface-artifact-updated) — Surface artifact generated or observed

## serving.checkout.session_initiated

- [`RECEIVE serving.checkout.session_initiated`](/docs/api/events/serving-checkout-session-initiated/on-checkout-session-initiated) — A checkout session was created

## serving.checkout.cart_updated

- [`RECEIVE serving.checkout.cart_updated`](/docs/api/events/serving-checkout-cart-updated/on-checkout-cart-updated) — A checkout session's cart/buyer/fulfillment was updated

## serving.checkout.payment_submitted

- [`RECEIVE serving.checkout.payment_submitted`](/docs/api/events/serving-checkout-payment-submitted/on-checkout-payment-submitted) — A checkout session's payment was submitted to the connector

## serving.checkout.escalated

- [`RECEIVE serving.checkout.escalated`](/docs/api/events/serving-checkout-escalated/on-checkout-escalated) — A checkout session requires buyer escalation

## serving.checkout.failed

- [`RECEIVE serving.checkout.failed`](/docs/api/events/serving-checkout-failed/on-checkout-failed) — A checkout session failed

## serving.checkout.initiated

- [`RECEIVE serving.checkout.initiated`](/docs/api/events/serving-checkout-initiated/on-checkout-initiated-v1) — A checkout session was created (versioned lifecycle contract)

## serving.checkout.updated

- [`RECEIVE serving.checkout.updated`](/docs/api/events/serving-checkout-updated/on-checkout-updated-v1) — A checkout session was updated (versioned lifecycle contract)

## serving.checkout.completed

- [`RECEIVE serving.checkout.completed`](/docs/api/events/serving-checkout-completed/on-checkout-completed-v1) — A checkout session completed (versioned lifecycle contract)

## serving.checkout.abandoned

- [`RECEIVE serving.checkout.abandoned`](/docs/api/events/serving-checkout-abandoned/on-checkout-abandoned-v1) — A checkout session was abandoned (versioned lifecycle contract)

## serving.checkout.order_intent

- [`RECEIVE serving.checkout.order_intent`](/docs/api/events/serving-checkout-order-intent/on-order-intent) — Order intent from a completed checkout (order already created)

## sync.order.created

- [`RECEIVE sync.order.created`](/docs/api/events/sync-order-created/on-order-created) — Order recorded from a completed checkout

## sync.order.handoff_failed

- [`RECEIVE sync.order.handoff_failed`](/docs/api/events/sync-order-handoff-failed/on-handoff-failed) — Order handoff failed (critical)

## sync.order.status_changed

- [`RECEIVE sync.order.status_changed`](/docs/api/events/sync-order-status-changed/on-order-status-changed) — Order status change (normalised)

## sync.task.raised

- [`RECEIVE sync.task.raised`](/docs/api/events/sync-task-raised/on-task-raised) — Detected task raised/updated/resolved from a sync-plane finding

## sync.journey.threshold_met

- [`RECEIVE sync.journey.threshold_met`](/docs/api/events/sync-journey-threshold-met/on-threshold-met) — Readiness threshold met

## sync.journey.threshold_lost

- [`RECEIVE sync.journey.threshold_lost`](/docs/api/events/sync-journey-threshold-lost/on-threshold-lost) — Readiness threshold lost

## control.journey.updated

- [`RECEIVE control.journey.updated`](/docs/api/events/control-journey-updated/on-journey-updated) — Journey state transitioned

## control.store.activation_state_changed

- [`RECEIVE control.store.activation_state_changed`](/docs/api/events/control-store-activation-state-changed/on-store-activation-state-changed) — Store UCP activation state transitioned

## control.store.surface_state_changed

- [`RECEIVE control.store.surface_state_changed`](/docs/api/events/control-store-surface-state-changed/on-store-surface-state-changed) — Agent-surface enrollment state transitioned

## sync.monitoring.alert_raised

- [`RECEIVE sync.monitoring.alert_raised`](/docs/api/events/sync-monitoring-alert-raised/on-monitoring-alert) — Monitoring alert

## control.task.updated

- [`RECEIVE control.task.updated`](/docs/api/events/control-task-updated/on-task-updated) — Task raised or state changed
