When an order is routed to Provider A, they confirm acceptance at 14:22:15. But Provider B (your fallback) doesn't see the cancellation until 14:22:47. For 32 seconds, your system thinks two providers are working on the same order. How do you reconcile that?

The consensus problem

Print is inherently distributed. You have multiple providers, each with their own sense of truth. Order XYZ is confirmed at Provider A, cancelled at Provider B, and has an indeterminate status at Provider C because their API is slow today.

In a single-provider world, you have one source of truth: your database. In a multi-provider world, you have n+1 sources of truth (your database plus each provider's), and they don't always agree.

The traditional approach: treat your database as the source of truth and poll providers to sync. It works, but it's brittle. Delays in polling mean your view of the world is always stale. Provider timeouts mean you miss status updates. Inconsistencies pile up.

The more providers you have, the more likely they'll disagree about what happened and when. You need a system that can live with disagreement.

Distributed state is hard

The naive approach to multi-provider state: create an "order status" field and update it based on the latest information from any provider.

This breaks immediately. You get a webhook from Provider A saying "order confirmed" at timestamp T1. You get a webhook from Provider B saying "order rejected" at timestamp T2. Which one is right?

Timestamps can be wrong (clock skew across systems). Webhooks can arrive out of order. Providers can send duplicate notifications. You can't tell which notification is "latest" because you don't trust timestamps.

And here's the real problem: sometimes both are true. Provider A confirmed the order. Provider B rejected it because you already filled capacity elsewhere. The order is confirmed at A but not at B, and that's the correct state.

The order ledger approach

Instead of "what is the order's current state?", ask "what is the complete history of this order?"

Every notification, every status update, every event gets logged to an immutable ledger. Nothing is ever updated or deleted — only appended. The ledger is the source of truth, not the current state.

An order ledger entry looks like:

  • order_id: xyz-789
  • provider_id: provider_a
  • event_type: submission.accepted
  • event_timestamp: 2026-05-16 14:22:15 (provider's timestamp)
  • received_timestamp: 2026-05-16 14:22:16 (when we got the notification)
  • provider_order_id: psp-order-12345
  • eta: 2026-05-22

And a second entry when Provider B rejects:

  • order_id: xyz-789
  • provider_id: provider_b
  • event_type: submission.rejected
  • event_timestamp: 2026-05-16 14:22:18
  • received_timestamp: 2026-05-16 14:22:47
  • reason: capacity_unavailable

Now your system can answer: "Order XYZ is confirmed at Provider A and rejected at Provider B. The confirmation came first. Status: CONFIRMED."

Embracing eventual consistency

The ledger-based approach means your state is eventually consistent, not immediately consistent. For a few seconds or minutes, different parts of the system might have different views of what happened.

But that's okay. Print fulfillment is inherently eventual anyway. You submit an order, wait for confirmation, produce it, ship it. The time between each step is minutes or hours, not milliseconds.

Eventual consistency lets you:

  • Handle providers with slow or unreliable webhooks
  • Recover from network partitions without losing information
  • Audit the complete history of what each provider said, in order
  • Reconcile disagreements automatically when new information arrives

The key insight: you don't need instant agreement across providers. You need agreement eventually, and the ability to explain why providers disagreed in the meantime.

How Oruve built it

Oruve maintains two things for every order:

  1. The ledger: Immutable log of every notification from every provider. Updated only by append. This is the source of truth.
  2. The projection: Cached view of the order's current state, derived from the ledger. Rebuilt when new ledger entries arrive. This is for fast reads.

When a notification arrives from a provider, we:

  1. Log it to the ledger (idempotency key ensures we don't double-log)
  2. Re-derive the current state from the ledger
  3. Compare to the cached projection — if different, update it
  4. Emit events to the dashboard, webhooks, etc.

If a provider's notification arrives late or out of order, the ledger is already complete. We just re-read the ledger and see the correct state.

This is what makes multi-provider orchestration reliable: not assuming providers agree, but building a system that can prove what each one said, in order, and reconcile differences automatically.

That infrastructure is built into Oruve. It handles the hard parts of distributed state so you don't have to.