OTAs & marketplaces

You sit between guest and property, you may be the merchant of record, you charge commission, and your tax responsibility shifts with each. This is the most technical persona: it leans on merchant-of-record routing, channel-scoped rates, commission-line reverse charge, and self-billing. Everything here maps to a request field.

The mental model: two supplies, not one

The single idea that unlocks OTA tax is that a marketplace booking contains two distinct supplies, each taxed on its own terms:

  • The room — a service connected to immovable property. Under EU VAT Directive Art. 47 it is always taxed where the property is, with local VAT. The room is never reverse-charged, regardless of who the guest is or where your platform is established.
  • Your commission / intermediary service — a separate supply from your platform to the property. This is where cross-border B2B reverse charge (Art. 44/196) can apply, and where the merchant-of-record routing actually changes who collects the room taxes.
The room is never reverse-charged
A reverse-charge rule attached to the room VAT is a compliance bug (the prior seeded room rule was removed for exactly this). Reverse charge belongs on the commission line. Keep these two supplies separate in your head and the rest of this page is mechanical. Deep version: reverse charge.

Merchant of record routes who collects

merchant_of_record is the field that tells the engine who is responsible for collecting the room taxes. It does not change how much tax there is — the stack is identical — it changes the collection_info routing in the response.

Merchant-of-record routing
Booking — same tax stack either way
merchant_of_record = "property"
Room taxes → property_collected
Commission → its own line_item
merchant_of_record = "platform"
Room taxes → platform_collected
(you collect + remit)
Per-rate collection_model can override one layer (e.g. MPF: city → platform, state → property)
Channel-scoped rates (applies_to_channels) fire only for the matching channel
Same tax stack; collection_info routes each component to the responsible party.

The scenario map, straight from the calculation surface:

FieldTypeDescription
merchant_of_record"property" | "platform" | "none""property": agency model — the property is MoR, you surface who owes what and add commission via a line item. "platform": you are MoR — same calc, but collection_info routes all room categories to platform_collected_components. Omitted with is_marketplace=false → legacy direct (property); omitted with is_marketplace=true → resolves to platform. Either way this only affects collection routing, not the tax total.
is_marketplace / platform_typeboolean / stringHelp resolve the booking's channel for channel-scoped rates. A rate with applies_to_channels set fires only for the matching channel; the engine drops non-matching layers before stacking.
line_items[item_type=commission]LineItem[]Your commission as its own taxable line, separate from the room. Commission tax (where jurisdictionally applicable) and reverse charge attach here — never to the room.
commission_supplier_countrystring (ISO-2)Your platform/intermediary's establishment country. Drives the commission-line cross-border test independently of the guest, so a non-EU platform billing an EU property reverse-charges its commission even for a domestic consumer.

The exhaustive scenario → field map lives in scenario fields, and the routing concept in channels & merchant of record.

The common platform scenarios

Each maps to a specific combination of fields:

  1. 1
    Agency model — property is MoR
    Set merchant_of_record: "property". collection_info surfaces who owes what; your commission goes through line_items with item_type: "commission". The room taxes remain the property's to collect.
  2. 2
    Merchant model — platform is MoR
    Set merchant_of_record: "platform". The room tax is unchanged, but collection_info routes every room category to platform_collected_components — you collect and remit.
  3. 3
    Marketplace facilitator (split collection)
    Some city laws make the platform collect the city tax while the state/VAT stays with the property. That's expressed per-rate via collection_model on the city-level rate — so a single calculation returns a split collection_info. No hand-authored mutual exemptions.
  4. 4
    Channel-mutex rates
    Where a city has separate articles for direct vs platform bookings (e.g. distinct occupancy-tax articles), they are two channel-scoped rates, not two exemption rules. The booking's resolved channel selects the right one — set is_marketplace / platform_type accurately.

Cross-border commission reverse charge

When your platform is established in a different country from the property and supplies an intermediary service B2B, the commission is reverse-charged: you charge 0% VAT and the property accounts for it. TaxLens models this precisely — and the precision matters, because getting the mechanism wrong corrupts the EN 16931 category mapping.

FieldTypeDescription
How it's authoredruleAn override to rate_value: 0 on a commission VAT rate (base_type=commission_amount), carrying action.exemption_reason: "reverse_charge". Not an exempt action (which nulls the taxable base and breaks reconciliation).
What gates itderivedis_commission_cross_border_b2b, derived from commission_supplier_country vs the property country — falling back to the guest-derived is_cross_border_b2b when omitted. Not has_valid_vat_id alone (that would wrongly reverse-charge domestic B2B).
What you getEN 16931The commission line projects to category AE (rate 0, VATEX-EU-AE code, "Reverse charge" reason) — keeping the taxable base so AE reconciliation holds, instead of being mis-mapped to a zero-rated Z.
// POST /v1/tax/calculate
// US platform (MoR) intermediating a German hotel for a business guest.
// Room: German VAT, local (Art. 47), never reverse-charged.
// Commission: reverse-charged (AE) because supplier country != property country.
{
  "jurisdiction_code": "DE",
  "stay_date": "2026-09-01",
  "nights": 2,
  "nightly_rate": 180,
  "currency": "EUR",
  "property_type": "hotel",
  "merchant_of_record": "platform",
  "customer_type": "business",
  "business_tax_id": "DE123456789",
  "commission_supplier_country": "US",
  "line_items": [
    // amount is PER NIGHT: €36/night × 2 nights = €72 commission base
    { "description": "Platform commission", "item_type": "commission", "amount": 36 }
  ]
}
Place of supply, restated
A cross-border B2B guest does not get the room reverse-charged — the room is taxed where the property is, with local VAT. Only the intermediary/commission service is. See reverse charge for the legal basis and the EN 16931 category-AE mapping.

Self-billing: issue in the supplier's name (389)

As merchant of record you often issue the invoice in the property's (supplier's) name — self-billing. TaxLens issues this as a 389 document on its own fiscal counter, rendered as a UBL Invoice (not a credit note), with the merchant-of-record routing already frozen into collection_info.

# The supplier_legal_issuer_id is the host/owner (the named seller on a 389).
# A managed property can supply this from default_supplier_legal_issuer_id.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/bookings \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_reference": "OTA-RES-55120",
    "idempotency_key": "OTA-RES-55120",
    "supplier_legal_issuer_id": 77,
    "calculation": {
      "jurisdiction_code": "DE",
      "stay_date": "2026-09-01",
      "nights": 2,
      "nightly_rate": 180,
      "currency": "EUR",
      "merchant_of_record": "platform"
    }
  }'
Immutability + corrections
A live 389 freezes the booking just like a 380 — corrections go through a credit note (381), not an edit. Full lifecycle in self-billing and numbering & immutability.

Putting it together

A platform integration typically: calculates per channel with the right merchant_of_record, attaches its commission as a line item, sets commission_supplier_country so cross-border commission reverse-charges correctly, persists the booking, and issues self-billed (389) where it bills in the supplier's name. Inspect collection_info on every response to confirm the split before you rely on it.

POST/v1/tax/calculate
Sign in to run

A platform-as-MoR booking. Check collection_info in the response for the routing. Read-only.

Request body
{
  "jurisdiction_code": "DE",
  "stay_date": "2026-09-01",
  "nights": 2,
  "nightly_rate": 180,
  "currency": "EUR",
  "property_type": "hotel",
  "merchant_of_record": "platform",
  "is_marketplace": true
}

Sign in to run this against the live API. Read-only — nothing is saved.

Where to go next

Selling direct or managing inventory too? See Hotels and Property managers.