> ## 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.

# Fermetures exceptionnelles

> Planifiez des fermetures ponctuelles pour un établissement avec l’objet special_closing et son cycle de vie CRUD complet.

Une **fermeture exceptionnelle** est une période ponctuelle pendant laquelle un
établissement est fermé, en complément de ses [horaires d’ouverture](/fr/location-organization-management/location-settings)
habituels : jours fériés, événements privés, travaux ou fermeture imprévue, par exemple.
Chaque fermeture appartient à un seul établissement et est délimitée par un instant de
début et un instant de fin.

Les fermetures exceptionnelles appartiennent à leur établissement parent. Le chemin
complet est `/v1/locations/{location_id}/special_closings`, et chaque fermeture est
accessible à l’adresse `/v1/locations/{location_id}/special_closings/{special_closing_id}`.

## L’objet special\_closing

```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">
  Identifiant opaque unique, préfixé par `scl_`. Traitez-le comme une chaîne sensible à la casse.
</ResponseField>

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

<ResponseField name="location_id" type="string">
  L’établissement parent (`loc_…`) auquel cette fermeture s’applique.
</ResponseField>

<ResponseField name="starts_at" type="string">
  Instant **UTC** au format ISO 8601 auquel la fermeture commence.
</ResponseField>

<ResponseField name="ends_at" type="string">
  Instant **UTC** au format ISO 8601 auquel la fermeture prend fin.
</ResponseField>

<ResponseField name="reason" type="string | null">
  Motif lisible de la fermeture (par exemple `"Jour férié"`). Ce motif donne au commis IA
  le contexte nécessaire pour expliquer la fermeture aux clients. Peut valoir `null`.
</ResponseField>

<ResponseField name="created_at" type="string">
  Horodatage UTC de création au format ISO 8601.
</ResponseField>

<ResponseField name="updated_at" type="string">
  Horodatage UTC de dernière mise à jour au format ISO 8601.
</ResponseField>

## Fuseaux horaires

`starts_at` et `ends_at` sont des **instants UTC absolus** : le `Z` final fait partie
du contrat. Il ne s’agit pas d’heures locales. Pour présenter une fermeture à un
exploitant de restaurant, convertissez chaque instant dans le `timezone` de
l’établissement (un identifiant IANA tel que `Europe/Zurich`, disponible dans
[l’objet établissement](/fr/location-organization-management/locations)).

<Warning>
  Envoyez toujours `starts_at` et `ends_at` en UTC. Si vous recueillez une date et une
  heure locales auprès d’un utilisateur, convertissez-les en UTC avant d’appeler l’API,
  en tenant compte de l’heure d’été dans le fuseau de l’établissement. Une fermeture
  prévue pour couvrir « toute la journée du 25 décembre à Zurich » s’étend de
  **23:00 UTC le 24 décembre à 23:00 UTC le 25 décembre**, et non de minuit à minuit UTC.
</Warning>

L’exemple ci-dessous convertit une plage horaire locale dans `Europe/Zurich` en instants UTC attendus par l’API.

<CodeGroup>
  ```javascript Node.js theme={null}
  // « Fermé toute la journée du 25 décembre 2026 » dans le fuseau local de l’établissement.
  const timezone = "Europe/Zurich";

  function toUtcInstant(localDateTime, tz) {
    // localDateTime est au format "YYYY-MM-DDTHH:mm" dans le fuseau cible.
    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 Affichage dans le fuseau de l’établissement theme={null}
  // Affiche un instant UTC enregistré à un opérateur dans le fuseau de l’établissement.
  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>

## Lister les fermetures exceptionnelles

Renvoie une [liste paginée](/fr/concepts/pagination) des fermetures d’un établissement, triées par `created_at`.

```text Point de terminaison theme={null}
GET /v1/locations/{location_id}/special_closings
```

### Paramètres de requête

<ParamField query="limit" type="integer" default="10">
  Nombre de résultats par page, compris entre `1` et `100`.
</ParamField>

<ParamField query="starting_after" type="string">
  Curseur de la page suivante : l’`id` du dernier objet de la page précédente. Mutuellement exclusif avec `ending_before`.
</ParamField>

<ParamField query="ending_before" type="string">
  Curseur de la page précédente : l’`id` du premier objet de la page actuelle. Mutuellement exclusif avec `starting_after`.
</ParamField>

<ParamField query="created_after" type="string">
  Renvoie les fermetures créées après cet horodatage ISO 8601.
</ParamField>

<ParamField query="created_before" type="string">
  Renvoie les fermetures créées avant cet horodatage ISO 8601.
</ParamField>

<ParamField query="updated_after" type="string">
  Renvoie les fermetures mises à jour après cet horodatage ISO 8601.
</ParamField>

<ParamField query="updated_before" type="string">
  Renvoie les fermetures mises à jour avant cet horodatage ISO 8601.
</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 Réponse 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"
}
```

## Créer une fermeture exceptionnelle

Crée une fermeture pour un établissement. Comme ce `POST` crée une ressource, l’en-tête `Idempotency-Key` est **obligatoire**.

```text Point de terminaison theme={null}
POST /v1/locations/{location_id}/special_closings
```

### Paramètres du corps

<ParamField body="starts_at" type="string" required>
  Instant **UTC** au format ISO 8601 auquel la fermeture commence.
</ParamField>

<ParamField body="ends_at" type="string" required>
  Instant **UTC** au format ISO 8601 auquel la fermeture prend fin.
</ParamField>

<ParamField body="reason" type="string">
  Motif lisible facultatif de la fermeture. Il est transmis au commis IA afin qu’il puisse
  expliquer la fermeture aux clients. Omettez-le ou envoyez `null` pour ne pas le définir.
</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 Réponse 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>
  Rejouer la même `Idempotency-Key` avec le même corps renvoie la fermeture d’origine,
  avec l’en-tête `Idempotent-Replayed: true`. Réutiliser la clé avec un corps
  **différent** renvoie `409 idempotency_error`, tandis qu’une requête identique exécutée
  simultanément renvoie `409 conflict_error`. Les clés sont conservées pendant
  **24 heures**. Consultez [Idempotence](/fr/concepts/idempotency).
</Note>

## Récupérer une fermeture exceptionnelle

Récupère une fermeture à partir de son identifiant.

```text Point de terminaison 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 Réponse 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"
}
```

Une requête portant sur un identifiant qui n’existe pas pour l’établissement renvoie `404 not_found_error`.

## Mettre à jour une fermeture exceptionnelle

Met à jour un ou plusieurs champs d’une fermeture. Seuls les champs envoyés sont modifiés ; les champs omis conservent leur valeur actuelle.

```text Point de terminaison theme={null}
PATCH /v1/locations/{location_id}/special_closings/{special_closing_id}
```

### Paramètres du corps

<ParamField body="starts_at" type="string">
  Nouvel instant de début, au format ISO 8601 **UTC**.
</ParamField>

<ParamField body="ends_at" type="string">
  Nouvel instant de fin, au format ISO 8601 **UTC**.
</ParamField>

<ParamField body="reason" type="string | null">
  Nouveau motif transmis au commis IA afin qu’il puisse expliquer la fermeture aux clients.
  Envoyez `null` pour effacer un motif existant.
</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 Réponse 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"
}
```

