---
title: "Link a channel’s calendar to a unit and import its stays"
method: POST
path: "/api/v1/units/{id}/calendars"
tags: ["Portfolio"]
---

# Link a channel’s calendar to a unit and import its stays

`POST /api/v1/units/{id}/calendars`

## Request

- Base URL: `http://localhost:5180`
- URL: `http://localhost:5180/api/v1/units/{id}/calendars`
- Auth: API key in cookie `better-auth.session_token`

Reads the iCal export once to prove it is a calendar, then imports it as `short_stay_sync_connection` does. Linking a channel the unit already reads from another address replaces it, and the stays the old address brought are removed in the same write. The address is kept, so the stays are read again every hour without anybody asking. A stay whose description states its payout is priced from it; anything else is held, earning nothing, until somebody prices it.

## Path parameters

- `id` string, required

## Request body

- object
  - `url` string, required
  - `channel` 'airbnb' | 'booking_com' | 'vrbo' | 'direct' | 'expedia' | 'other', required: Who publishes the calendar. Linking the same channel again replaces its address.

## Response `200`

What the calendar said, and what was written.

- object
  - `ok` unknown, required
  - `data` object, required
    - `connectionId` string, required: Stable record id, e.g. `lse_012`.
    - `unitId` string, required: Stable record id, e.g. `lse_012`.
    - `status` 'connected' | 'error', required
    - `read` number, required: Events in the feed, blocks included.
    - `created` number, required: Stays the feed showed and we had no record of. Written held, earning nothing, unless the stay’s own description states its payout.
    - `nights` number, required: Nights those stays put on the calendar.
    - `linked` number, required: Stays we already held, now carrying the channel’s own reference.
    - `priced` number, required: Stays given the payout their own description states, read through a template learned from the feed and checked against each description before a figure is taken.
    - `unchanged` number, required
    - `blocked` number, required: Dates the channel is closing rather than selling. Counted, never written as a booking.
    - `skipped` number, required: Events with no usable id or dates.
    - `gone` string[], required: Future stays we hold that the feed no longer shows. Reported and never cancelled here: a feed that failed to load looks exactly like a feed with nothing in it.
    - `clashes` object[], required: Nights sold twice, on two channels. Each raises a task, because there is a guest on the end of it.
      - `bookingId` string, required: Stable record id, e.g. `lse_012`.
      - `heldChannel` 'airbnb' | 'booking_com' | 'vrbo' | 'direct' | 'expedia' | 'other', required
      - `uid` string, required: The channel’s own id for the stay it is trying to add.
      - `from` string, required: A calendar date, `YYYY-MM-DD`.
      - `to` string, required: A calendar date, `YYYY-MM-DD`.
    - `tasksRaised` number, required
  - `meta` object, required
    - `requestId` string, required: Echoed in the x-request-id header. Quote it in a bug report.

## Other responses

- `400`: The address is not a calendar, or the channel could not be reached.
- `404`: No such unit, or no such calendar on it.

---

[API](https://skmtc.dev/dardaris/apis/dardaris-api.md) · [All operations](https://skmtc.dev/dardaris/apis/dardaris-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dardaris/apis/dardaris-api/revisions/4aff33835c73?raw)
