# Idempotency

Networks are unreliable. If a request gets no answer, you should be able to just repeat it without creating duplicate calendars or events.

## Events: idempotent by design

- **`PUT` on an event** writes the event under your `external_id`. Sent twice, it results in the same event. If nothing differs from before, you get `"changed": false` and nothing is written.
- **`DELETE` on an event** always answers `204`, also when the event was already deleted.
- **Batch** consists of `PUT` and `DELETE` and can therefore be repeated as a whole, too.

## Creating calendars: `Idempotency-Key`

`POST /v1/publisher/calendars` creates a new calendar on every call. Send a unique header with it, for example a UUID:

```bash
curl https://api.kalender-sync.de/v1/publisher/calendars \
  -H "Authorization: Bearer $KS_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3b0f6a52-6a1e-4c55-9d1b-0a7f2c9e1d44" \
  -d '{ "name": "Riverside Yoga", "timezone": "America/New_York" }'
```

- If you repeat **the same request with the same key** within 24 hours, you get the stored answer of the first call, with the header `Idempotent-Replayed: true`. No second calendar is created.
- The same key with **a different request** (different body or path) answers `409` [`idempotency_key_reused`](https://docs.kalender-sync.de/en/api/errors/idempotency_key_reused/).
- If the first request is still running, a repeat answers `409` [`idempotency_in_progress`](https://docs.kalender-sync.de/en/api/errors/idempotency_in_progress/). Wait a moment and try again.
- If the first request failed with an error, nothing is stored. The repeat then runs normally.
- The key may be up to 255 characters, otherwise `400` [`invalid_idempotency_key`](https://docs.kalender-sync.de/en/api/errors/invalid_idempotency_key/). It is scoped to the API key.

## Optimistic locking

To keep two systems from overwriting each other, send `If-Match` with the last `sequence` you saw on `PUT` to an event. → [Events: If-Match](https://docs.kalender-sync.de/en/api/events/#optimistic-locking-with-if-match)