## Supprimer une fermeture exceptionnelle

Supprime définitivement une fermeture. L’établissement revient à ses horaires d’ouverture habituels pendant cette période.

```text Point de terminaison 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>

Supprimer une fermeture qui n’existe pas renvoie `404 not_found_error`.

## Erreurs

Les points de terminaison des fermetures exceptionnelles utilisent le
[modèle d’erreur](/fr/concepts/errors) standard. Cas les plus courants :

| Statut HTTP | `error.type`            | Cas                                                                                                      |
| ----------- | ----------------------- | -------------------------------------------------------------------------------------------------------- |
| `400`       | `invalid_request_error` | Un horodatage est manquant ou n’est pas conforme au format ISO 8601, ou un paramètre est mal formé.      |
| `401`       | `authentication_error`  | Aucune clé API n’a été fournie.                                                                          |
| `403`       | `authorization_error`   | Le périmètre ou les permissions de la clé n’autorisent pas cette action.                                 |
| `404`       | `not_found_error`       | L’identifiant de l’établissement ou de la fermeture n’existe pas ou n’est pas accessible avec votre clé. |
| `409`       | `idempotency_error`     | L’`Idempotency-Key` a été réutilisée avec un corps différent.                                            |
| `409`       | `conflict_error`        | Une requête simultanée en cours a utilisé la même `Idempotency-Key`.                                     |
| `429`       | `rate_limit_error`      | La limite de débit par clé a été dépassée ; réessayez après `Retry-After`.                               |

```json Exemple d’erreur 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"
  }
}
```

## Étapes suivantes

<Card title="Horaires d’ouverture" icon="clock" href="/fr/location-organization-management/location-settings">
  Les fermetures exceptionnelles s’ajoutent au planning hebdomadaire habituel de livraison et de retrait d’un établissement.
</Card>

<Card title="Établissements" icon="store" href="/fr/location-organization-management/locations">
  Consultez le `timezone` d’un établissement pour afficher `starts_at` et `ends_at` en heure locale.
</Card>

<Card title="Idempotence" icon="rotate" href="/fr/concepts/idempotency">
  La création d’une fermeture nécessite une `Idempotency-Key`. Découvrez le fonctionnement des nouvelles tentatives sécurisées.
</Card>
