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

# Special closings

> Schedule one-off closures for a location with the special_closing object and its full CRUD lifecycle.

A **special closing** is a one-off window during which a location is closed, on top of its regular [opening hours](/location-organization-management/location-settings) — think public holidays, private events, renovations, or an unplanned shutdown. Each closing belongs to exactly one location and is bounded by a start and end instant.

Special closings live under their parent location. The full path is `/v1/locations/{location_id}/special_closings`, with individual closings addressed at `/v1/locations/{location_id}/special_closings/{special_closing_id}`.

## The special\_closing object

```json theme={null}
{
  "id": "scl_3b9f2c7a1e8d40f6",
  "object": "special_closing",
  "location_id": "loc_8f2c91d4a7e34b10",
  "starts_at": "2026-12-24T22:00:00Z",
  "ends_at": "2026-12-26T06:00:00Z",
  "reason": "Christmas holidays",
  "created_at": "2026-05-29T10:15:00Z",
  "updated_at": "2026-05-29T10:15:00Z"
}
```

<ResponseField name="id" type="string">
  Unique opaque identifier, prefixed `scl_`. Treat it as a case-sensitive string.
</ResponseField>

<ResponseField name="object" type="string">
  Always `"special_closing"`.
</ResponseField>

<ResponseField name="location_id" type="string">
  The parent location (`loc_…`) this closing applies to.
</ResponseField>

<ResponseField name="starts_at" type="string">
  ISO 8601 **UTC** instant at which the closure begins.
</ResponseField>

<ResponseField name="ends_at" type="string">
  ISO 8601 **UTC** instant at which the closure ends.
</ResponseField>

<ResponseField name="reason" type="string | null">
  A human-readable explanation for the closure (for example `"Public holiday"`). May be `null`.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 UTC creation timestamp.
</ResponseField>

<ResponseField name="updated_at" type="string">
  ISO 8601 UTC last-update timestamp.
</ResponseField>

## Time zones

`starts_at` and `ends_at` are **absolute UTC instants** — the trailing `Z` is part of the contract. They are not wall-clock times. To present a closing to a restaurant operator, convert each instant into the location's `timezone` (an IANA identifier such as `Europe/Zurich`, available on the [location object](/location-organization-management/locations)).

<Warning>
  Always send `starts_at` and `ends_at` in UTC. If you collect a local date and time from a user, convert it to UTC before calling the API — accounting for daylight saving time in the location's zone. A closing meant to run "all of December 25th in Zurich" spans **23:00 UTC Dec 24 → 23:00 UTC Dec 25**, not midnight to midnight UTC.
</Warning>

The example below converts a local wall-clock window in `Europe/Zurich` into the UTC instants the API expects.

<CodeGroup>
  ```javascript Node.js theme={null}
  // "Closed all day on 25 December 2026" in the location's local zone.
  const timezone = "Europe/Zurich";

  function toUtcInstant(localDateTime, tz) {
    // localDateTime is "YYYY-MM-DDTHH:mm" in the target zone.
    const target = new Date(`${localDateTime}:00`);
    const asUtc = new Date(target.toLocaleString("en-US", { timeZone: "UTC" }));
    const asTz = new Date(target.toLocaleString("en-US", { timeZone: tz }));
    const offsetMs = asUtc.getTime() - asTz.getTime();
    return new Date(target.getTime() + offsetMs).toISOString();
  }

  const startsAt = toUtcInstant("2026-12-25T00:00", timezone); // 2026-12-24T23:00:00.000Z
  const endsAt = toUtcInstant("2026-12-26T00:00", timezone); // 2026-12-25T23:00:00.000Z
  ```

  ```javascript Render in the location zone theme={null}
  // Display a stored UTC instant to an operator in the location's zone.
  function renderLocal(utcInstant, tz) {
    return new Intl.DateTimeFormat("fr-CH", {
      timeZone: tz,
      dateStyle: "medium",
      timeStyle: "short",
    }).format(new Date(utcInstant));
  }

  renderLocal("2026-12-24T23:00:00Z", "Europe/Zurich"); // "25 déc. 2026, 00:00"
  ```
</CodeGroup>

## List special closings

Returns a [paginated list](/concepts/pagination) of the closings for a location, ordered by `created_at`.

```text Endpoint theme={null}
GET /v1/locations/{location_id}/special_closings
```

### Query parameters

<ParamField query="limit" type="integer" default="10">
  Number of results per page, between `1` and `100`.
</ParamField>

<ParamField query="starting_after" type="string">
  Cursor for the next page — the `id` of the last object in the previous page. Mutually exclusive with `ending_before`.
</ParamField>

<ParamField query="ending_before" type="string">
  Cursor for the previous page — the `id` of the first object in the current page. Mutually exclusive with `starting_after`.
</ParamField>

<ParamField query="created_after" type="string">
  Return closings created after this ISO 8601 timestamp.
</ParamField>

<ParamField query="created_before" type="string">
  Return closings created before this ISO 8601 timestamp.
</ParamField>

<ParamField query="updated_after" type="string">
  Return closings updated after this ISO 8601 timestamp.
