# Idempotentiškumas

> Kaip su Idempotency-Key saugiai kartoti POST užklausas ir nesukurti dublių.

Šaltinis: https://diko.app/api-docs/idempotentiskumas

## Kam to reikia

Jei `POST /library/imports` užklausa nutrūko ir nežinote, ar importas buvo sukurtas, pakartojus ją galėtų atsirasti antras importas. `Idempotency-Key` tai išsprendžia: pakartotą užklausą su tuo pačiu raktu API atpažįsta ir grąžina tą patį rezultatą, o nauja operacija nepradedama.

Antraštė palaikoma **visoms POST užklausoms**. Ją rekomenduojame siųsti su `POST /library/imports`.

```http
POST /api/v1/library/imports
Authorization: Bearer dk_live_...
Content-Type: application/json
Idempotency-Key: 8f3a5c2e-1b7d-4e90-a6c4-2d9b0e7f5a13
```

## Taisyklės

| Taisyklė | Reikšmė |
|---|---|
| Formatas | 8–255 simbolių iš aibės `A-Za-z0-9_-:.`. Priimama ir kabutėse (`"abc..."`, IETF Structured Field forma) |
| Taikymo sritis | naudotojas: tas pats `Idempotency-Key` galioja visiems jo API raktams |
| Saugojimas | 24 valandos |
| Pakartojimas | grąžinamas tas pats statusas ir turinys su antrašte `Idempotent-Replayed: true` |
| Tas pats raktas, kita užklausa | 422 [`idempotency_key_reused`](https://diko.app/api-docs/klaidos.md#idempotency_key_reused). Lyginamas metodas, kelias ir kanoninis JSON turinys |
| Lygiagreti užklausa | 409 [`idempotency_in_progress`](https://diko.app/api-docs/klaidos.md#idempotency_in_progress) su `Retry-After` (likusios sekundės) |
| Netinkamas raktas | 400 [`idempotency_key_invalid`](https://diko.app/api-docs/klaidos.md#idempotency_key_invalid) |

Vykdoma užklausa užrakinama 60 sekundžių. Jei jos vykdymas nutrūksta, kitas bandymas su tuo pačiu raktu gali ją perimti pasibaigus užraktui.

### Kas išsaugoma, o kas ne

Išsaugomi tik **2xx** atsakymai ir deterministinės klaidos: 400, 404, 413, 415, 422. Atsakymai **402, 403, 408, 409, 429 ir 5xx neišsaugomi**, o raktas atlaisvinamas. Todėl po tokios klaidos tą pačią užklausą su tuo pačiu raktu galite saugiai kartoti, kai problema išspręsta (pavyzdžiui, papildėte kreditus po 402).

Atsakymai, kuriuose būtų paslapčių, niekada nesaugomi. v1 API tokių atsakymų negrąžina.

## Kaip naudoti

1. Prieš pirmą bandymą sugeneruokite **naują UUID** ir išsaugokite jį kartu su operacija.
2. Siųskite tą patį raktą kiekvienam **tos pačios** operacijos bandymui.
3. Naujai operacijai generuokite naują raktą. Nenaudokite pastovios reikšmės, pavyzdžiui, `"import"`: antra užklausa su kitu turiniu gautų 422, o su tuo pačiu turiniu per 24 val. gautų seną atsakymą.

**Python**

```python
import os
import uuid

import requests

key = str(uuid.uuid4())  # sukuriama vieną kartą ir saugoma kartu su operacija

last_error = None
for attempt in range(3):
    try:
        response = requests.post(
            "https://api.diko.app/api/v1/library/imports",
            headers={
                "Authorization": f"Bearer {os.environ['DIKO_API_KEY']}",
                "Idempotency-Key": key,
            },
            json={"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"},
            timeout=30,
        )
        break
    except (requests.ConnectionError, requests.Timeout) as error:
        last_error = error  # tas pats raktas: pakartojimas dublio nesukurs
else:
    raise last_error  # nepavyko nė vienas iš 3 bandymų

if response.headers.get("Idempotent-Replayed") == "true":
    print("Tai ankstesnės užklausos rezultatas")
```

**TypeScript**

```typescript
const key = crypto.randomUUID() // sukuriama vieną kartą ir saugoma kartu su operacija

let response: Response | undefined
for (let attempt = 0; attempt < 3 && !response; attempt++) {
  try {
    response = await fetch("https://api.diko.app/api/v1/library/imports", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.DIKO_API_KEY}`,
        "Content-Type": "application/json",
        "Idempotency-Key": key, // tas pats raktas: dublio nebus
      },
      body: JSON.stringify({ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }),
      signal: AbortSignal.timeout(30_000),
    })
  } catch {
    // tinklo klaida arba pasibaigė skirtasis laikas: kartojame su tuo pačiu raktu
  }
}
if (!response) throw new Error("Per 3 bandymus atsakymo negauta")
if (response.headers.get("Idempotent-Replayed") === "true") console.log("Tai ankstesnės užklausos rezultatas")
```

> **Pastaba:**
> Importams yra ir papildoma apsauga nuo dublių, nepriklausanti nuo rakto: jei to paties vaizdo įrašo importas jau vyksta, `POST /library/imports` grąžina **200** su esamu importu (`language` skirtumas ignoruojamas).
