TaxLens API Docs
TaxLens calculates, stores, and audits accommodation taxes for hotel, OTA, and marketplace booking flows. Start with an in-app calculation, then wire the same request shape into your backend with an API key.
Get Started
The normal integration path is calculate first, persist once the booking is confirmed, then use booking endpoints for downstream operations.
Use a company email address, confirm the verification link, and finish organization setup in Settings.
API keys are enabled manually during onboarding. Until then, the calculator and jurisdiction browser stay available.
Send booking dates, jurisdiction, price, guests, property type, and channel context to get a tax breakdown.
Store the original request and response with an idempotency key so voids, refunds, and audits refer to one frozen snapshot.
Authentication
All API requests (except /health) require an API key passed in the X-API-Key header. New organizations can browse data and use the calculator immediately; production API keys are enabled after onboarding review.
curl https://api.taxlens.getdynamiq.ai/v1/jurisdictions \
-H "X-API-Key: <your-api-key>"Requests without a valid key receive a 401 Unauthorized response.
Errors
The API uses standard HTTP status codes. Error responses include a JSON body with a detail field.
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created (for POST endpoints) |
| 400 | Bad request — invalid parameters |
| 401 | Unauthorized — missing or invalid API key |
| 404 | Not found — resource does not exist |
| 409 | Conflict — resource already exists |
| 422 | Validation error — check field constraints |
| 500 | Internal server error |
{
"detail": "Jurisdiction not found: XX-YY-ZZ"
}Tax Calculation
/v1/tax/calculateCalculate Tax
Calculate all applicable accommodation taxes for a specific stay. The engine traverses the jurisdiction hierarchy (country → state → city), collects all active rates, evaluates rules (exemptions, reductions, caps, surcharges), and returns an itemized breakdown. Supports flat, percentage, and tiered rate types with per-stay and per-night charging models.
Two expanded inputs are available for non-uniform stays: pass `nightly_rates` (list of { date, rate }) for variable-rate stays (e.g. promo-priced opening night, weekly-rate discounts) — it overrides `nightly_rate × nights`. Pass `guests` (list of GuestSpec) for mixed-guest stays where exemptions depend on per-guest details (e.g. one resident + one tourist) — it overrides the scalar `guest_*` fields and `number_of_guests`. You can also skip `jurisdiction_code` and provide `lat` + `lng` instead; the geocoder will resolve the matching jurisdiction.
Request Body
jurisdiction_codestringoptionalISO jurisdiction code (e.g. "US-NY-NYC", "HR-19-DBV"). Optional if lat+lng provided.
latfloatoptionalLatitude. Provide with `lng` instead of `jurisdiction_code` to auto-resolve via geocoder.
lngfloatoptionalLongitude. Paired with `lat`.
stay_datedaterequiredCheck-in date (YYYY-MM-DD).
checkout_datedateoptionalCheck-out date. Optional, but when supplied it must equal `stay_date + nights`.
nightly_ratedecimalrequiredRoom rate per night. Must be > 0. Overridden when `nightly_rates` is provided.
currencystringrequiredISO 4217 currency code (e.g. "USD", "EUR", "JPY").
nightsintegerrequiredNumber of nights (>= 1).
number_of_guestsintegeroptionalGuest count. Affects per-person taxes. Auto-derived from `guests.length` when present.
Default: 1
property_typestringoptionalPrimary lodging product type. Inputs are normalized before rule matching; for example short_term_rental -> str, villa -> vacation_rental, and aparthotel -> apartment_hotel.
hotelmotelresortboutiquepalaceapartment_hotelhostelbnbstrvacation_rentalcampgroundapartmentlong_term_rentalDefault: "hotel"
property_classificationstringoptionalOptional authority-assigned lodging class when property_type/star_rating are not precise enough, e.g. Dubai holiday_home_deluxe.
star_ratingintegeroptionalHotel star rating (1-5). Used for tiered rates (e.g. Florence, Milan).
guest_typestringoptionalGuest classification: standard, business, government, etc.
Default: "standard"
guest_ageintegeroptionalGuest age. Triggers age-based exemptions/reductions (e.g. Croatia: under 12 exempt, 12-17 half price). Overridden when `guests` is provided.
guest_nationalitystringoptionalISO country code. Triggers nationality-based rules (e.g. Bhutan: SAARC countries exempt). Overridden when `guests` is provided.
is_marketplacebooleanoptionalWhether booked through an OTA marketplace. Triggers platform surcharges (e.g. Denver +2%).
Default: false
platform_typestringoptionalBooking channel: direct, ota, metasearch, etc.
Default: "direct"
is_bundledbooleanoptionalWhether the room is part of a bundled package.
Default: false
property_room_countintegeroptionalTotal rooms in the property (≥0). Drives operator-size tier rules (e.g. Croatia's per-bed tax varies by property size).
bedroom_countintegeroptionalBedrooms in the booked unit (≥0). Used by per-bedroom cap rules.
payment_methodstringoptionalcard, cash, bank_transfer, mobile_money. Some jurisdictions exempt cash transactions below a threshold.
Default: "unknown"
booking_purposestringoptionalbusiness, leisure, government, military, diplomatic. Drives exempt-entity rules.
guestslist[GuestSpec]optionalPer-guest details. Overrides scalar `guest_age` / `guest_nationality` / `number_of_guests` when present. GuestSpec: { nationality?: string, age?: int (0-130), type?: string (canonical guest_type: "resident"|"non_resident"|"disabled"|"disabled_companion"|"diplomat"|"military"|"government"|"ngo"|"student"|"senior"|…), disability_percentage?: int (0-100) }. Legacy `residency_status` / `exempt_entity_type` are still accepted as input aliases for `type`.
nightly_rateslist[NightlyRateEntry]optionalPer-night rate schedule. Overrides `nightly_rate × nights` when present. NightlyRateEntry: { date: "YYYY-MM-DD", rate: decimal > 0 }. Useful for promo-priced opening nights or weekly-rate discounts.
line_itemslist[LineItem]optionalAdditional itemized charges. Each item is taxed by rates matching its `item_type` to `tax_category.base_type`; for example, resort fees can be taxed separately from room revenue and cleaning fees can follow fee-specific VAT treatment.
descriptionstringitem_typeone of the values belowamountdecimal > 0, per nightnightsoptional integer; defaults to booking nightstaxableoptional boolean; defaults to trueroomresort_feecleaning_feeservice_feedestination_feesustainability_feeamenity_feecommissionotherRoom line items are ignored. nightly_rate x nights remains the canonical room base.
price_includes_taxbooleanoptionalIf true, the engine treats `nightly_rate` (and line_item amounts) as tax-inclusive and reverse-derives the base. Used by EU consumer-protection display compliance and Booking.com TaxPolicy Type=Inclusive.
Default: false
merchant_of_recordstringoptionalWho legally remits the tax. `property` = hotel/host (Booking agency, Vrbo non-MPF). `platform` = OTA/marketplace (Booking MoR, Airbnb MPF, ETG MoR). `none` or omitted = engine falls back to is_marketplace heuristic. Drives `collection_info.platform_collected_components` / `property_collected_components` in the response.
customer_typestringoptionalCustomer profile: consumer | business | government | diplomat | ngo | exempt_entity. Drives B2B reverse-charge + government/diplomat exemptions. Defaults to consumer.
business_tax_idstringoptionalBusiness VAT ID / EIN / GSTIN. Truthy presence exposes `has_valid_vat_id` derived field on rule context (used for B2B reverse-charge gating).
settlement_currencystringoptionalISO 4217 currency the platform will settle in (may differ from booking currency). Response includes `tax_breakdown.amount_in_settlement_currency` and `settlement_fx_rate`.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/tax/calculate \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-api-key>" \
-d '{
"jurisdiction_code": "ES-CT-BCN",
"stay_date": "2026-06-15",
"checkout_date": "2026-06-17",
"nightly_rate": 180,
"currency": "EUR",
"nights": 2,
"number_of_guests": 2,
"property_type": "hotel",
"star_rating": 4
}'
# Compare another difficult jurisdiction with tax-on-tax:
curl -X POST https://api.taxlens.getdynamiq.ai/v1/tax/calculate \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-api-key>" \
-d '{
"jurisdiction_code": "CA-ON-TOR",
"stay_date": "2026-06-15",
"nightly_rate": 220,
"currency": "CAD",
"nights": 2,
"number_of_guests": 2,
"property_type": "hotel"
}'{
"calculation_id": "calc_c484953024e7b4e04f7e9b05ba1ca866",
"jurisdiction": {
"code": "ES-CT-BCN",
"name": "Barcelona",
"path": "ES.CT.BCN"
},
"input": {
"nightly_rate": 180,
"nights": 2,
"subtotal": 360,
"currency": "EUR",
"property_type": "hotel"
},
"tax_breakdown": {
"components": [
{
"name": "VAT / Sales Tax (reduced rate)",
"category_code": "vat_reduced",
"jurisdiction_code": "ES",
"jurisdiction_level": "country",
"rate": 0.1,
"rate_type": "percentage",
"taxable_amount": "360",
"tax_amount": "36.00",
"legal_reference": "Ley 37/1992, de 28 de diciembre, del Impuesto sobre el Valor Anadido, Articulo 91.uno.2.2 - servicios de hosteleria al tipo reducido del 10%",
"authority": "Agencia Tributaria",
"line_item_index": null,
"collected_by": null
},
{
"name": "Tourism Tax (flat per person per night)",
"category_code": "tourism_flat_person_night",
"jurisdiction_code": "ES-CT",
"jurisdiction_level": "state",
"rate": 0.0,
"rate_type": "tiered",
"taxable_amount": null,
"tax_amount": "7.20",
"legal_reference": "Ley 2/2026 and ATC IEET tariff table, engine-compatible Catalonia regional IEET base from 2026-04-01. Barcelona municipality stacks its separate recargo in ES-CT-BCN tax_rate 5117.",
"authority": "Agencia Tributaria de Catalunya",
"line_item_index": null,
"collected_by": null
},
{
"name": "Tourism Tax (flat per person per night)",
"category_code": "tourism_flat_person_night",
"jurisdiction_code": "ES-CT-BCN",
"jurisdiction_level": "city",
"rate": 0.0,
"rate_type": "tiered",
"taxable_amount": null,
"tax_amount": "26.40",
"legal_reference": "Ley 2/2026 and ATC IEET tariff table, decomposed for TaxLens additive semantics: this ES-CT-BCN row contains only the Barcelona municipal recargo from 2026-04-01. The Catalan regional base remains in ES-CT tax_rate 3475.",
"authority": "Ajuntament de Barcelona / Agencia Tributaria de Catalunya",
"line_item_index": null,
"collected_by": null
}
],
"total_tax": "69.60",
"effective_rate": "0.1933333333333333333333333333",
"currency": "EUR",
"subtotal_excluding_tax": "360",
"subtotal_including_tax": "429.60",
"amount_in_settlement_currency": null,
"settlement_currency": null,
"settlement_fx_rate": null,
"settlement_fx_error": null
},
"total_with_tax": "429.60",
"rules_applied": [
{
"rule_id": 1460,
"name": "Spanish IVA does not apply in Canary Islands (IGIC zone)",
"rule_type": "exemption",
"result": "skipped"
},
{
"rule_id": 620,
"name": "Catalonia IEET - capped at 7 nights",
"rule_type": "cap",
"result": "skipped"
},
{
"rule_id": 627,
"name": "Catalonia IEET - under-17 exemption (statewide)",
"rule_type": "exemption",
"result": "skipped"
},
{
"rule_id": 621,
"name": "Barcelona child exemption (under 17)",
"rule_type": "exemption",
"result": "skipped"
},
{
"rule_id": 1363,
"name": "Barcelona IEET - EU social-tourism program exemption",
"rule_type": "exemption",
"result": "skipped"
},
{
"rule_id": 1362,
"name": "Barcelona IEET - health/medical stay exemption",
"rule_type": "exemption",
"result": "skipped"
},
{
"rule_id": 1361,
"name": "Barcelona IEET - 7-night cap per continuous stay",
"rule_type": "cap",
"result": "applied"
}
],
"collection_info": {
"who_collects": "property",
"taxable_base": "room_rate",
"platform_must_collect": false,
"notes": [],
"merchant_of_record": null,
"platform_collected_components": [],
"property_collected_components": [
"vat_reduced",
"tourism_flat_person_night",
"tourism_flat_person_night"
]
},
"calculated_at": "2026-05-26T23:32:49.396031Z",
"data_version": null
}/v1/tax/calculate/batchBatch Calculate
Calculate taxes for multiple stays in a single request. Each item in the batch is calculated independently. Useful for comparing rates across jurisdictions or processing bulk bookings.
Request Body
calculationsarrayrequiredArray of TaxCalculationRequest objects (same schema as single calculate endpoint).
curl -X POST https://api.taxlens.getdynamiq.ai/v1/tax/calculate/batch \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-api-key>" \
-d '{
"calculations": [
{
"jurisdiction_code": "ES-CT-BCN",
"stay_date": "2026-06-15",
"checkout_date": "2026-06-17",
"nightly_rate": 180,
"currency": "EUR",
"nights": 2,
"number_of_guests": 2,
"property_type": "hotel",
"star_rating": 4
},
{
"jurisdiction_code": "CA-ON-TOR",
"stay_date": "2026-06-15",
"nightly_rate": 220,
"currency": "CAD",
"nights": 2,
"number_of_guests": 2,
"property_type": "hotel"
}
]
}'{
"results": [
{
"id": "0",
"total_tax": "69.60",
"effective_rate": "0.1933333333333333333333333333",
"components": [
{
"name": "VAT / Sales Tax (reduced rate)",
"category_code": "vat_reduced",
"jurisdiction_code": "ES",
"jurisdiction_level": "country",
"rate": 0.1,
"rate_type": "percentage",
"taxable_amount": "360",
"tax_amount": "36.00",
"legal_reference": "Ley 37/1992, Articulo 91.uno.2.2",
"authority": "Agencia Tributaria",
"line_item_index": null,
"collected_by": null
},
{
"name": "Tourism Tax (flat per person per night)",
"category_code": "tourism_flat_person_night",
"jurisdiction_code": "ES-CT",
"jurisdiction_level": "state",
"rate": 0.0,
"rate_type": "tiered",
"taxable_amount": null,
"tax_amount": "7.20",
"legal_reference": "Ley 2/2026 and ATC IEET tariff table",
"authority": "Agencia Tributaria de Catalunya",
"line_item_index": null,
"collected_by": null
},
{
"name": "Tourism Tax (flat per person per night)",
"category_code": "tourism_flat_person_night",
"jurisdiction_code": "ES-CT-BCN",
"jurisdiction_level": "city",
"rate": 0.0,
"rate_type": "tiered",
"taxable_amount": null,
"tax_amount": "26.40",
"legal_reference": "Ley 2/2026 and ATC IEET tariff table",
"authority": "Ajuntament de Barcelona / Agencia Tributaria de Catalunya",
"line_item_index": null,
"collected_by": null
}
],
"error": null
},
{
"id": "1",
"total_tax": "99.46",
"effective_rate": "0.2260454545454545454545454545",
"components": [
{
"name": "Municipal Surcharge (%)",
"category_code": "municipal_pct",
"jurisdiction_code": "CA-ON-TOR",
"jurisdiction_level": "city",
"rate": 0.085,
"rate_type": "percentage",
"taxable_amount": "440",
"tax_amount": "37.40",
"legal_reference": "City of Toronto Bylaw 1259-2024",
"authority": "City of Toronto",
"line_item_index": null,
"collected_by": null
},
{
"name": "VAT / Sales Tax (standard)",
"category_code": "vat_standard",
"jurisdiction_code": "CA",
"jurisdiction_level": "country",
"rate": 0.05,
"rate_type": "percentage",
"taxable_amount": "477.40",
"tax_amount": "23.87",
"legal_reference": "Excise Tax Act, Part IX",
"authority": "Canada Revenue Agency",
"line_item_index": null,
"collected_by": null
},
{
"name": "VAT / Sales Tax (standard)",
"category_code": "vat_standard",
"jurisdiction_code": "CA-ON",
"jurisdiction_level": "province",
"rate": 0.08,
"rate_type": "percentage",
"taxable_amount": "477.40",
"tax_amount": "38.19",
"legal_reference": "Excise Tax Act, Part IX; Ontario HST provincial portion",
"authority": "Ontario Ministry of Finance",
"line_item_index": null,
"collected_by": null
}
],
"error": null
}
]
}Jurisdictions
/v1/jurisdictionsList Jurisdictions
Retrieve a paginated list of jurisdictions. Supports filtering by country, type, status, and parent. Use this to discover available jurisdictions in the hierarchy.
Parameters
country_codestringoptionalFilter by ISO country code (e.g. "US", "HR")
jurisdiction_typestringoptionalFilter by type: country, state, province, region, city, district, special_zone
statusstringoptionalFilter by status: active, inactive, pending
parent_codestringoptionalFilter by parent jurisdiction code
qstringoptionalSearch by name or code (case-insensitive)
limitintegeroptionalMax results (1-500)
Default: 100
offsetintegeroptionalPagination offset
Default: 0
curl https://api.taxlens.getdynamiq.ai/v1/jurisdictions?country_code=US&jurisdiction_type=state \
-H "X-API-Key: <your-api-key>"[
{
"id": 2,
"code": "US-NY",
"name": "New York",
"local_name": null,
"jurisdiction_type": "state",
"path": "US.NY",
"parent_id": 1,
"country_code": "US",
"subdivision_code": "US-NY",
"timezone": "America/New_York",
"currency_code": "USD",
"status": "active",
"created_by": "system",
"created_at": "2026-03-12T00:52:31.340Z",
"updated_at": "2026-03-12T00:52:31.340Z"
}
]/v1/jurisdictions/{code}Get Jurisdiction
Retrieve a single jurisdiction by its code.
Parameters
codestringrequiredJurisdiction code (path parameter)
curl https://api.taxlens.getdynamiq.ai/v1/jurisdictions/US-NY-NYC \
-H "X-API-Key: <your-api-key>"{
"id": 3,
"code": "US-NY-NYC",
"name": "New York City",
"jurisdiction_type": "city",
"path": "US.NY.NYC",
"parent_id": 2,
"country_code": "US",
"subdivision_code": "US-NY",
"timezone": "America/New_York",
"currency_code": "USD",
"status": "active"
}/v1/jurisdictions/{code}/childrenGet Children
Retrieve direct child jurisdictions. Use this for progressive disclosure — start from a country and drill down through states/regions to cities.
Parameters
codestringrequiredParent jurisdiction code (path parameter)
curl https://api.taxlens.getdynamiq.ai/v1/jurisdictions/US/children \
-H "X-API-Key: <your-api-key>"[
{
"id": 40,
"code": "US-CA",
"name": "California",
"jurisdiction_type": "state",
"path": "US.CA",
"parent_id": 1,
"country_code": "US",
"subdivision_code": "US-CA",
"timezone": "America/Los_Angeles",
"currency_code": "USD",
"status": "active"
},
{
"id": 41,
"code": "US-CO",
"name": "Colorado",
"jurisdiction_type": "state",
"path": "US.CO",
"parent_id": 1,
"country_code": "US",
"subdivision_code": "US-CO",
"timezone": "America/Denver",
"currency_code": "USD",
"status": "active"
},
{
"id": 2,
"code": "US-NY",
"name": "New York",
"jurisdiction_type": "state",
"path": "US.NY",
"parent_id": 1,
"country_code": "US",
"subdivision_code": "US-NY",
"timezone": "America/New_York",
"currency_code": "USD",
"status": "active"
}
]/v1/jurisdictions/{code}/ancestorsGet Ancestors
Retrieve the full ancestor chain from root to the parent of the given jurisdiction. Useful for building breadcrumb trails. The jurisdiction itself is NOT included.
Parameters
codestringrequiredJurisdiction code (path parameter)
curl https://api.taxlens.getdynamiq.ai/v1/jurisdictions/US-NY-NYC/ancestors \
-H "X-API-Key: <your-api-key>"[
{
"id": 1,
"code": "US",
"name": "United States",
"jurisdiction_type": "country",
"path": "US",
"parent_id": null,
"country_code": "US",
"timezone": "America/New_York",
"currency_code": "USD",
"status": "active"
},
{
"id": 2,
"code": "US-NY",
"name": "New York",
"jurisdiction_type": "state",
"path": "US.NY",
"parent_id": 1,
"country_code": "US",
"subdivision_code": "US-NY",
"timezone": "America/New_York",
"currency_code": "USD",
"status": "active"
}
]Coverage Map
/v1/map/coverageCoverage Rollup
Return customer-safe country rollups, admin-region rollups, and local jurisdiction marker GeoJSON for the authenticated organization. This is the same read-only payload behind the in-app coverage map. Use it to show where TaxLens has active data, how deep the jurisdiction graph is by country, and which cities or region-level jurisdictions have marker coordinates. Marker coordinates come from jurisdiction metadata first, then deterministic vendored Natural Earth data when available.
curl https://api.taxlens.getdynamiq.ai/v1/map/coverage \
-H "X-API-Key: <your-api-key>"{
"generated_at": "2026-06-01T12:00:00Z",
"totals": {
"countries": 245,
"jurisdictions": 1024,
"cities": 470,
"active_rates": 1130,
"active_rules": 660,
"sources": 830,
"city_markers": 430,
"missing_city_markers": 40,
"marker_jurisdictions": 500,
"jurisdiction_markers": 430,
"missing_jurisdiction_markers": 70
},
"countries": [
{
"code": "ES",
"name": "Spain",
"currency_code": "EUR",
"jurisdiction_count": 18,
"subjurisdiction_count": 17,
"city_count": 6,
"active_rate_count": 22,
"active_rule_count": 14,
"source_count": 9,
"coverage_score": 84
}
],
"regions": [
{
"code": "ES-CT",
"name": "Catalonia",
"country_code": "ES",
"jurisdiction_type": "region",
"child_count": 1,
"active_rate_count": 1,
"active_rule_count": 2,
"source_count": 1
}
],
"city_markers": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [2.1734, 41.3851]
},
"properties": {
"code": "ES-CT-BCN",
"name": "Barcelona",
"country_code": "ES",
"jurisdiction_type": "city",
"currency_code": "EUR",
"active_rate_count": 3,
"active_rule_count": 4,
"coordinate_source": "metadata"
}
}
]
}
}/v1/map/jurisdictions/{code}Jurisdiction Dossier
Return a focused dossier for a clicked country, region, city, or marker without recomputing the full global map payload. The response includes hierarchy context, child jurisdictions, direct and inherited effective rates, active rules, monitored sources, the country rollup, and optional marker geometry. This endpoint is useful when a partner wants a drill-down panel or support view that explains why a jurisdiction is covered.
Parameters
codestringrequiredJurisdiction code to inspect (for example ES, ES-CT, or ES-CT-BCN).
curl https://api.taxlens.getdynamiq.ai/v1/map/jurisdictions/ES-CT-BCN \
-H "X-API-Key: <your-api-key>"{
"generated_at": "2026-06-01T12:00:00Z",
"jurisdiction": {
"id": 301,
"code": "ES-CT-BCN",
"name": "Barcelona",
"local_name": "Barcelona",
"jurisdiction_type": "city",
"path": "ES.CT.BCN",
"parent_id": 201,
"country_code": "ES",
"subdivision_code": "ES-CT",
"timezone": "Europe/Madrid",
"currency_code": "EUR",
"status": "active",
"created_at": "2026-03-12T00:00:00Z",
"updated_at": "2026-05-28T20:40:00Z"
},
"ancestors": [
{
"id": 100,
"code": "ES",
"name": "Spain",
"local_name": "Espana",
"jurisdiction_type": "country",
"path": "ES",
"parent_id": null,
"country_code": "ES",
"subdivision_code": null,
"timezone": "Europe/Madrid",
"currency_code": "EUR",
"status": "active",
"created_at": "2026-03-12T00:00:00Z",
"updated_at": "2026-05-28T20:40:00Z"
},
{
"id": 201,
"code": "ES-CT",
"name": "Catalonia",
"local_name": "Catalunya",
"jurisdiction_type": "region",
"path": "ES.CT",
"parent_id": 100,
"country_code": "ES",
"subdivision_code": "ES-CT",
"timezone": "Europe/Madrid",
"currency_code": "EUR",
"status": "active",
"created_at": "2026-03-12T00:00:00Z",
"updated_at": "2026-05-28T20:40:00Z"
}
],
"children": [],
"country": {
"code": "ES",
"name": "Spain",
"currency_code": "EUR",
"jurisdiction_count": 18,
"subjurisdiction_count": 17,
"city_count": 6,
"active_rate_count": 22,
"active_rule_count": 14,
"source_count": 9,
"coverage_score": 84
},
"direct_rates": [
{
"id": 5117,
"jurisdiction_id": 301,
"jurisdiction_code": "ES-CT-BCN",
"tax_category_id": 44,
"tax_category_code": "tourism_flat_person_night",
"rate_type": "tiered",
"rate_value": null,
"currency_code": "EUR",
"tiers": [{ "min": 4, "value": 6.6 }],
"tier_type": "single_amount",
"percentage_basis": null,
"basis_value": null,
"basis_currency": null,
"enacted_date": "2026-02-01",
"effective_start": "2026-04-01",
"effective_end": null,
"applicability_start": null,
"announcement_date": "2026-02-01",
"calculation_order": 100,
"base_includes": [],
"legal_reference": "Ley 2/2026 and ATC IEET tariff table",
"legal_uri": "https://atc.gencat.cat/",
"source_url": "https://atc.gencat.cat/",
"authority_name": "Ajuntament de Barcelona / Agencia Tributaria de Catalunya",
"version": 1,
"supersedes_id": null,
"status": "scheduled",
"collection_model": null,
"taxable_amount_rule": null
}
],
"effective_rates": [
{
"id": 120,
"tax_category_code": "vat_reduced",
"tax_category_name": "VAT / Sales Tax (reduced rate)",
"rate_type": "percentage",
"rate_value": "0.10",
"currency_code": null,
"tiers": null,
"effective_start": "2024-01-01",
"effective_end": null,
"legal_reference": "Ley 37/1992, Articulo 91.uno.2.2",
"authority_name": "Agencia Tributaria",
"source_url": "https://sede.agenciatributaria.gob.es/",
"attached_to_jurisdiction_id": 100,
"attached_to_jurisdiction_code": "ES",
"attached_to_jurisdiction_name": "Spain",
"attached_to_jurisdiction_type": "country",
"is_inherited": true
}
],
"rules": [
{
"id": 1361,
"tax_rate_id": 5117,
"jurisdiction_id": 301,
"jurisdiction_code": "ES-CT-BCN",
"rule_type": "cap",
"priority": 100,
"name": "Barcelona IEET - 7-night cap per continuous stay",
"description": "Caps city tourism tax at 7 nights.",
"conditions": { "operator": "AND", "rules": [] },
"action": { "type": "cap", "max_nights": 7 },
"effective_start": "2026-04-01",
"effective_end": null,
"enacted_date": "2026-02-01",
"legal_reference": "Ley 2/2026",
"legal_uri": "https://atc.gencat.cat/",
"authority_name": "Ajuntament de Barcelona",
"status": "active",
"version": 1,
"supersedes_id": null
}
],
"sources": [
{
"id": 88,
"jurisdiction_code": "ES-CT-BCN",
"url": "https://atc.gencat.cat/",
"source_type": "tax_authority",
"language": "es",
"check_frequency_days": 7,
"last_checked_at": "2026-05-28T20:40:00Z",
"last_content_hash": "sha256:example",
"status": "active",
"created_at": "2026-03-12T00:00:00Z",
"updated_at": "2026-05-28T20:40:00Z"
}
],
"marker": {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [2.1734, 41.3851]
},
"properties": {
"code": "ES-CT-BCN",
"name": "Barcelona",
"country_code": "ES",
"jurisdiction_type": "city",
"currency_code": "EUR",
"active_rate_count": 3,
"active_rule_count": 4,
"coordinate_source": "metadata"
}
},
"metadata": {
"map_centroid": { "lat": 41.3851, "lng": 2.1734 }
}
}Tax Rates
/v1/ratesList Rates
Retrieve a paginated list of tax rates. Rates define the actual tax amounts: percentage, flat, or tiered.
Parameters
jurisdiction_codestringoptionalFilter by jurisdiction code
category_codestringoptionalFilter by tax category code
statusstringoptionalFilter: active, draft, approved, scheduled, superseded, rejected, needs_review
effective_datedateoptionalFilter rates effective on this date
limitintegeroptionalMax results (1-500)
Default: 100
offsetintegeroptionalPagination offset
Default: 0
curl "https://api.taxlens.getdynamiq.ai/v1/rates?jurisdiction_code=US-NY-NYC&status=active" \
-H "X-API-Key: <your-api-key>"[
{
"id": 3,
"jurisdiction_code": "US-NY-NYC",
"tax_category_code": "occ_pct",
"rate_type": "percentage",
"rate_value": 0.05875,
"currency_code": "USD",
"calculation_order": 30,
"effective_start": "2024-01-01",
"status": "active",
"legal_reference": "NYC Admin Code §11-2502",
"authority_name": "NYC Department of Finance",
"source_url": "https://www.nyc.gov/site/finance/property/hotel-room-occupancy-tax.page"
}
]/v1/rates/lookupLookup Rates
Look up all active rates for a jurisdiction on a given date, including inherited rates from parent jurisdictions. Returns the combined percentage rate — useful for displaying the total tax rate to users before they book.
Parameters
jurisdiction_codestringrequiredJurisdiction code to look up
effective_datedateoptionalDate to check (defaults to today)
curl "https://api.taxlens.getdynamiq.ai/v1/rates/lookup?jurisdiction_code=US-NY-NYC" \
-H "X-API-Key: <your-api-key>"{
"jurisdiction": {
"code": "US-NY-NYC",
"name": "New York City",
"path": "US.NY.NYC"
},
"date": "2026-03-12",
"rates": [
{
"jurisdiction_code": "US-NY",
"tax_category_code": "vat_standard",
"rate_type": "percentage",
"rate_value": 0.04,
"currency_code": "USD",
"status": "active",
"effective_start": "2024-01-01"
},
{
"jurisdiction_code": "US-NY-NYC",
"tax_category_code": "occ_pct",
"rate_type": "percentage",
"rate_value": 0.05875,
"currency_code": "USD",
"status": "active",
"effective_start": "2024-01-01"
}
],
"combined_percentage_rate": 0.14375
}/v1/rates/{rate_id}Get Rate
Retrieve a single tax rate by ID.
Parameters
rate_idintegerrequiredRate ID (path parameter)
curl https://api.taxlens.getdynamiq.ai/v1/rates/3 \
-H "X-API-Key: <your-api-key>"{
"id": 3,
"jurisdiction_code": "US-NY-NYC",
"tax_category_code": "occ_pct",
"rate_type": "percentage",
"rate_value": 0.05875,
"currency_code": "USD",
"tiers": null,
"calculation_order": 30,
"base_includes": ["base_amount"],
"effective_start": "2024-01-01",
"effective_end": null,
"status": "active",
"legal_reference": "NYC Admin Code §11-2502",
"authority_name": "NYC Department of Finance",
"source_url": "https://www.nyc.gov/site/finance/property/hotel-room-occupancy-tax.page"
}Tax Rules
/v1/rulesList Rules
Retrieve tax rules. Rules modify how rates are applied: exemptions (zero out tax), reductions (percentage discount), caps (max nights/amount), surcharges (additional %), and overrides.
Parameters
jurisdiction_codestringoptionalFilter by jurisdiction code
rule_typestringoptionalFilter: exemption, reduction, surcharge, cap, override, condition, threshold
statusstringoptionalFilter by status
tax_rate_idintegeroptionalFilter by associated rate ID
limitintegeroptionalMax results (1-500)
Default: 100
offsetintegeroptionalPagination offset
Default: 0
curl "https://api.taxlens.getdynamiq.ai/v1/rules?jurisdiction_code=HR-19-DBV" \
-H "X-API-Key: <your-api-key>"[
{
"id": 21,
"jurisdiction_code": "HR-19-DBV",
"rule_type": "exemption",
"priority": 100,
"name": "Children Under 12 Exemption",
"conditions": {
"operator": "AND",
"rules": [
{ "field": "guest_age", "op": "<", "value": 12 }
]
},
"action": { "type": "exempt" },
"effective_start": "2025-04-01",
"status": "active"
},
{
"id": 22,
"jurisdiction_code": "HR-19-DBV",
"rule_type": "reduction",
"priority": 90,
"name": "Youth 50% Reduction (Ages 12-17)",
"conditions": {
"operator": "AND",
"rules": [
{ "field": "guest_age", "op": ">=", "value": 12 },
{ "field": "guest_age", "op": "<=", "value": 17 }
]
},
"action": { "reduction_percent": 0.5 },
"effective_start": "2025-04-01",
"status": "active"
}
]/v1/rules/{rule_id}Get Rule
Retrieve a single tax rule by ID.
Parameters
rule_idintegerrequiredRule ID (path parameter)
curl https://api.taxlens.getdynamiq.ai/v1/rules/21 \
-H "X-API-Key: <your-api-key>"{
"id": 21,
"jurisdiction_code": "HR-19-DBV",
"rule_type": "exemption",
"priority": 100,
"name": "Children Under 12 Exemption",
"conditions": {
"operator": "AND",
"rules": [
{ "field": "guest_age", "op": "<", "value": 12 }
]
},
"action": { "type": "exempt" },
"effective_start": "2025-04-01",
"effective_end": null,
"status": "active"
}Bookings
/v1/bookingsCreate Booking
Persist a tax calculation as a booking. The endpoint runs the calculation server-side (same engine as /v1/tax/calculate), stores the request + response snapshots, and returns the booking record. Pass `property_id` to use a managed property's jurisdiction, tax-driver, property default seller, and property default supplier when the booking omits those fields. Pass an `idempotency_key` to make retries safe: re-POSTing with the same key + org returns the original booking with HTTP 200 instead of creating a duplicate (201 only on first write). Bookings are org-scoped — the org is resolved from the JWT and cannot be set by the caller.
Request Body
calculationobjectrequiredA full TaxCalculationRequest payload (same schema as POST /v1/tax/calculate). Re-run server-side to produce the booking totals. For e-invoicing it also accepts `buyer_name` (EN 16931 BT-44) and `buyer_address` ({line1, line2, city, region, postal_code, country_code} — BG-8); these don't affect tax but are required for a PEPPOL-valid invoice.
property_idintegeroptionalManaged property/listing ID. If calculation omits jurisdiction or tax drivers, the property fills them. If booking omits legal issuer fields, the property default seller and property default supplier can fill them before org-level fallbacks.
legal_issuer_idintegeroptionalThe legal entity that issues the invoice (must belong to your org). Explicit value wins; when omitted TaxLens uses the property default seller, then the org default issuer. Drives the invoice seller party.
buyer_referencestringoptionalEN 16931 BT-10 buyer reference (PO number, XRechnung Leitweg-ID, French B2G reference). Stored verbatim for the invoice. Required for a B2B PEPPOL invoice (PEPPOL-EN16931-R003).
buyer_endpoint_idstringoptionalBuyer PEPPOL electronic address (EN 16931 BT-49) — the network routing address. REQUIRED for a B2B PEPPOL invoice (PEPPOL-EN16931-R010); without it /issue and /document/ubl are blocked (422).
buyer_endpoint_schemestringoptional4-digit EAS/ICD scheme code for buyer_endpoint_id (e.g. 9930 for VAT, 0204 for Leitweg-ID). Required alongside buyer_endpoint_id.
supplier_legal_issuer_idintegeroptionalHost/supplier legal entity for a self-billed (389) invoice. Explicit value wins; when omitted TaxLens uses the property default supplier. The platform/OTA issues IN THIS ENTITY'S NAME and burns the host's fiscal sequence at /issue with self_billed=true.
payment_termsstringoptionalEN 16931 BT-20 payment terms. Defaults to "Due on receipt"; required (with a positive total) for B2B (BR-CO-25).
external_referencestringoptionalYour internal booking ID (max 200 chars). Surfaced in list filters and used for reconciliation against your PMS/OTA.
idempotency_keystringoptionalOptional client-generated key (max 200 chars). Re-POSTing with the same key + org returns the existing booking (HTTP 200) instead of creating a duplicate.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/bookings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"external_reference": "PMS-2026-00482",
"idempotency_key": "pms-2026-00482-v1",
"property_id": 42,
"calculation": {
"jurisdiction_code": "US-NY-NYC",
"stay_date": "2026-06-15",
"checkout_date": "2026-06-18",
"nightly_rate": 250,
"currency": "USD",
"nights": 3,
"number_of_guests": 2,
"property_type": "hotel",
"buyer_name": "ACME GmbH",
"buyer_address": { "line1": "Hauptstrasse 1", "city": "Berlin", "postal_code": "10115", "country_code": "DE" }
}
}'{
"id": 4821,
"org_id": 17,
"user_id": 42,
"external_reference": "PMS-2026-00482",
"idempotency_key": "pms-2026-00482-v1",
"status": "confirmed",
"total_tax": "87.37",
"total_with_tax": "837.37",
"currency": "USD",
"request": {
"jurisdiction_code": "US-NY-NYC",
"stay_date": "2026-06-15",
"checkout_date": "2026-06-18",
"nightly_rate": 250.0,
"currency": "USD",
"nights": 3,
"number_of_guests": 2,
"property_type": "hotel"
},
"response": {
"calculation_id": "calc_97d7d87e3cce7947c44e9fa20100f416",
"jurisdiction": { "code": "US-NY-NYC", "name": "New York City", "path": "US.NY.NYC" },
"input": { "nightly_rate": 250.0, "nights": 3, "subtotal": 750.0, "currency": "USD", "property_type": "hotel" },
"tax_breakdown": {
"components": [
{ "name": "VAT / Sales Tax (standard)", "rate": 0.04, "rate_type": "percentage", "taxable_amount": "750", "tax_amount": "30.00" },
{ "name": "Municipal Surcharge (%)", "rate": 0.00375, "rate_type": "percentage", "taxable_amount": "750", "tax_amount": "2.81" },
{ "name": "Occupancy Tax (% of room)", "rate": 0.05875, "rate_type": "percentage", "taxable_amount": "750", "tax_amount": "44.06" },
{ "name": "Occupancy Tax (flat per night)", "rate": 0.0, "rate_type": "tiered", "taxable_amount": null, "tax_amount": "6.00" },
{ "name": "Convention Center Tax (flat)", "rate": 1.5, "rate_type": "flat", "taxable_amount": null, "tax_amount": "4.50" },
{ "name": "VAT / Sales Tax (standard)", "rate": 0.0, "rate_type": "percentage", "taxable_amount": "750", "tax_amount": "0.00" }
],
"total_tax": "87.37",
"effective_rate": "0.1164933333333333333333333333",
"currency": "USD"
},
"total_with_tax": "837.37"
},
"metadata": {},
"created_at": "2026-05-21T10:29:52.943980Z",
"updated_at": "2026-05-21T10:29:52.943980Z",
"voided_at": null
}/v1/bookings/{booking_id}Get Booking
Retrieve a single booking by its numeric ID. Returns the full record including the original request snapshot and the calculation response that was persisted at create time. Org-scoped — bookings belonging to other organizations return 404, never 403, to avoid leaking existence.
Parameters
booking_idintegerrequiredNumeric booking ID (the `id` returned from POST /v1/bookings).
curl https://api.taxlens.getdynamiq.ai/v1/bookings/4821 \
-H "Authorization: Bearer <token>"{
"id": 4821,
"org_id": 17,
"user_id": 42,
"external_reference": "PMS-2026-00482",
"idempotency_key": "pms-2026-00482-v1",
"status": "confirmed",
"total_tax": "87.37",
"total_with_tax": "837.37",
"currency": "USD",
"request": {
"jurisdiction_code": "US-NY-NYC",
"stay_date": "2026-06-15",
"checkout_date": "2026-06-18",
"nightly_rate": 250.0,
"currency": "USD",
"nights": 3,
"number_of_guests": 2,
"property_type": "hotel"
},
"response": {
"calculation_id": "calc_97d7d87e3cce7947c44e9fa20100f416",
"jurisdiction": { "code": "US-NY-NYC", "name": "New York City", "path": "US.NY.NYC" },
"tax_breakdown": {
"components": [],
"total_tax": "87.37",
"effective_rate": "0.1164933333333333333333333333",
"currency": "USD"
},
"total_with_tax": "837.37"
},
"metadata": {},
"created_at": "2026-05-21T10:29:52.943980Z",
"updated_at": "2026-05-21T10:29:52.943980Z",
"voided_at": null
}/v1/bookingsList Bookings
List bookings for the caller's organization, ordered most-recent first. Supports filtering by status, external reference, and creation date range, plus cursor-based pagination. The cursor is the last booking ID returned — pass it back in the next request to fetch the following page. Returns `next_cursor: null` when there are no more results.
Parameters
statusstringoptionalFilter by status: `confirmed`, `adjusted`, or `voided`.
external_referencestringoptionalExact-match filter on the customer's reference ID supplied at create time.
from_datedatetimeoptionalISO 8601 datetime. Returns bookings with `created_at >= from_date`.
to_datedatetimeoptionalISO 8601 datetime. Returns bookings with `created_at <= to_date`.
limitintegeroptionalPage size (1-200). Default 50.
Default: 50
cursorintegeroptionalBooking ID to paginate before. Pass the `next_cursor` from the previous response.
curl "https://api.taxlens.getdynamiq.ai/v1/bookings?status=confirmed&from_date=2026-05-01T00:00:00Z&limit=25" \
-H "Authorization: Bearer <token>"{
"items": [
{
"id": 4821,
"external_reference": "PMS-2026-00482",
"status": "confirmed",
"total_tax": "87.37",
"total_with_tax": "837.37",
"currency": "USD",
"request": {
"jurisdiction_code": "US-NY-NYC",
"stay_date": "2026-06-15",
"nightly_rate": 250.0,
"currency": "USD",
"nights": 3,
"property_type": "hotel"
},
"response": {
"calculation_id": "calc_97d7d87e3cce7947c44e9fa20100f416",
"jurisdiction": { "code": "US-NY-NYC", "name": "New York City", "path": "US.NY.NYC" },
"tax_breakdown": { "components": [], "total_tax": "87.37", "effective_rate": "0.1164933333333333333333333333", "currency": "USD" },
"total_with_tax": "837.37"
},
"metadata": {},
"created_at": "2026-05-21T10:29:52.943980Z",
"updated_at": "2026-05-21T10:29:52.943980Z",
"voided_at": null
}
],
"next_cursor": "4820"
}/v1/bookings/{booking_id}Adjust Booking
Re-run the tax engine for an existing booking when a tax-affecting input changes, such as dates, jurisdiction, nightly price, guest count, property type, or buyer tax context. The booking remains the same row, `status` becomes `adjusted`, totals and the persisted calculation snapshots are replaced, and the prior calculation is appended to `metadata.adjustment_history`. Adjustments are blocked after void, full refund, any partial refund, or issued invoice; correct issued invoices with a credit note plus a new booking.
Parameters
booking_idintegerrequiredNumeric booking ID to adjust.
Request Body
calculationobjectrequiredFull TaxCalculationRequest replacement. Send the complete recalculation input, not only changed fields.
reasonstringoptionalOptional operator/customer reason stored in `metadata.adjustment_history[].reason`.
curl -X PATCH https://api.taxlens.getdynamiq.ai/v1/bookings/4821 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"reason": "nightly rate corrected after PMS sync",
"calculation": {
"jurisdiction_code": "US-NY-NYC",
"stay_date": "2026-06-15",
"checkout_date": "2026-06-18",
"nightly_rate": 275.0,
"currency": "USD",
"nights": 3,
"number_of_guests": 2,
"property_type": "hotel"
}
}'{
"id": 4821,
"org_id": 17,
"user_id": 42,
"external_reference": "PMS-2026-00482",
"idempotency_key": "pms-2026-00482-v1",
"status": "adjusted",
"total_tax": "96.11",
"total_with_tax": "921.11",
"currency": "USD",
"request": {
"jurisdiction_code": "US-NY-NYC",
"stay_date": "2026-06-15",
"checkout_date": "2026-06-18",
"nightly_rate": 275.0,
"currency": "USD",
"nights": 3,
"number_of_guests": 2,
"property_type": "hotel"
},
"response": {
"calculation_id": "calc_2f0986f5a3f2b6c99f8a0d7452a27ed1",
"jurisdiction": { "code": "US-NY-NYC", "name": "New York City", "path": "US.NY.NYC" },
"tax_breakdown": {
"components": [],
"total_tax": "96.11",
"effective_rate": "0.1164969696969696969696969697",
"currency": "USD"
},
"total_with_tax": "921.11"
},
"metadata": {
"adjustment_history": [
{
"at": "2026-05-22T11:42:31.120000+00:00",
"reason": "nightly rate corrected after PMS sync",
"previous_request": {
"jurisdiction_code": "US-NY-NYC",
"stay_date": "2026-06-15",
"checkout_date": "2026-06-18",
"nightly_rate": 250.0,
"currency": "USD",
"nights": 3,
"number_of_guests": 2,
"property_type": "hotel"
},
"previous_total_tax": "87.37"
}
]
},
"created_at": "2026-05-21T10:29:52.943980Z",
"updated_at": "2026-05-22T11:42:31.120000Z",
"voided_at": null
}/v1/bookings/{booking_id}Void Booking
Void a booking. Voids are sticky and idempotent — a second DELETE on the same booking is a no-op. The booking row is NOT deleted; its `status` flips to `voided` and `voided_at` is set, preserving the audit trail. Voided bookings continue to appear in list responses (filter with `?status=voided`) and remain retrievable via GET. Returns HTTP 204 with no response body on success.
Parameters
booking_idintegerrequiredNumeric booking ID to void.
curl -X DELETE https://api.taxlens.getdynamiq.ai/v1/bookings/4821 \
-H "Authorization: Bearer <token>" -iHTTP/1.1 204 No Content/v1/bookings/{booking_id}/refundRefund Booking
Process a partial or full refund on a persisted booking. The engine computes a proportional tax delta per jurisdiction — i.e. how much of the originally-collected tax should be returned to the guest / clawed back from authorities. Idempotent via `refund_idempotency_key`: re-POSTing with the same key returns the prior result. A full refund (`refund_amount >= booking.total_with_tax`) flips status to `refund`; `voided_at` remains reserved for explicit voids. Partial refunds preserve the booking's prior status. Refund history is appended to `booking.metadata.refund_history` (a JSONB array).
The refund uses the ORIGINAL calc's tax components (not a re-calc against current rates) — the booking already had its tax fixed at original calc time; refund returns a proportional fraction of that tax. Jurisdictions that tax refunds at the refund-date rate (rare) would require a per-jurisdiction rule — deferred until we encounter one in primary sources.
Parameters
booking_idintegerrequiredNumeric booking ID to refund.
Request Body
refund_amountdecimalrequiredAmount to refund in booking currency. Must be > 0 and ≤ booking.total_with_tax.
refund_currencystringrequiredISO 4217. Must match the booking's currency.
refund_reasonstringrequiredguest_cancel | courtesy | no_show | duplicate_charge | downgrade | other
refund_datedateoptionalEffective date of the refund. Defaults to today. Matters for jurisdictions that tax refunds at the refund-date rate.
refund_idempotency_keystringoptionalIf provided, re-POSTing with the same key returns the prior result (idempotent). Max 200 chars.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/bookings/4821/refund \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{
"refund_amount": 100.00,
"refund_currency": "USD",
"refund_reason": "guest_cancel",
"refund_idempotency_key": "refund-4821-v1"
}'{
"booking_id": 4821,
"refund_amount": 100.00,
"refund_currency": "USD",
"refund_reason": "guest_cancel",
"refund_date": "2026-05-22",
"refund_idempotency_key": "refund-4821-v1",
"refund_status": "partial",
"tax_delta": [
{
"category_code": "vat_standard",
"jurisdiction_code": "US-NY",
"original_tax": "30.00",
"refunded_tax": "3.58",
"notes": null
},
{
"category_code": "municipal_pct",
"jurisdiction_code": "US-NY-NYC",
"original_tax": "2.81",
"refunded_tax": "0.34",
"notes": null
},
{
"category_code": "occ_pct",
"jurisdiction_code": "US-NY-NYC",
"original_tax": "44.06",
"refunded_tax": "5.26",
"notes": null
},
{
"category_code": "occ_flat_night",
"jurisdiction_code": "US-NY-NYC",
"original_tax": "6.00",
"refunded_tax": "0.72",
"notes": null
},
{
"category_code": "convention_flat",
"jurisdiction_code": "US-NY-NYC",
"original_tax": "4.50",
"refunded_tax": "0.54",
"notes": null
}
],
"total_tax_refunded": "10.44",
"new_booking_status": "confirmed",
"refunded_at": "2026-05-22T14:23:45.123Z"
}Properties
/v1/propertiesCreate Property
Create an org-scoped managed property/listing. TaxLens geocodes the address, resolves the closest jurisdiction, stores mapping confidence, and keeps property tax-driver fields that can prefill future bookings. Properties are durable inventory records; historical booking snapshots do not change when a property is remapped.
Request Body
external_referencestringoptionalYour PMS/listing/property ID for reconciliation.
namestringrequiredDisplay name for the property.
address_line1stringrequiredStreet address.
citystringoptionalCity/locality.
regionstringoptionalState, province, region, or county.
postal_codestringoptionalPostal code used for geocoding.
country_codestringrequiredISO 3166-1 alpha-2 country code.
jurisdiction_codestringoptionalOptional TaxLens jurisdiction code to pin deterministically when the customer already knows the exact local jurisdiction.
property_typestringoptionalHotel, vacation rental, apartment hotel, villa, hostel, etc.
star_ratingintegeroptional1-5 where jurisdictional rules depend on classification.
property_postal_codestringoptionalTax-zone postal code when different from the mailing postal code.
default_legal_issuer_idintegeroptionalOptional org-owned legal issuer used as the property default seller when bookings from this property omit legal_issuer_id. Setting or changing this requires org-admin permissions.
default_supplier_legal_issuer_idintegeroptionalOptional org-owned host/owner issuer used as the property default supplier for self-billed 389 flows when bookings omit supplier_legal_issuer_id. Setting or changing this requires org-admin permissions.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/properties \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"external_reference": "PMS-HOTEL-42",
"name": "Downtown Hotel",
"address_line1": "350 5th Ave",
"city": "New York",
"region": "NY",
"postal_code": "10118",
"country_code": "US",
"jurisdiction_code": "US-NY-NYC",
"property_type": "hotel",
"star_rating": 4,
"default_legal_issuer_id": 7,
"default_supplier_legal_issuer_id": 18
}'{
"id": 42,
"external_reference": "PMS-HOTEL-42",
"name": "Downtown Hotel",
"jurisdiction_code": "US-NY-NYC-MAN",
"jurisdiction_confidence": "high",
"mapping_status": "mapped",
"tax_driver_status": "complete",
"property_type": "hotel",
"star_rating": 4,
"default_legal_issuer_id": 7,
"default_supplier_legal_issuer_id": 18
}/v1/properties/bulk-mapBulk Map Properties
Create up to 500 managed properties in one request. Each property is geocoded, jurisdiction-mapped, and stored as durable inventory. This is the recommended import path for PMS/channel-manager listing syncs. Org-admin permissions are required only when a row sets non-null legal issuer defaults.
Request Body
propertiesarrayrequired1-500 PropertyCreate objects. Supports the same fields as Create Property, including default_legal_issuer_id and default_supplier_legal_issuer_id.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/properties/bulk-map \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"properties": [
{
"external_reference": "GUESTY-ROME-1",
"name": "Rome Host Unit",
"address_line1": "Via Roma 1",
"country_code": "IT",
"property_type": "apartment",
"default_supplier_legal_issuer_id": 18
}
]
}'{
"items": [
{
"id": 42,
"external_reference": "GUESTY-ROME-1",
"name": "Rome Host Unit",
"jurisdiction_code": "IT-RM",
"mapping_status": "mapped",
"default_legal_issuer_id": null,
"default_supplier_legal_issuer_id": 18
}
],
"mapped": 1,
"unresolved": 0
}/v1/propertiesList Properties
List the caller organization's managed properties. Filter by mapping status, country, jurisdiction, or external reference. Use this to keep PMS/listing inventory synchronized before creating bookings.
Parameters
mapping_statusstringoptionalmapped, unresolved, or stale.
country_codestringoptionalISO country filter.
jurisdiction_codestringoptionalExact TaxLens jurisdiction code.
external_referencestringoptionalYour property/listing ID.
curl "https://api.taxlens.getdynamiq.ai/v1/properties?mapping_status=mapped&country_code=US" \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"items": [
{ "id": 42, "name": "Downtown Hotel", "jurisdiction_code": "US-NY-NYC-MAN", "mapping_status": "mapped" }
],
"total": 1
}/v1/properties/{property_id}/remapRemap Property
Re-run address geocoding and jurisdiction resolution for one managed property. Future bookings can use the refreshed mapping; existing booking and invoice snapshots remain frozen.
Parameters
property_idintegerrequiredManaged property ID owned by the active organization.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/properties/42/remap \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"id": 42,
"mapping_status": "mapped",
"jurisdiction_code": "US-NY-NYC-MAN",
"last_mapped_at": "2026-06-04T12:00:00Z"
}E-invoicing
/v1/bookings/{booking_id}/issueIssue Invoice
Turn a booking into an immutable fiscal document. Runs the EN 16931 / PEPPOL send-readiness pre-check, burns a gapless fiscal number (per issuer / document-type / series / year), and freezes the document. Idempotent — re-issuing returns the same invoice. 422 if not send-ready, 409 if the booking is voided. For self-billed 389 flows, set supplier_legal_issuer_id on the booking first; otherwise the platform issuer is used for backward compatibility. Requires org owner or admin.
Parameters
booking_idintegerrequiredBooking ID.
Request Body
seriesstringoptionalOptional numbering series prefix (Italy/Spain/Mexico keep parallel streams). Defaults to none.
self_billedbooleanoptionalIssue as a self-billed invoice (UNCL1001 389) — a platform/OTA issuing in the supplier's name (merchant-of-record). Defaults to false (380).
curl -X POST https://api.taxlens.getdynamiq.ai/v1/bookings/123/issue \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-d '{ "series": "" }'{
"id": 9,
"org_id": 7,
"booking_id": 123,
"legal_issuer_id": 1,
"document_type": "380",
"series": "",
"fiscal_year": 2026,
"sequence_number": 1,
"invoice_number": "2026-000001",
"currency": "EUR",
"grand_total": "495.00",
"status": "issued",
"preceding_invoice_id": null,
"issued_at": "2026-05-29T10:00:00Z",
"cancelled_at": null,
"created_at": "2026-05-29T10:00:00Z",
"updated_at": "2026-05-29T10:00:00Z",
"document": null
}/v1/invoices/{invoice_id}/credit-noteIssue Credit Note
Issue a credit note (UNCL1001 381) that reverses an issued invoice. Burns a fiscal number from the credit-note sequence, references the original (BT-25), marks the original 'credited', and serializes as a PEPPOL CreditNote document. Idempotent (one per booking). Owner/admin.
Parameters
invoice_idintegerrequiredIssued invoice ID to reverse.
Request Body
reasonstringoptionalAudit-trail reason for the credit.
seriesstringoptionalOptional credit-note numbering series.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/invoices/9/credit-note \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-d '{ "reason": "guest cancelled", "series": "" }'{
"id": 10,
"org_id": 7,
"booking_id": 123,
"legal_issuer_id": 1,
"document_type": "381",
"series": "",
"fiscal_year": 2026,
"sequence_number": 1,
"invoice_number": "2026-000001",
"currency": "EUR",
"grand_total": "495.00",
"status": "issued",
"preceding_invoice_id": 9,
"issued_at": "2026-05-29T10:05:00Z",
"cancelled_at": null,
"created_at": "2026-05-29T10:05:00Z",
"updated_at": "2026-05-29T10:05:00Z",
"document": null
}/v1/invoicesList Issued Invoices
List issued fiscal invoices for your org (newest first). Filter by booking_id; cursor-paginate via next_cursor.
Parameters
booking_idintegeroptionalFilter to one booking.
limitintegeroptionalPage size (1-200, default 50).
cursorintegeroptionalPagination cursor (last id seen).
curl https://api.taxlens.getdynamiq.ai/v1/invoices?booking_id=123 \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"items": [
{
"id": 9,
"org_id": 7,
"booking_id": 123,
"legal_issuer_id": 1,
"document_type": "380",
"series": "",
"fiscal_year": 2026,
"sequence_number": 1,
"invoice_number": "2026-000001",
"currency": "EUR",
"grand_total": "495.00",
"status": "issued",
"preceding_invoice_id": null,
"issued_at": "2026-05-29T10:00:00Z",
"cancelled_at": null,
"created_at": "2026-05-29T10:00:00Z",
"updated_at": "2026-05-29T10:00:00Z",
"document": null
}
],
"next_cursor": null
}/v1/invoices/{invoice_id}Get Issued Invoice
Fetch one issued invoice including its frozen EN 16931 document. The document never changes after issuance — booking edits don't touch it.
Parameters
invoice_idintegerrequiredIssued invoice ID.
curl https://api.taxlens.getdynamiq.ai/v1/invoices/9 \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"id": 9,
"org_id": 7,
"booking_id": 123,
"legal_issuer_id": 1,
"document_type": "380",
"series": "",
"fiscal_year": 2026,
"sequence_number": 1,
"invoice_number": "2026-000001",
"currency": "EUR",
"grand_total": "495.00",
"status": "issued",
"preceding_invoice_id": null,
"issued_at": "2026-05-29T10:00:00Z",
"cancelled_at": null,
"created_at": "2026-05-29T10:00:00Z",
"updated_at": "2026-05-29T10:00:00Z",
"document": {
"document": {
"type_code": "380",
"type_name": "Commercial invoice",
"issue_date": "2026-05-29",
"currency": "EUR"
},
"seller": { "legal_name": "Acme Hotel SRL" },
"buyer": { "name": "ACME GmbH" },
"totals": { "grand_total": "495.00", "currency": "EUR" },
"validation": { "is_valid": true, "violations": [] }
}
}/v1/invoices/{invoice_id}/ublGet Issued Invoice UBL
Serialize the FROZEN issued document to UBL 2.1 / PEPPOL BIS 3.0 XML, with the real fiscal invoice_number as cbc:ID (the send-ready artifact, vs. the booking-level UBL's provisional BKG-{booking_id}).
Parameters
invoice_idintegerrequiredIssued invoice ID.
curl https://api.taxlens.getdynamiq.ai/v1/invoices/9/ubl \
-H "Authorization: Bearer $TAXLENS_TOKEN"<?xml version="1.0"?><Invoice ...><cbc:ID>2026-000001</cbc:ID>...</Invoice>/v1/bookings/{booking_id}/invoice-detailsUpdate Invoice Details
Set invoice-only metadata on an existing booking WITHOUT re-running the tax engine: buyer name (BT-44), buyer address (BG-8), buyer reference (BT-10), and/or the issuing legal entity. This is how a booking becomes PEPPOL send-ready after creation (e.g. you configured your issuer after the booking was made). Partial update — only fields you send are changed. Does not touch totals or status and does not append to adjustment history. Blocked (409) on voided/refunded bookings; use this instead of `adjust` when only invoice metadata changes.
Parameters
booking_idintegerrequiredBooking ID.
Request Body
buyer_namestringoptionalBuyer legal name (EN 16931 BT-44).
buyer_endpoint_idstringoptionalBuyer PEPPOL electronic address (BT-49) — required to make a B2B invoice send-ready (R010).
buyer_endpoint_schemestringoptional4-digit EAS/ICD scheme for buyer_endpoint_id (e.g. 9930).
payment_termsstringoptionalEN 16931 BT-20 payment terms (BR-CO-25 for B2B).
supplier_legal_issuer_idintegeroptionalHost/supplier legal entity for a self-billed (389) invoice.
buyer_addressobjectoptional{ line1, line2, city, region, postal_code, country_code }. country_code (BT-55) is required for a PEPPOL-valid invoice.
buyer_referencestringoptionalEN 16931 BT-10 buyer reference (PO / Leitweg-ID).
legal_issuer_idintegeroptionalAttach/replace the issuing legal entity (must belong to your org); re-freezes the invoice seller party.
curl -X PATCH https://api.taxlens.getdynamiq.ai/v1/bookings/123/invoice-details \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-d '{
"buyer_name": "ACME GmbH",
"buyer_address": { "line1": "Hauptstrasse 1", "city": "Berlin", "postal_code": "10115", "country_code": "DE" },
"buyer_reference": "PO-2026-7781"
}'{
"id": 123,
"org_id": 7,
"user_id": 8,
"external_reference": "PMS-2026-00482",
"idempotency_key": "pms-2026-00482-v1",
"status": "confirmed",
"total_tax": "45.00",
"total_with_tax": "495.00",
"currency": "EUR",
"request": {
"jurisdiction_code": "IT-62-ROM",
"stay_date": "2026-06-01",
"nightly_rate": 150,
"currency": "EUR",
"nights": 3,
"property_type": "hotel"
},
"response": {
"calculation_id": "calc_abc123",
"jurisdiction": { "code": "IT-62-ROM", "name": "Roma", "path": "IT.62.ROM" },
"tax_breakdown": { "components": [], "total_tax": "45.00", "effective_rate": "0.10", "currency": "EUR" },
"total_with_tax": "495.00"
},
"metadata": {},
"created_at": "2026-05-25T14:00:00Z",
"updated_at": "2026-05-25T14:05:00Z",
"voided_at": null
}/v1/bookings/{booking_id}/documentGet Invoice Document Projection
Project a persisted booking as a country-agnostic JSON document aligned with EN 16931 / PEPPOL BIS Billing 3.0. Read-only. The `validation` block is a compliance pre-check: `is_valid` is true when the document is PEPPOL send-ready, otherwise `violations` lists the missing/invalid Business Terms (e.g. BT-44 buyer name). Used as the foundation for the UBL serializer and later CFDI / IRN / ZATCA ones. Returns 422 if the booking has no seller party — set up a legal issuer first via /v1/legal-issuers.
Parameters
booking_idintegerrequiredBooking ID.
curl https://api.taxlens.getdynamiq.ai/v1/bookings/123/document \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"document": {
"type_code": "380",
"type_name": "Commercial invoice",
"issue_date": "2026-05-25",
"delivery_period": { "from": "2026-06-01", "to": "2026-06-04" },
"currency": "EUR",
"buyer_reference": null,
"preceding_document_ref": null,
"regulator_identifiers": {}
},
"seller": {
"legal_name": "Acme Hotel SRL",
"trading_name": "Acme Resort",
"address": { "line1": "Via Roma 1", "city": "Roma", "postal_code": "00100", "country_code": "IT" },
"tax_registrations": [
{ "scheme": "VAT", "scheme_icd": "9930", "value": "IT12345678901", "country_code": "IT" }
],
"eas_endpoint": { "id": "IT12345678901", "scheme_code": "9930" }
},
"buyer": {
"customer_type": "business", "tax_id": "DE111111111",
"name": "ACME GmbH",
"address": { "line1": "Hauptstrasse 1", "city": "Berlin", "postal_code": "10115", "country_code": "DE" }
},
"lines": [
{ "id": "1", "description": "Room rate", "item_type": "room",
"quantity": "3", "unit_of_measure": "DAY",
"unit_price": "150.00", "line_net": "450.00",
"tax_category_code": "S", "tax_rate_percent": "10.00", "exemption_code": null }
],
"tax_breakdown": [
{ "category_code": "S", "rate_percent": "10.00",
"taxable_amount": "450.00", "tax_amount": "45.00", "exemption_code": null }
],
"totals": {
"lines_net_total": "450.00",
"tax_total": "45.00",
"grand_total": "495.00",
"currency": "EUR",
"amount_in_settlement_currency": null,
"settlement_currency": null
},
"collection_info": {
"merchant_of_record": "property",
"platform_collected_components": [],
"property_collected_components": ["accommodation_vat"]
},
"validation": { "is_valid": true, "violations": [] },
"trace": {
"booking_id": 123,
"calculation_id": "calc_abc123",
"data_version": "2026-05-24",
"generated_at": "2026-05-25T14:00:00Z"
}
}/v1/bookings/{booking_id}/document/ublExport Invoice as UBL (PEPPOL BIS 3.0)
Serialize a booking's invoice document to UBL 2.1 XML conforming to PEPPOL BIS Billing 3.0 (the format that travels the PEPPOL network). Runs the EN 16931 pre-check first: if the booking is not send-ready it returns 422 with the exact list of violations (see example) instead of emitting an invoice a tax authority would reject. The export is VAT-only — occupancy/tourist taxes are not VAT and cannot share an EN 16931 invoice, so they are excluded and flagged as a warning on the JSON document. Supports MULTIPLE VAT rates on one invoice (e.g. room 10% + breakfast 20% → one cac:TaxSubtotal per rate) and EU cross-border B2B REVERSE CHARGE (category AE, 0% VAT, buyer VAT id + exemption reason — when a reverse-charge rule applies for a business buyer in a different country than the property). `cbc:ID` is a provisional identifier (BKG-{booking_id}) for this preview — issue the booking (POST /v1/bookings/{booking_id}/issue) to get the real fiscal number, after which this endpoint returns 409 and you use GET /v1/invoices/{invoice_id}/ubl. No Schematron/XSD, signing, or clearance yet. Response Content-Type is application/xml.
Parameters
booking_idintegerrequiredBooking ID.
curl https://api.taxlens.getdynamiq.ai/v1/bookings/123/document/ubl \
-H "Authorization: Bearer $TAXLENS_TOKEN"<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>BKG-123</cbc:ID>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<!-- AccountingSupplierParty / AccountingCustomerParty / TaxTotal / ... -->
<cac:LegalMonetaryTotal>
<cbc:TaxInclusiveAmount currencyID="EUR">495.00</cbc:TaxInclusiveAmount>
<cbc:PayableAmount currencyID="EUR">495.00</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
</Invoice>
# 422 when not send-ready:
{
"detail": {
"message": "Booking is not PEPPOL send-ready. Resolve the listed violations and try again.",
"violations": [
{ "code": "BT-44", "severity": "error", "field": "BT-44", "message": "Buyer name is required. Pass buyer_name when creating the booking." }
]
}
}/v1/legal-issuersCreate Legal Issuer
Create a legal entity that issues invoices on behalf of this organization. One org can own many issuers (an OTA or PMS managing multiple properties, each its own legal seller). Admin or org owner only.
Request Body
legal_namestringrequiredLegal entity name.
trading_namestringoptionalDBA / display name.
country_codestringrequiredISO 3166-1 alpha-2.
registered_addressobjectoptional{line1, line2, city, region, postal_code, country_code}
default_currencystringoptionalISO 4217.
eas_endpoint_idstringoptionalEN 16931 BT-34 PEPPOL electronic address.
eas_scheme_codestringoptionalISO 6523 ICD (e.g. 9930 DE-VAT, 0088 GLN).
curl -X POST https://api.taxlens.getdynamiq.ai/v1/legal-issuers \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"legal_name": "Acme Hotel SRL",
"trading_name": "Acme Resort",
"country_code": "IT",
"registered_address": {"line1": "Via Roma 1", "city": "Roma", "postal_code": "00100", "country_code": "IT"},
"default_currency": "EUR",
"eas_endpoint_id": "IT12345678901",
"eas_scheme_code": "9930"
}'{
"id": 1,
"org_id": 7,
"legal_name": "Acme Hotel SRL",
"trading_name": "Acme Resort",
"country_code": "IT",
"registered_address": {"line1": "Via Roma 1", "city": "Roma", "postal_code": "00100", "country_code": "IT"},
"default_currency": "EUR",
"eas_endpoint_id": "IT12345678901",
"eas_scheme_code": "9930",
"is_active": true,
"created_at": "2026-05-25T14:00:00Z",
"updated_at": "2026-05-25T14:00:00Z"
}/v1/legal-issuersList Legal Issuers
List the caller's organization's legal issuers. Soft-deleted issuers are excluded unless `include_inactive=true`.
Parameters
include_inactivebooleanoptionalInclude soft-deleted issuers in the result.
Default: false
curl https://api.taxlens.getdynamiq.ai/v1/legal-issuers \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"items": [
{
"id": 1,
"org_id": 7,
"legal_name": "Acme Hotel SRL",
"trading_name": "Acme Resort",
"country_code": "IT",
"registered_address": {
"line1": "Via Roma 1",
"city": "Roma",
"postal_code": "00100",
"country_code": "IT"
},
"default_currency": "EUR",
"eas_endpoint_id": "IT12345678901",
"eas_scheme_code": "9930",
"is_active": true,
"created_at": "2026-05-25T14:00:00Z",
"updated_at": "2026-05-25T14:00:00Z"
}
]
}/v1/legal-issuers/{issuer_id}Get Legal Issuer
Fetch one legal issuer, including the full registered address and PEPPOL endpoint fields. Org-scoped.
Parameters
issuer_idintegerrequiredLegal issuer ID.
curl https://api.taxlens.getdynamiq.ai/v1/legal-issuers/1 \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"id": 1,
"org_id": 7,
"legal_name": "Acme Hotel SRL",
"trading_name": "Acme Resort",
"country_code": "IT",
"registered_address": {
"line1": "Via Roma 1",
"line2": null,
"city": "Roma",
"region": "Lazio",
"postal_code": "00100",
"country_code": "IT"
},
"default_currency": "EUR",
"eas_endpoint_id": "IT12345678901",
"eas_scheme_code": "9930",
"is_active": true,
"created_at": "2026-05-25T14:00:00Z",
"updated_at": "2026-05-25T14:00:00Z"
}/v1/legal-issuers/{issuer_id}Update Legal Issuer
Partially update a legal issuer. Send null for nullable fields you want to clear. Admin or org owner only.
Parameters
issuer_idintegerrequiredLegal issuer ID.
Request Body
legal_namestringoptionalLegal entity name.
trading_namestring | nulloptionalDBA / display name. Pass null to clear.
registered_addressobject | nulloptional{line1, line2, city, region, postal_code, country_code}. Passing an object replaces the stored address object.
default_currencystring | nulloptionalISO 4217. Pass null to clear.
eas_endpoint_idstring | nulloptionalSeller PEPPOL endpoint (BT-34). Pass null to clear.
eas_scheme_codestring | nulloptionalEAS/ICD scheme code. Pass null to clear.
curl -X PATCH https://api.taxlens.getdynamiq.ai/v1/legal-issuers/1 \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"trading_name": "Acme Roma",
"registered_address": {
"line1": "Via Roma 1",
"line2": "Floor 3",
"city": "Roma",
"region": "Lazio",
"postal_code": "00100",
"country_code": "IT"
}
}'{
"id": 1,
"org_id": 7,
"legal_name": "Acme Hotel SRL",
"trading_name": "Acme Roma",
"country_code": "IT",
"registered_address": {
"line1": "Via Roma 1",
"line2": "Floor 3",
"city": "Roma",
"region": "Lazio",
"postal_code": "00100",
"country_code": "IT"
},
"default_currency": "EUR",
"eas_endpoint_id": "IT12345678901",
"eas_scheme_code": "9930",
"is_active": true,
"created_at": "2026-05-25T14:00:00Z",
"updated_at": "2026-05-25T14:05:00Z"
}/v1/legal-issuers/{issuer_id}Deactivate Legal Issuer
Soft-delete a legal issuer. Existing booking snapshots keep their frozen seller; new bookings must use another active issuer. Admin or org owner only.
Parameters
issuer_idintegerrequiredLegal issuer ID.
curl -X DELETE https://api.taxlens.getdynamiq.ai/v1/legal-issuers/1 \
-H "Authorization: Bearer $TAXLENS_TOKEN" -iHTTP/1.1 204 No Content/v1/legal-issuers/{issuer_id}/tax-registrationsAdd Tax Registration
Attach a tax identifier (VAT, GSTIN, EIN, ABN, etc.) to a legal issuer. `scheme` and `country_code` are normalized to uppercase and the `(legal_issuer, country, scheme)` triple is unique.
Parameters
issuer_idintegerrequiredLegal issuer ID.
Request Body
country_codestringrequiredISO 3166-1 alpha-2.
schemestringrequiredVAT, GSTIN, EIN, ABN, RFC, CUIT, CNPJ, UEN, etc.
scheme_icdstringoptionalISO 6523 ICD.
valuestringrequiredThe identifier value.
valid_fromdateoptionalISO date.
valid_todateoptionalISO date.
curl -X POST https://api.taxlens.getdynamiq.ai/v1/legal-issuers/1/tax-registrations \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "country_code": "IT", "scheme": "VAT", "scheme_icd": "9930", "value": "IT12345678901" }'{
"id": 1,
"legal_issuer_id": 1,
"country_code": "IT",
"scheme": "VAT",
"scheme_icd": "9930",
"value": "IT12345678901",
"valid_from": null,
"valid_to": null,
"created_at": "2026-05-25T14:00:00Z",
"updated_at": "2026-05-25T14:00:00Z"
}/v1/legal-issuers/{issuer_id}/tax-registrationsList Tax Registrations
List tax identifiers attached to a legal issuer. These feed the seller party in booking document projections and issued invoices.
Parameters
issuer_idintegerrequiredLegal issuer ID.
curl https://api.taxlens.getdynamiq.ai/v1/legal-issuers/1/tax-registrations \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"items": [
{
"id": 1,
"legal_issuer_id": 1,
"country_code": "IT",
"scheme": "VAT",
"scheme_icd": "9930",
"value": "IT12345678901",
"valid_from": "2026-01-01",
"valid_to": null,
"created_at": "2026-05-25T14:00:00Z",
"updated_at": "2026-05-25T14:00:00Z"
}
]
}/v1/legal-issuers/{issuer_id}/tax-registrations/{registration_id}Delete Tax Registration
Remove one tax registration from a legal issuer. Existing booking snapshots and issued invoices remain frozen.
Parameters
issuer_idintegerrequiredLegal issuer ID.
registration_idintegerrequiredTax registration ID.
curl -X DELETE https://api.taxlens.getdynamiq.ai/v1/legal-issuers/1/tax-registrations/1 \
-H "Authorization: Bearer $TAXLENS_TOKEN" -iHTTP/1.1 204 No Content/v1/orgs/me/legal-profileUpdate Org Legal Profile
Set the organization's legal identity used as a fallback seller when bookings are created without an explicit `legal_issuer_id`. Admin or org owner only.
Request Body
legal_namestringoptionalTrimmed; whitespace-only is rejected.
tax_country_codestringoptionalISO 3166-1 alpha-2.
default_legal_issuer_idinteger | nulloptionalID of a legal issuer the org owns. Pass null to clear.
curl -X PATCH https://api.taxlens.getdynamiq.ai/v1/orgs/me/legal-profile \
-H "Authorization: Bearer $TAXLENS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "legal_name": "Acme Holdings", "tax_country_code": "IT", "default_legal_issuer_id": 1 }'{
"id": 7,
"name": "Acme",
"slug": "acme",
"owner_id": 8,
"api_access_enabled": true,
"legal_name": "Acme Holdings",
"tax_country_code": "IT",
"default_legal_issuer_id": 1
}Invoice Reports
/v1/invoices/liability-summaryInvoice Tax Summary
Summarize issued-invoice tax liabilities by jurisdiction, authority, category, and currency. This is a read-only report derived from issued fiscal documents; calculated-but-unissued bookings are intentionally excluded. Credit notes reverse the original liability with negative amounts. Multi-currency reports return total_tax_amount as null and expose total_tax_amount_by_currency for remittance totals.
Parameters
fromdatetimeoptionalInclusive issued_at lower bound.
todatetimeoptionalInclusive issued_at upper bound.
currencystringoptionalISO currency filter.
legal_issuer_idintegeroptionalFilter by org-owned legal issuer.
jurisdiction_codestringoptionalExact component jurisdiction.
authoritystringoptionalExact tax authority name.
category_codestringoptionalTax category code.
is_testbooleanoptionalSet true for sandbox invoices, false for live invoices. If omitted, live invoices are returned unless include_test=true.
curl "https://api.taxlens.getdynamiq.ai/v1/invoices/liability-summary?from=2026-06-01T00:00:00Z&to=2026-06-30T23:59:59Z" \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"items": [
{
"jurisdiction_code": "US-NY-NYC",
"authority": "New York City Department of Finance",
"category_code": "hotel_occupancy_tax",
"currency": "USD",
"taxable_amount": "750.0000",
"tax_amount": "44.0600",
"invoice_count": 3
}
],
"total_tax_amount": "44.0600",
"total_tax_amount_by_currency": {
"USD": "44.0600"
}
}/v1/invoices/liability-linesInvoice Tax Lines
Return invoice-level component lines for liability reconciliation and remittance workpapers. Commercial invoices (380) and self-billed invoices (389) are positive; credit notes (381) are negative.
Parameters
limitintegeroptional1-500 component lines.
offsetintegeroptionalPagination offset.
jurisdiction_codestringoptionalExact component jurisdiction.
authoritystringoptionalExact authority name.
category_codestringoptionalTax category code.
curl "https://api.taxlens.getdynamiq.ai/v1/invoices/liability-lines?category_code=hotel_occupancy_tax" \
-H "Authorization: Bearer $TAXLENS_TOKEN"{
"items": [
{
"invoice_number": "2026-000001",
"document_type": "380",
"booking_id": 123,
"property_id": 42,
"jurisdiction_code": "US-NY-NYC",
"authority": "New York City Department of Finance",
"category_code": "hotel_occupancy_tax",
"taxable_amount": "750.0000",
"tax_amount": "44.0600",
"sign": 1
}
],
"total": 1,
"next_cursor": null
}/v1/invoices/liability-export.csvExport Invoice Tax CSV
Download the same invoice tax lines as CSV for remittance preparation and reconciliation.
curl "https://api.taxlens.getdynamiq.ai/v1/invoices/liability-export.csv?currency=USD" \
-H "Authorization: Bearer $TAXLENS_TOKEN"invoice_number,document_type,booking_id,property_id,external_reference,issued_at,legal_issuer_id,currency,jurisdiction_code,authority,category_code,taxable_amount,tax_amount,sign
2026-000001,380,123,42,PMS-123,2026-06-04T12:00:00Z,7,USD,US-NY-NYC,New York City Department of Finance,hotel_occupancy_tax,750.0000,44.0600,1Addresses
/v1/addresses/validateValidate Address
Forward-geocode a free-text address and resolve it to a TaxLens jurisdiction code. Use this when you have a street address but no lat/lng or `jurisdiction_code` yet — the response can be fed directly into POST /v1/tax/calculate. The geocoder chain is LocationIQ primary with Nominatim fallback; sub-city resolution walks district → suburb → city → county → state, biased by `country_hint` when supplied. Returns 422 if the address cannot be geocoded at all. Note: a separate address-autocomplete / search endpoint is not yet available — this is the only address endpoint today.
Request Body
addressstringrequiredFree-text address (3-500 chars). The more specific, the better — "350 5th Ave, New York, NY" resolves higher-confidence than "New York".
country_hintstringoptionalISO 3166-1 alpha-2 country code (e.g. "US", "HR"). Biases the geocoder toward that country and avoids ambiguity (e.g. Paris, France vs Paris, Texas).
curl -X POST https://api.taxlens.getdynamiq.ai/v1/addresses/validate \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-api-key>" \
-d '{
"address": "350 5th Ave, New York, NY 10118",
"country_hint": "US"
}'{
"normalized_address": "Empire State Building, 350, 5th Avenue, Manhattan, New York County, City of New York, New York, 10118, United States",
"lat": 40.748432,
"lng": -73.985656,
"country": "United States",
"country_code": "US",
"state": "New York",
"city": "New York",
"jurisdiction": {
"code": "US-NY-NYC-MAN",
"name": "Manhattan",
"jurisdiction_type": "district",
"path": "US.NY.NYC.MAN"
},
"ancestors": [
{ "code": "US", "name": "United States", "jurisdiction_type": "country", "path": "US" },
{ "code": "US-NY", "name": "New York", "jurisdiction_type": "state", "path": "US.NY" },
{ "code": "US-NY-NYC", "name": "New York City", "jurisdiction_type": "city", "path": "US.NY.NYC" }
],
"confidence": "high"
}