Property managers (PMS)
You run many units across many jurisdictions and sell them through several channels at once. The hard parts are scale and routing: map every property once, calculate per channel, and reconcile what each channel collected. This page sequences that for a short-term-rental / PMS operation.
What makes this persona different
Compared with a single hotel, a PMS has three multipliers working against it: many units, many jurisdictions (often a different city tax regime per unit), and many channels (your direct site plus one or more marketplaces). The same physical stay can carry different tax responsibilities depending on who the merchant of record is — so a single unit may need a different calculation per channel.
Two product features carry most of the weight here: property defaults (so you configure tax drivers once per unit, not per booking) and the channels & merchant-of-record model (so the same unit calculates correctly direct vs platform). Read that concepts page first — this is the persona where it bites.
Bulk-map your inventory
Start by getting every unit into TaxLens with the right jurisdiction and the right tax drivers. This is the highest-leverage step: a correct property default removes that field from every future booking and stops a whole class of mistakes.
- 1Register each unit and verify its mappingCreate a property per unit. TaxLens geocodes the address and maps it to a jurisdiction. For a portfolio, script the creation from your PMS export and then sweep the results: any unit with a non-resolved
mapping_statusor a jurisdiction that stops at the country level needs a manual fix before it goes live. The Properties dashboard lists mapping confidence for the whole portfolio at a glance. - 2Set tax-driver defaults per unitOn each property store its
property_type(e.g.vacation_rental,apartment,villa), classification, star rating where relevant, and the tax postal code. STR-class types behave differently from hotel-class types for things like France's 10% VAT — so the type is load-bearing, not cosmetic. - 3Attach issuer defaultsSet
default_legal_issuer_id(the invoice seller) and, where you bill on behalf of the unit owner,default_supplier_legal_issuer_id(the host/owner for self-billing). Bookings inherit these, so you never re-key the seller. Setting non-null issuer defaults is fiscal configuration and is org-admin gated.
List your mapped inventory and confirm each unit's jurisdiction + mapping status. Read-only.
Sign in to run this against the live API. Read-only — nothing is saved.
Calculate once per channel
The core PMS pattern: a unit sold on more than one channel is calculated once per channel, with a different merchant_of_record each time. On your direct channel you are the merchant of record ("property"); on a marketplace where the platform takes payment and remits, the platform is ("platform"). The engine returns the same room tax but routes collection responsibility differently, and channel-scoped rates fire only for the matching channel.
// POST /v1/tax/calculate — your own website / booking engine.
{
"property_id": 308,
"stay_date": "2026-08-10",
"nights": 4,
"nightly_rate": 150,
"currency": "EUR",
"number_of_guests": 3,
"adults": 2,
"merchant_of_record": "property"
}The collection_info block on the response tells you who collects what: platform_collected_components vs property_collected_components. A common real-world split is a marketplace collecting the city tax while the state/VAT stays property-collected — the engine expresses that per-rate via collection_model, so you don't hand-author mutual exemptions. The deep mechanics live in channels & merchant of record; the full request surface is in scenario fields.
adults when set, else by number_of_guests. On a 3-guest booking with two adults and a child, adults: 2 charges the per-night levy for two. See tiered & per-person taxes.Short-term-rental nuances to pass
STR units attract rules that hotels don't. These are all optional request fields — pass them where they apply, omit them for legacy behaviour:
- France para-hôtelier — a bare furnished rental is VAT-exempt; the 10% rate applies only to a para-hôtelier supply (≥3 of 4 services: breakfast, cleaning, linen, reception). Pass
para_hotelier: true|falseon STR-class units to select 10% vs exempt. Hotel-class types always get 10%. See VAT base & para-hôtelier. - Geofenced city districts — some city levies apply only inside a ZIP boundary (e.g. a downtown improvement district). Pass
property_postal_codeso those rules can gate correctly; absent, they fail-open to their pre-geofence behaviour. - Tax-inclusive display — if you advertise a gross, all-in nightly price (EU consumer law), set
price_includes_tax: trueand the engine reverse-derives the net base. See inclusive pricing & currency. - Cross-currency settlement — book in one currency, settle in another by setting
settlement_currency; the response adds the converted amount and FX rate.
guest_age evaluates against the first guest; adults is the surgical lever for the common minors case. See limitations.Persist, issue per channel, reconcile
Each calculation you intend to keep becomes a booking. Use a deterministic idempotency_key — your PMS reservation id plus the channel works well — so retries and replays never double-book.
# POST /v1/bookings — idempotency_key keyed on reservation + channel.
# A repeat returns the SAME booking (partial-unique on org + key).
curl -X POST https://api.taxlens.getdynamiq.ai/v1/bookings \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"property_id": 308,
"external_reference": "RES-88412",
"idempotency_key": "RES-88412:direct",
"calculation": {
"stay_date": "2026-08-10",
"nights": 4,
"nightly_rate": 150,
"currency": "EUR",
"number_of_guests": 3,
"adults": 2,
"merchant_of_record": "property"
}
}'Then issue per channel. On your direct bookings you issue a standard invoice (380) in your own issuer's name. Where a marketplace is the merchant of record and you bill in the owner/host's name, you issue a self-billed invoice (389) against the supplier issuer — the booking inherits default_supplier_legal_issuer_id from the property. See self-billing (389) and the OTAs & marketplaces page for the routing detail.
Reconcile: list persisted bookings to match against your PMS and each channel's payout. Read-only.
Sign in to run this against the live API. Read-only — nothing is saved.
For period close, the invoice tax report aggregates everything you issued across units and channels — pull it from Reports (API) or the Tax report screen, and export CSV for filing.
Operating at scale
- Drive everything from the API. A PMS integration should call calculate at quote time and create the booking on confirmation, fanning out per channel. Use the batch endpoint for backfills.
- Make every write idempotent. Reservation-derived keys make replays, webhook retries, and reconciliation jobs safe. See errors & idempotency.
- Use the dashboard for exceptions. Inspect a surprising number, issue a credit note, or fix a mapping by hand — the dashboard and API share one source of truth.
Next: tighten the routing model in channels & merchant of record, or read OTAs & marketplaces if you also operate as the platform.