Why the tax line decides an alert
StayParity compares a hotel's direct price with what Booking.com, Expedia and the sellers on Google Hotels quote for the same stay. Those prices don't arrive on the same footing. One includes VAT, another adds a city tax per person per night at checkout, and the rates change by season and whenever the law does.
So the tax line is the comparison. Read it wrong and a fair price looks like an undercut, or a real undercut hides inside the tax.
StayParity and Octroi are built by the same team. StayParity still gets no side door, only the contract any customer gets.
How StayParity reads Octroi
StayParity's production deployment reads Octroi's production the way any customer does: a bearer token, the public routes of the delivery contract, and the signature checked against a key it already holds. A daily sync runs this, and an operator can run it on demand:
- Ask
/tax-data/v1/releases/latestfor the newest release and the token's expiry. A release already cached ends the run. - Fetch
manifest.jsonandmanifest.json.sig, and verify the signature over the manifest's bytes with the pinned public key. - Parse the manifest. Its release must be the one
latestnamed, and its calculator's major version the one StayParity runs. - Fetch the changes, the licence and every country file the manifest lists. Each file's size and SHA-256 must match the manifest.
- Store each file as it's fetched, then record the release, its files and the signed manifest in one step. A release signed before the newest one cached is refused, so a replayed old release never becomes the newest, and so is a rollback.
Any failure deletes what the run stored, so a release is cached whole or not at all. The three newest releases are kept, with any release an applied law still names, and the rest deleted. StayParity's health view raises an alert at once when a release fails a check (its signature, a hash, the manifest, or an older release), after 72 hours without a good sync when Octroi can't be reached, and when the token is within 30 days of expiry.
Pinned, never fetched
Octroi serves its public key at /tax-data/v1/signing-key, but that comes from the same origin as the data, so trusting it would check nothing. StayParity holds the key in its own configuration. If Octroi rotates its key, StayParity's syncs fail as signature_invalid until the new key is set.
From a release to a price
A cached release changes nothing on its own. An operator applies a country from it, and only then does its law reach pricing. Applying reads the country's file back from storage and hashes it again; a file that no longer matches the SHA-256 the sync verified is refused and nothing is written.
Each hotel has a place in that law: a jurisdiction, a property type and, where the law has them, a class. For a stay, StayParity runs resolve and quote from @repo/tax-calculator, the calculator every customer gets, over the applied law:
resolvegives the place's jurisdictions and the charges in force on the night, with their rates.quoteprices a one-night stay for 2 adults and for 1, with no room price.- The VAT is the rate in force on the night at the nearest jurisdiction of the place that states one.
- What the guest owes on top of the price is every line outside the price, with the VAT on it, in the hotel's currency.
One night in Bucharest
The figure below isn't typed by hand. This page's build reads Romania's law from the current release, runs the charges through the calculator and reads the VAT from the law, so every number is what a customer gets from the same data.
ro, from 1 Aug 202511%For 2 adults, that's RON 20.00 owed on top of the room price. It's the number StayParity reads for the stay, with the VAT, when it puts an OTA's price beside the hotel's.
When the law is unknown
StayParity fails closed. A hotel with no place in the law is refused at admission, so no check runs for it at all. A stay whose law can't be stated exactly gets no law, and a stay with no law is never compared: the check fails before any vendor call, and nothing is read as a zero tax. For example, a quote that has unknowns or is partial, a stay with no VAT in force, or a charge whose last version ended with nothing after it.
That last case matters. A law that stops a charge with no successor is read as unresearched, not as no charge, even where the calculator itself answers in full:
StayParity's website badge makes no claim for such a stay either, and no alert opens, moves or closes from a check that compared nothing.
What's live
The current release carries 12 countries. An operator can apply any of them to StayParity:
AT AustriaBG BulgariaCY CyprusCZ CzechiaDE GermanyES SpainFR FranceGR GreeceHR CroatiaIT ItalyPT PortugalRO Romania
Each country's places, charges and sources are on the coverage page.