> ## Documentation Index
> Fetch the complete documentation index at: https://voyant.travel/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Distribution

> The commercial-network module: channels, channel contracts, commission rules, product mappings, allotments, channel push, reconciliation, and settlement for OTA, tour-operator, and DMC deployments.

The distribution module (`@voyant-travel/distribution`) owns the broader commercial network: the [Channels](/docs/concepts/glossary) that sell an operator's inventory, the contracts and commission rules that govern them, the mappings and allotments that target inventory at each channel, the outbound channel push that keeps upstream systems in sync, and the reconciliation and settlement that close the financial loop. It also carries supplier-side identity and external references. It is cross-cutting channel support, not a separate user base.

Distribution is the **outbound** complement to the [catalog](/docs/platform/modules/catalog) source-adapter contract. Where catalog's inbound direction pulls projections, prices, and content from upstream sources, distribution pushes the inverse: when a booking commits, an availability changes, or content is edited on Voyant, distribution syndicates that change to the channels that resell the inventory. It extends bookings and inventory through the established `distributionBookingExtension` pattern, so those modules stay clean of channel concepts while distribution depends on them.

## Key concepts

<ResponseField name="Channel" type="distribution counterparty">
  A counterparty that sells the operator's inventory: direct, OTA, affiliate, reseller, marketplace, or API partner. A first-class entity (`channels`) with its own contracts, contacts, commission rules, allotments, and reconciliation surface. See [Channel](/docs/concepts/glossary).
</ResponseField>

<ResponseField name="Channel Contract" type="agreed terms">
  The agreed commercial terms with a Channel (`channel_contracts`): commission, compensation policy, rate-limit overrides, and the like. See [Channel Contract](/docs/concepts/glossary).
</ResponseField>

<ResponseField name="Commission Rule" type="scoped earning rule">
  A scoped (booking, product, rate, or category) fixed-or-percentage rule for what a Channel earns (`channel_commission_rules`). See [Commission Rule](/docs/concepts/glossary).
</ResponseField>

<ResponseField name="Channel Product Mapping" type="product-level identity">
  The relationship between a Voyant product, option, or category and the channel's external identifiers (`channel_product_mappings`). Used at booking-push time to translate a Voyant booking item into the upstream product reference. One row per channel and product.
</ResponseField>

<ResponseField name="Channel Inventory Allotment" type="per-slot targeting">
  Per-slot, per-option, per-start-time inventory targeting (`channel_inventory_allotments`): how many units of which option a channel sells for a given departure. Used at availability-push time to know which slots a channel cares about. Many rows per channel and product. Release rules return unsold allotment to general capacity.
</ResponseField>

<ResponseField name="Channel Booking Link" type="booking cross-reference">
  The relationship between a Voyant booking and the channel's booking or reference (`channel_booking_links`): external booking id, external status, last-synced timestamp, and the push status that tracks an in-flight or completed outbound push.
</ResponseField>

<ResponseField name="Channel push" type="outbound sync">
  The outbound direction of supplier integration: booking push (per-booking, transactional), availability push (per-slot-change, idempotent, eventually-consistent), and content push (per-edit, infrequent). Each shares the `SourceAdapter` contract but uses a distinct durable-intent and workflow code path because the retry and error semantics differ.
</ResponseField>

<ResponseField name="Reconciliation" type="expected vs actual">
  Comparison of expected versus actual bookings and amounts against a Channel (`channel_reconciliation_runs`, `channel_reconciliation_items`), producing Reconciliation Issues such as `missing_booking`, `status_mismatch`, `amount_mismatch`, `cancel_mismatch`, and `missing_payout`. It also catches divergence after a long channel outage. See [Reconciliation](/docs/concepts/glossary).
</ResponseField>

<ResponseField name="Settlement" type="financial close">
  An accounting run reconciling booking-side amounts owed to or from a Channel (`channel_settlement_runs`, `channel_settlement_items`), with approval and policy surfaces. See [Settlement](/docs/concepts/glossary).
</ResponseField>

## What it owns

* **Channels and contracts**: the `channels` and `channel_contracts` tables, channel contacts, and the per-channel commercial terms.
* **Commission rules**: `channel_commission_rules`, scoped by booking, product, rate, or category.
* **Mappings and allotments**: `channel_product_mappings` (product-level identity), `channel_inventory_allotments` and their targets (per-slot inventory), and the release rules and schedules that govern unsold allotment.
* **Channel push**: the durable-intent tables (`channel_availability_push_intents`, `channel_content_push_intents`), the push-status fields on `channel_booking_links` and `channel_product_mappings`, and the per-flow workflows. Subscribers write durable intent rows and return immediately; durable workflows do the HTTP so the in-process event bus is never blocked.
* **Reconciliation and settlement**: the run, item, policy, and approval tables for both, plus remittance-exception handling.
* **Inbound channel webhooks**: `channel_webhook_events` logs events received from channels (the opposite direction from outbound push).
* **Suppliers and external refs**: supplier identity, services, rates, and notes (`./suppliers`), and per-entity upstream identifiers (`./external-refs`).
* **Per-channel rate limiting**: token-bucket config on channels and contracts so booking pushes pre-empt availability and content pushes while sharing one upstream budget.

