> ## Documentation Index
> Fetch the complete documentation index at: https://developer.maatstays.wtf/llms.txt
> Use this file to discover all available pages before exploring further.

# Calendar blocks

> Tell Maat which nights are already taken on your platform — and nothing else.

A calendar block is one message: *these nights are gone*. When somebody books a property
on your platform, you push a block so Maat stops selling those nights; when that
reservation ends early, you release it. Three operations cover the whole lifecycle:

| Operation                          | What it does                                                         |
| :--------------------------------- | :------------------------------------------------------------------- |
| `PUT /v1/blocks/{external_uid}`    | Hold a range of nights. Creates the block, or moves it if it exists. |
| `DELETE /v1/blocks/{external_uid}` | Release the nights that block still holds.                           |
| `GET /v1/blocks`                   | Read your own blocks back — the reconciliation surface.              |

`external_uid` is **your** identifier — typically your reservation id — unique to you.
That makes every operation idempotent: re-sending a block unchanged is a no-op,
re-sending it with different dates **moves** it (old nights released, new nights held,
in one step), and releasing a block that is already gone is a `204` with nothing to
retry.

## Dates: `check_out` is exclusive

`check_out` is the departure day, not the last night held. A block from `2026-09-01` to
`2026-09-05` holds four nights, and the departure day itself stays sellable for same-day
turnover. The response restates `nights` so you can check the arithmetic. A single block
can hold up to 730 nights — two years, enough for any legitimate long-term closure.

## No guest data. Ever. This is a rule you are bound by

Maat has no relationship with somebody who booked on *your* platform, and therefore no
lawful basis to hold anything about them. So the block schema has **no field for a
guest's name, a booking reference, an amount, or a note** — and no free-text field of
any kind, because the obvious thing anyone writes in one is a guest's name.

This is enforced, not requested: an unrecognised field is a `validation_failed` naming
the field, never a silent drop. If your integration framework "helpfully" attaches
reservation details to outgoing payloads, you will find out on your first sandbox call
— which is exactly when you want to find out.

The only descriptive field is `reason`, a closed set — `reserved`, `maintenance`,
`owner_stay`, `other` — enough to tell "somebody is staying" from "the boiler is
broken" in both our audit trails, and structurally incapable of carrying more.

## Conflicts: the whole range or none of it

If any night in your range already carries a Maat booking or a live hold, the push
answers `409` and **nothing is written**. We never apply the free part of a range,
because a partial apply you were told succeeded is the one failure you could not
detect — you would stop holding those nights yourself, and the same room would be sold
twice.

The problem document names every colliding date in a machine-readable `conflicts[]`
array (complete, unlike the human-readable sentence next to it). Act on the array:
re-push a range that excludes those dates, or leave the block off if the stay cannot be
honoured.

Note that the conflict scan runs on **every** push, including an identical retry. If
your nights were taken by a Maat booking after your original push, a re-push tells you
so with a `409` — which is the fact you most need, not an error to suppress.

## Release frees only what the block holds

`DELETE` removes the block and frees the nights **it** is still holding. A night that is
unavailable for another reason — the property's owner closed it, or a Maat booking has
since taken it — stays unavailable. Releasing a block is "I no longer need these nights
held", never "declare these dates open". There is no partial release: shorten a stay by
re-pushing the shorter range with `PUT`.

## Reading your blocks back

`GET /v1/blocks` lists the blocks *you* pushed into the workspace your key addresses —
after a timeout or a `500`, this is how you find out whether a push landed rather than
guessing. Two boundaries are fixed and are not parameters: the blocks are yours (another
partner syncing the same property never sees them, and you never see theirs), and the
workspace is the one your key was issued for. Filter by date window with `from`/`to`,
and page with `page`/`page_size` — see [Pagination](/guides/pagination).

## Addressing the property

Identify the property with either your own `provider_id` or Maat's `listing_id` — and
send exactly one. **Today, use `listing_id`** (it is returned when you push a property,
and in every booking notification): resolving a block by `provider_id` is not enabled
yet and answers `501` naming the working alternative. The reference marks the current
status of each path.

## Availability

Live in the sandbox — pushes, releases, reads and real `409` conflicts. Against a
production workspace every request is authenticated and validated in full, then answers
`501`: keep holding those nights on your own platform until the production contract is
enabled.