</ParamField>

<ParamField query="updated_before" type="string">
  Return closings updated before this ISO 8601 timestamp.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl "https://server.chataigne.ai/v1/locations/loc_8f2c91d4a7e34b10/special_closings?limit=20" \
    -H "x-api-key: ch_org_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  ```

  ```javascript Node.js theme={null}
  const locationId = "loc_8f2c91d4a7e34b10";
  const res = await fetch(
    `https://server.chataigne.ai/v1/locations/${locationId}/special_closings?limit=20`,
    { headers: { "x-api-key": process.env.CHATAIGNE_API_KEY } }
  );
  const closings = await res.json();
  console.log(closings.data);
  ```
</CodeGroup>

```json Response theme={null}
{
  "object": "list",
  "data": [
    {
      "id": "scl_3b9f2c7a1e8d40f6",
      "object": "special_closing",
      "location_id": "loc_8f2c91d4a7e34b10",
      "starts_at": "2026-12-24T22:00:00Z",
      "ends_at": "2026-12-26T06:00:00Z",
      "reason": "Christmas holidays",
      "created_at": "2026-05-29T10:15:00Z",
      "updated_at": "2026-05-29T10:15:00Z"
    }
  ],
  "has_more": false,
  "url": "/v1/locations/loc_8f2c91d4a7e34b10/special_closings"
}
```

## Create a special closing

Creates a closing under a location. This is a resource-creating `POST`, so an `Idempotency-Key` header is **required**.

```text Endpoint theme={null}
POST /v1/locations/{location_id}/special_closings
```

### Body parameters

<ParamField body="starts_at" type="string" required>
  ISO 8601 **UTC** instant at which the closure begins.
</ParamField>

<ParamField body="ends_at" type="string" required>
  ISO 8601 **UTC** instant at which the closure ends.
</ParamField>

<ParamField body="reason" type="string">
  Optional human-readable explanation for the closure. Omit or send `null` to leave it unset.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl https://server.chataigne.ai/v1/locations/loc_8f2c91d4a7e34b10/special_closings \
    -H "x-api-key: ch_org_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Idempotency-Key: 6f1d3b8a-2c47-4e90-9a1b-0d2f7c5e1234" \
    -H "Content-Type: application/json" \
    -d '{
      "starts_at": "2026-12-24T22:00:00Z",
      "ends_at": "2026-12-26T06:00:00Z",
      "reason": "Christmas holidays"
    }'
  ```

  ```javascript Node.js theme={null}
  import { randomUUID } from "node:crypto";

  const locationId = "loc_8f2c91d4a7e34b10";
  const res = await fetch(
    `https://server.chataigne.ai/v1/locations/${locationId}/special_closings`,
    {
      method: "POST",
      headers: {
        "x-api-key": process.env.CHATAIGNE_API_KEY,
        "Idempotency-Key": randomUUID(),
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        starts_at: "2026-12-24T22:00:00Z",
        ends_at: "2026-12-26T06:00:00Z",
        reason: "Christmas holidays",
      }),
    }
  );
  const closing = await res.json();
  ```
</CodeGroup>

```json Response theme={null}
{
  "id": "scl_3b9f2c7a1e8d40f6",
  "object": "special_closing",
  "location_id": "loc_8f2c91d4a7e34b10",
  "starts_at": "2026-12-24T22:00:00Z",
  "ends_at": "2026-12-26T06:00:00Z",
  "reason": "Christmas holidays",
  "created_at": "2026-05-29T10:15:00Z",
  "updated_at": "2026-05-29T10:15:00Z"
}
```

<Note>
  Replaying the same `Idempotency-Key` with the same body returns the original closing, with the header `Idempotent-Replayed: true`. Reusing the key with a **different** body returns `409 idempotency_error`, and a concurrent in-flight duplicate returns `409 conflict_error`. Keys are retained for **24 hours**. See [Idempotency](/concepts/idempotency).
</Note>

## Retrieve a special closing

Fetches a single closing by id.

```text Endpoint theme={null}
GET /v1/locations/{location_id}/special_closings/{special_closing_id}
```

<CodeGroup>
  ```bash curl theme={null}
  curl https://server.chataigne.ai/v1/locations/loc_8f2c91d4a7e34b10/special_closings/scl_3b9f2c7a1e8d40f6 \
    -H "x-api-key: ch_org_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  ```

  ```javascript Node.js theme={null}
  const locationId = "loc_8f2c91d4a7e34b10";
  const closingId = "scl_3b9f2c7a1e8d40f6";
  const res = await fetch(
    `https://server.chataigne.ai/v1/locations/${locationId}/special_closings/${closingId}`,
    { headers: { "x-api-key": process.env.CHATAIGNE_API_KEY } }
  );
  const closing = await res.json();
  ```
</CodeGroup>

```json Response theme={null}
{
  "id": "scl_3b9f2c7a1e8d40f6",
  "object": "special_closing",
  "location_id": "loc_8f2c91d4a7e34b10",
  "starts_at": "2026-12-24T22:00:00Z",
  "ends_at": "2026-12-26T06:00:00Z",
  "reason": "Christmas holidays",
  "created_at": "2026-05-29T10:15:00Z",
  "updated_at": "2026-05-29T10:15:00Z"
}
```

A request for an id that does not exist under the location returns `404 not_found_error`.

## Update a special closing

Updates one or more fields on a closing. Only the fields you send are changed; omitted fields keep their current value.

```text Endpoint theme={null}
PATCH /v1/locations/{location_id}/special_closings/{special_closing_id}
```

### Body parameters

<ParamField body="starts_at" type="string">
  New start instant, ISO 8601 **UTC**.
</ParamField>

<ParamField body="ends_at" type="string">
  New end instant, ISO 8601 **UTC**.
</ParamField>

<ParamField body="reason" type="string | null">
  New reason. Send `null` to clear an existing reason.
</ParamField>

<CodeGroup>
  ```bash curl theme={null}
  curl -X PATCH https://server.chataigne.ai/v1/locations/loc_8f2c91d4a7e34b10/special_closings/scl_3b9f2c7a1e8d40f6 \
    -H "x-api-key: ch_org_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "ends_at": "2026-12-27T06:00:00Z",
      "reason": "Christmas & Boxing Day"
    }'
  ```

  ```javascript Node.js theme={null}
  const locationId = "loc_8f2c91d4a7e34b10";
  const closingId = "scl_3b9f2c7a1e8d40f6";
  const res = await fetch(
    `https://server.chataigne.ai/v1/locations/${locationId}/special_closings/${closingId}`,
    {
      method: "PATCH",
      headers: {
        "x-api-key": process.env.CHATAIGNE_API_KEY,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        ends_at: "2026-12-27T06:00:00Z",
        reason: "Christmas & Boxing Day",
      }),
    }
  );
  const closing = await res.json();
  ```
</CodeGroup>

```json Response theme={null}
{
  "id": "scl_3b9f2c7a1e8d40f6",
  "object": "special_closing",
  "location_id": "loc_8f2c91d4a7e34b10",
  "starts_at": "2026-12-24T22:00:00Z",
  "ends_at": "2026-12-27T06:00:00Z",
  "reason": "Christmas & Boxing Day",
  "created_at": "2026-05-29T10:15:00Z",
  "updated_at": "2026-05-29T11:42:08Z"
}
```

## Delete a special closing

Permanently removes a closing. The location reverts to its regular opening hours for that window.

```text Endpoint theme={null}
DELETE /v1/locations/{location_id}/special_closings/{special_closing_id}
```

<CodeGroup>
  ```bash curl theme={null}
  curl -X DELETE https://server.chataigne.ai/v1/locations/loc_8f2c91d4a7e34b10/special_closings/scl_3b9f2c7a1e8d40f6 \
    -H "x-api-key: ch_org_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  ```

  ```javascript Node.js theme={null}
  const locationId = "loc_8f2c91d4a7e34b10";
  const closingId = "scl_3b9f2c7a1e8d40f6";
  const res = await fetch(
    `https://server.chataigne.ai/v1/locations/${locationId}/special_closings/${closingId}`,
    {
      method: "DELETE",
      headers: { "x-api-key": process.env.CHATAIGNE_API_KEY },
    }
  );
  ```
</CodeGroup>

Deleting a closing that does not exist returns `404 not_found_error`.

## Errors

Special closing endpoints use the standard [error model](/concepts/errors). The most common cases:

| HTTP status | `error.type`            | When                                                                            |
| ----------- | ----------------------- | ------------------------------------------------------------------------------- |
| `400`       | `invalid_request_error` | A timestamp is missing or not valid ISO 8601, or a parameter is malformed.      |
| `401`       | `authentication_error`  | No API key was provided.                                                        |
| `403`       | `authorization_error`   | An admin key (`ch_admin_…`) was used on `/v1`, or a dashboard session was used. |
| `404`       | `not_found_error`       | The location or closing id does not exist or is not accessible to your key.     |
| `409`       | `idempotency_error`     | The `Idempotency-Key` was reused with a different body.                         |
| `409`       | `conflict_error`        | A concurrent in-flight request used the same `Idempotency-Key`.                 |
| `429`       | `rate_limit_error`      | The per-key rate limit was exceeded; retry after `Retry-After`.                 |

```json Example error theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "ends_at must be a valid ISO 8601 UTC timestamp.",
    "param": "ends_at",
    "request_id": "req_2y8Fk1Zx0Qm7"
  }
}
```

## Next steps

<Card title="Opening hours" icon="clock" href="/location-organization-management/location-settings">
  Special closings sit on top of a location's regular weekly delivery and pickup schedule.
</Card>

<Card title="Locations" icon="store" href="/location-organization-management/locations">
  Read a location's `timezone` to render `starts_at` and `ends_at` in local wall-clock time.
</Card>

<Card title="Idempotency" icon="rotate" href="/concepts/idempotency">
  Creating a closing requires an `Idempotency-Key`. Learn how safe retries work.
</Card>
