Write Restrictions
Restrictions are written in Restriction Batches: one call covers many Properties and date ranges, ZenHost answers at once, then sends the changes to the Sales Channels at their pace. The rate you write is the base rate of the Property: its Property Pricing Modifiers (for example +15 % on one Sales Channel) still apply on top. A batch never sets a Manual Override, so you can push your whole calendar every day.
1. Read the calendar first
GET /restrictions returns, per Property and date (up to 100 Properties and 366 days per call), the current values and unwritable_fields: the fields a batch will refuse on that date, and why. Skip them instead of discovering refusals afterwards.
curl -G https://app.zenhost.com/api/v1/restrictions \
-H "Authorization: Bearer zh_at_..." \
-d "property_id[]=PROPERTY_ID" \
-d from=2026-11-01 \
-d to=2026-11-30| Unwritable reason | Meaning |
|---|---|
manual_override | The host froze this field on this date in ZenHost. Your value is ignored until the host lifts the Manual Override. |
managed_by_another_pricing_tool | The Property is priced by another pricing tool, which owns rate, min_stay, closed_to_arrival, and closed_to_departure. Only availability is writable. |
blocked_by_linked_property | The night is closed because a linked Property (for example the whole house this room belongs to) is booked or closed. availability is not writable on that night. |
One Property, one pricing tool
2. Try it with dry_run
With "dry_run": true, ZenHost answers 200 with what would change on each Property (current and new value per date and field, and the refused fields), without storing or sending anything. Test your script against the real calendar this way. A dry run still counts against the submission limit of 10 per 60 seconds.
curl -X POST https://app.zenhost.com/api/v1/restriction-batches \
-H "Authorization: Bearer zh_at_..." \
-H "Content-Type: application/json" \
-d '{
"dry_run": true,
"entries": [
{
"property_id": "PROPERTY_ID",
"from": "2026-11-01",
"to": "2026-11-30",
"values": { "rate": 129.5, "min_stay": 2 }
}
]
}'3. Submit, idempotently
Without dry_run, ZenHost answers 202 with the batch id. Send an Idempotency-Key header (one per logical run): replaying the same key on the same App Connection within 24 hours returns the batch it created instead of a second one, so a retry after a timeout is safe. The same key with a different body answers idempotency_key_reused.
Limits: up to 20,000 entries over 100 Properties and 200,000 date × field changes per batch (one batch per page of GET /properties covers any account); dates from today to today + 730 days, both read in the time zone of each Property. A malformed body, a date outside that range or a Property outside your Connection Scope rejects the whole batch with 422; nothing is stored.
curl -X POST https://app.zenhost.com/api/v1/restriction-batches \
-H "Authorization: Bearer zh_at_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: nightly-2026-10-10" \
-d '{ "entries": [ ... ] }'
# 202 Accepted
{ "id": "BATCH_ID", "status": "queued" }4. Read the outcome
Read GET /restriction-batches/{id}, or wait for the restriction_batch.completed App Event instead of polling. The batch moves from queued and processing to a final status: completed, partially_failed, failed or cancelled. For each Property, the report counts the changes applied, unchanged, superseded, refused, failed and cancelled, and lists refused fields and failures as runs of dates. Reports are kept 30 days.
- Properties succeed or fail on their own: a refused Property never blocks the others, and nothing is rolled back.
- Batches apply in submission order: on the same date and field, the latest batch wins and the older change, if not sent yet, is
superseded(not a failure). - A value equal to the current one is
unchangedand is not sent again: pushing an identical calendar every day costs nothing on the Sales Channels. - A field the host froze after you submitted is still refused at send time with
manual_override. - If the owner revokes the connection (or you revoke your last refresh token), ZenHost suspends your App, or the account is no longer allowed on your Private App, changes not sent yet are
cancelledat once and are never replayed, even if the App is reactivated. The same happens to the changes of a Property the owner removes from your Property Access (or leaves out when connecting again), and to all of them once your App no longer holdsrestrictions.write: the Connection Scope is checked again when sending. A change already on its way to the Sales Channels completes.
curl https://app.zenhost.com/api/v1/restriction-batches/BATCH_ID \
-H "Authorization: Bearer zh_at_..."