Omnichannel
Note: This is an enterprise API feature. Reach out to your Partnership Manager for more information about unlocking it and other advanced features.
This page describes how a retailer uses the Distribusion Retailer API (rAPI) to record which sales channel a transaction came from — your website, your mobile app, an agency counter, a station kiosk, an AI assistant, and so on.
General principle. sales_channel_type is transaction metadata, not an entity. It describes how a booking was made, not who made it. You send it as an optional parameter on the requests you already make, Distribusion records it on the resulting reservation, booking, cancellation or amendment, and returns it on every read of those records. One set of API credentials (one retailer partner number) can therefore serve all of your channels — you no longer need a separate partner number per channel to tell them apart.
When to use it. Send sales_channel_type on every request whenever you sell through more than one surface and want to distinguish them in reporting, reconciliation and customer service. If you sell through a single channel you can omit it; the field then stays null.
sales_channel_type does not change the content you are offered, the price you are quoted, or the fare rules that apply. It is recorded for classification and reporting.
Supported values
sales_channel_type is a string and must be exactly one of the following values. Values are lower-case with underscores.
Value | Channel |
|---|---|
| Mobile web — a browser on a phone or tablet |
| Desktop web |
| Native iOS app |
| Native Android app |
| Mobile web reached by scanning a QR code |
| Travel-agency counter portal |
| Handheld payment-terminal app |
| Sales point at a station |
| Single-screen counter portal used at a station |
| Self-service kiosk or ticket vending machine (TVM) |
| Contactless tap-to-pay acceptance |
| Conversational chatbot |
| AI assistant |
| B2C website |
| B2C app |
| Corporate online booking tool |
| Corporate offline booking tool |
| API partner redistributing content to third parties |
Any other value is rejected with 400.101.227. New channels are added as new values — no change to your credentials or configuration is required to start using one.
Affected endpoints
sales_channel_type is optional everywhere. It is accepted in the JSON body of POST/PUT requests and as a query parameter on GET requests, on both rAPI v4 and v5.
Endpoint | What the value does |
|---|---|
| Tags the offers produced by the call |
| Tags the seat-availability request; resolved from the supplied offers when omitted |
| Recorded on the reservation |
| Recorded on the booking; falls back to the reservation's value when omitted |
| Recorded on the reservation being cancelled |
| Classification only — no record is written |
| Recorded on the cancellation; falls back to the booking's value when omitted |
| Recorded on the amendment |
| Recorded on the new booking created by the amendment; falls back to the amended reservation's value when omitted |
| Classification only — no record is written |
The rule to remember: the value you send on a call is the value that gets recorded. If you omit it, the value already stored on the reservation or booking is kept.
All examples below use the demo environment (api.demo.distribusion.com). Authentication via the Api-Key header is required on every request — omitted from the snippets for brevity. Station codes (ZZSTN001…), offer IDs, passenger IDs and booking references are placeholders; real responses return longer generated identifiers.
Sending the sales channel
Add sales_channel_type alongside the parameters you already send. Send the same value at every step of one customer journey.
Checking price and vacancy
POST https://api.demo.distribusion.com/retailers/v4/connections/vacancy{"offers": [{"id": "offer-1", "passenger_ids": ["pax-1", "pax-2"]}], "sales_channel_type": "app_ios"}Creating a reservation
POST https://api.demo.distribusion.com/retailers/v4/reservations/create{"offers": [{"id": "offer-1", "passenger_ids": ["pax-1", "pax-2"]}], "passengers": [{"id": "pax-1", "type": "PNOS", "first_name": "Ana", "last_name": "Tesswann"}, {"id": "pax-2", "type": "PNOS", "first_name": "Leo", "last_name": "Pires"}], "terms_accepted": true, "sales_channel_type": "app_ios"}Confirming the reservation
The booking inherits the reservation's value, so you do not have to repeat it. Send it again only if the confirmation genuinely happened on a different channel.
PUT https://api.demo.distribusion.com/retailers/v4/reservations/confirm{"reservation_id": "RZ4SW21E", "send_customer_email": true, "execute_payment": false, "payment_method": "invoice", "passengers": [{"id": "pax-1", "type": "PNOS", "first_name": "Ana", "last_name": "Tesswann"}], "sales_channel_type": "app_ios"}On a GET request
On GET endpoints the value is passed in the query string. It is used for classification only — no record is written.
GET https://api.demo.distribusion.com/retailers/v4/bookings/EsEOGnFVTdwytNacNljztg?sales_channel_type=agency_counterReading the sales channel back
sales_channel_type is returned as an attribute on the reservations, bookings, cancellations, amendments and amendment_reservations objects. It is null when no value was ever recorded.
On POST /reservations/create, PUT /reservations/confirm and GET /reservations/{reservation_id}:
{"data": {"id": "RZ4SW21E", "type": "reservations", "attributes": {"retailer_partner_number": "222222", "agent_id": null, "customer_id": null, "sales_channel_type": "app_ios"}}}On GET /bookings and GET /bookings/{booking_id}, the same attribute appears on the bookings object.
On POST /seats_availability, the resolved value is echoed on each connections object so you can confirm which channel the seat prices were quoted for:
{"data": [{"id": "ZZSTN001-ZZSTN002-2026-04-21T10:00-2026-04-21T12:00-0", "type": "connections", "attributes": {"departure_station": "ZZSTN001", "arrival_station": "ZZSTN002", "sales_channel_type": "ai_assistant"}}]}Use the returned value on your Manage Booking and customer-service surfaces to show where a booking originated.
Post-sales actions on a different channel
A booking made on one channel is frequently cancelled or amended on another — a customer books in the app and then calls an agent, for example. rAPI records both.
- Send
sales_channel_typeonPOST /cancellations/createorPOST /amendments/createto record the channel that performed the post-sales action. The cancellation or amendment carries that value; the original booking keeps its own. - Omit it, and the cancellation or amendment inherits the value stored on the original booking.
POST https://api.demo.distribusion.com/retailers/v4/cancellations/create{"booking": "EsEOGnFVTdwytNacNljztg", "reason": "customer_request", "sales_channel_type": "agency_counter"}The cancellation above is recorded as agency_counter even though the booking it cancels was created as app_ios.
Using the sales channel with the Offers flow
In the Offers flow, offers carry the sales channel of the search that produced them. When you then call POST /seats_availability with those offers and no explicit sales_channel_type, the value is taken from the offers.
This means offers from different searches must not be mixed if those searches used different channels. If they are, the request is rejected with 400.101.226:
{"jsonapi": {"version": "1.0"}, "errors": [{"code": "400.101.226", "title": "Offers have conflicting sales_channel_type values. Pass sales_channel_type explicitly or use offers from the same search."}]}There are two ways to resolve it: send sales_channel_type explicitly on the request — an explicit value always wins and suppresses the conflict — or use offers from a single search.
Sales channels and agencies
Sales channels and agencies answer two different questions and are set independently:
sales_channel_typerecords how a transaction was made.- The agency, resolved from your credentials, records which business unit under your partner number made it, and is returned as the agency relationship on reservations and bookings.
A booking can carry both — for example sales_channel_type: "agency_counter" for a sale made at a counter, plus the agency that owns that counter. Note that agency_counter is a channel value, not an agency: sending it does not scope the request to any agency.
Errors
Error | HTTP status | Code | Raised when |
|---|---|---|---|
SalesChannelTypeValidationError | 400 | 400.101.227 | The value sent is not one of the supported values |
SalesChannelTypeConflictError | 400 | 400.101.226 | Offers in a single request carry different sales_channel_typevalues and no explicit value was sent |
Leading and trailing whitespace is trimmed before validation. An empty string is treated as if the parameter had been omitted.
On this page
- Omnichannel