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.
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.
property_collectedline_itemplatform_collectedcollection_model can override one layer (e.g. MPF: city → platform, state → property)applies_to_channels) fire only for the matching channelThe scenario map, straight from the calculation surface:
| Field | Type | Description |
|---|---|---|
| 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_type | boolean / string | Help 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_country | string (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:
- 1Agency model — property is MoRSet
merchant_of_record: "property".collection_infosurfaces who owes what; your commission goes throughline_itemswithitem_type: "commission". The room taxes remain the property's to collect. - 2Merchant model — platform is MoRSet
merchant_of_record: "platform". The room tax is unchanged, butcollection_inforoutes every room category toplatform_collected_components— you collect and remit. - 3Marketplace 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_modelon the city-level rate — so a single calculation returns a splitcollection_info. No hand-authored mutual exemptions. - 4Channel-mutex ratesWhere 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_typeaccurately.
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.
| Field | Type | Description |
|---|---|---|
| How it's authored | rule | An 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 it | derived | is_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 get | EN 16931 | The 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 }
]
}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"
}
}'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.
A platform-as-MoR booking. Check collection_info in the response for the routing. Read-only.
{
"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.
- Channels & merchant of record — the routing model in depth.
- Reverse charge — the commission-line AE mechanism and EN 16931 mapping.
- Self-billing — issuing 389 in the supplier's name.
- Scenario fields and Calculate tax (API) — the full request surface. Limitations covers what's deferred (live VIES, TOMS margin scheme, MPF thresholds).
Selling direct or managing inventory too? See Hotels and Property managers.