It does **not** own bookings, products, or finance truth; it references them by id and extends them through the booking extension. The catalog plane stays neutral, owning the adapter contract while distribution owns the operational push state.

## Working with it

Distribution registers as a module like any other.

Standard modules are not registered by hand. `@voyant-travel/distribution` is part of the standard product graph, so its routes, services, subscribers, and jobs are resolved into the application at build time. See [Configuration](/docs/platform/fundamentals/configuration).

Channel push is the operational heart. A booking commit triggers a write-only subscriber that records durable intent and returns; a durable workflow does the actual upstream calls with retries, rate limiting, and compensation.

```ts theme={null}
// Subscriber: re-fetch state, write pending links, trigger the workflow. No HTTP here.
eventBus.subscribe("booking.confirmed", async ({ data }) => {
  const booking = await readBookingWithItems(db, data.bookingId);
  const channels = await resolveChannelsForBooking(db, booking); // via channel_product_mappings
  if (channels.length === 0) return; // owned product not syndicated

  for (const { itemId, channel } of channels) {
    if (!channel.mapping.pushBookings) continue;
    if (!channel.adapter.capabilities.supportsBookingPush) continue;
    await upsertChannelBookingLink(db, {
      bookingId: booking.id,
      bookingItemId: itemId,
      channelId: channel.id,
      sourceConnectionId: channel.connectionId,
      pushStatus: "pending",
      idempotencyKey: stableKey(booking.id, itemId, channel.id),
    });
  }

  await workflows.trigger("channel.booking.push", { bookingId: booking.id });
});
```

The outbound contract extends the same `SourceAdapter` used for inbound. One adapter instance per connection carries both directions; capability flags separate them.

```ts theme={null}
export interface SourceAdapter {
  // ...inbound methods (discover, liveResolve, reserve, cancel) ...
  pushBooking?(ctx, request): Promise<PushBookingResult>;
  pushAvailability?(ctx, request): Promise<PushAvailabilityResult>;
  pushContent?(ctx, request): Promise<PushContentResult>;
}
```

<Note>
  Resolve channels with the right table for each flow, or you get a correctness bug. Booking push and content push use `channel_product_mappings` (product-level), because they translate a product reference and content is product-shaped. Availability push uses `channel_inventory_allotments` (per-slot), so it only pushes slots a channel actually has an allotment for. Resolving booking push via allotments misses channels sold "on request"; resolving availability push via mappings floods every channel with every slot.
</Note>

For the marketplace direction (selling owned inventory through Viator or GetYourGuide), distribution is the home for the channel facade: those integrations are distribution channel adapters that sit on top of inventory, commerce, and bookings, never catalog source adapters. Voyant stays the operator's system of record; the marketplace reads availability and price and sends reservations back through the adapter, which creates real local bookings and allocations.

## Links to other modules

* **bookings** is extended by `distributionBookingExtension` and is the trigger for booking push; channel booking links cross-reference Voyant bookings to channel references.
* **[inventory](/docs/platform/modules/inventory)** supplies the Slots that allotments target and the availability changes that drive availability push.
* **[catalog](/docs/platform/modules/catalog)** owns the `SourceAdapter` contract distribution extends for outbound, and the registry keyed by connection.
* **[commerce](/docs/platform/modules/commerce)** supplies channel and commission context for the commercial decision; what a channel earns is settled here.
* **finance** consumes settlement output and remittance, and reconciliation issues feed financial dispute handling.

## React package

The `@voyant-travel/distribution-react` family provides the staff-facing channel UI: channel setup, contract and commission management, product and option mapping, allotment configuration, the channel-sync health view backed by push status, and the reconciliation and settlement surfaces. Operator language stays explicit ("Sell this owned product on Viator", "Channel booking", "Channel reference"); rate-limit tokens and intent tables stay invisible.

## Next steps

<CardGroup cols={2}>
  <Card title="Catalog" icon="layer-group" href="/docs/platform/modules/catalog">
    The inbound source-adapter contract distribution extends for outbound push.
  </Card>

  <Card title="Inventory" icon="calendar-days" href="/docs/platform/modules/inventory">
    The Slots and availability that allotments target and push to channels.
  </Card>

  <Card title="Commerce" icon="tag" href="/docs/platform/modules/commerce">
    Channels, commission, and the commercial decision that scopes a sale.
  </Card>

  <Card title="Glossary" icon="book-open" href="/docs/concepts/glossary">
    Channel, Commission Rule, Settlement, and Reconciliation defined.
  </Card>
</CardGroup>
