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

# Idempotência

> Como usar a chave de idempotência para enviar requisições com segurança sem duplicar dados.

Operações de escrita na Vendaze API suportam idempotência via header `Idempotency-Key`. Isso permite que você reenvie uma requisição com segurança caso não tenha recebido resposta, sem risco de criar registros duplicados.

## Como usar

Inclua o header `Idempotency-Key` com uma string única gerada por você:

```bash theme={null}
curl -X POST https://api.vendaze.com/v1/deals \
  -H "Authorization: Bearer SEU_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7f8a9b2c-3d4e-5f60-a1b2-c3d4e5f60718" \
  -d '{
    "name": "Contrato Anual Acme",
    "pipeline_id": "uuid-do-pipeline",
    "stage_id": "uuid-do-stage",
    "currency": "BRL",
    "price_cts": 1200000
  }'
```

Se você enviar a mesma requisição novamente com o mesmo `Idempotency-Key`, a API retorna a resposta original sem processar novamente.

## Formato da chave

A chave deve ser:

* Uma string de até 255 caracteres
* Única por operação (recomendado: UUID v4)
* Gerada pelo seu sistema, não reutilizada entre operações distintas

```javascript theme={null}
import { randomUUID } from 'crypto';

const idempotencyKey = randomUUID();
```

## Comportamento por cenário

| Cenário                                              | Comportamento                                    |
| ---------------------------------------------------- | ------------------------------------------------ |
| Primeira requisição                                  | Processa normalmente e armazena a resposta.      |
| Requisição repetida com mesma chave e mesmo body     | Retorna a resposta original sem reprocessar.     |
| Requisição repetida com mesma chave e body diferente | Retorna `422` com código `idempotency_conflict`. |
| Requisição repetida após 24 horas                    | Chave expirada. Processa como nova requisição.   |

## Por que PATCH e DELETE são naturalmente idempotentes

Endpoints `PATCH` e `DELETE` não precisam do header `Idempotency-Key` porque são idempotentes por definição:

* **PATCH:** aplicar a mesma atualização duas vezes produz o mesmo resultado. Definir `full_name` como `"Ana Costa"` uma segunda vez não altera o registro.
* **DELETE:** deletar um registro que já foi deletado retorna `404`. O estado final (registro não existe) é o mesmo independente de quantas vezes você chame.

O header `Idempotency-Key` só é necessário para operações `POST`, onde cada chamada sem chave criaria um novo registro.

## Endpoints que suportam idempotência

O header `Idempotency-Key` é aceito em todos os endpoints de escrita:

* `POST /v1/people`
* `POST /v1/companies`
* `POST /v1/deals`

## Padrão recomendado para criações críticas

```javascript theme={null}
import { randomUUID } from 'crypto';

async function createDeal(payload, accessToken) {
  // Gere a chave uma vez por operação lógica, antes da primeira tentativa
  const idempotencyKey = randomUUID();

  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      const res = await fetch('https://api.vendaze.com/v1/deals', {
        method: 'POST',
        headers: {
          Authorization: `Bearer ${accessToken}`,
          'Content-Type': 'application/json',
          'Idempotency-Key': idempotencyKey,
        },
        body: JSON.stringify(payload),
      });

      if (res.ok) return res.json();

      // Não retentar em erros de validação ou erros de cliente
      if (res.status >= 400 && res.status < 500) {
        const { error } = await res.json();
        throw new Error(`${error.code}: ${error.message}`);
      }

      // Retentar em falhas de rede ou 5xx
      if (res.status >= 500) {
        await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, attempt)));
        continue;
      }
    } catch (err) {
      if (attempt === 2) throw err;
      await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, attempt)));
    }
  }
}
```

## Rastreando chaves de idempotência no banco de dados

Para operações onde você precisa de trilha de auditoria ou quer correlacionar seus registros internos com respostas da API, armazene a chave de idempotência junto ao recurso criado:

```sql theme={null}
CREATE TABLE deal_creations (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  idempotency_key UUID NOT NULL UNIQUE,
  vendaze_deal_id UUID,
  payload         JSONB NOT NULL,
  status          TEXT NOT NULL DEFAULT 'pending',
  created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
  resolved_at     TIMESTAMPTZ
);
```

```javascript theme={null}
async function createDealWithTracking(payload, accessToken) {
  const idempotencyKey = randomUUID();

  await db.dealCreations.insert({
    idempotency_key: idempotencyKey,
    payload,
    status: 'pending',
  });

  try {
    const res = await fetch('https://api.vendaze.com/v1/deals', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotencyKey,
      },
      body: JSON.stringify(payload),
    });

    const deal = await res.json();

    await db.dealCreations.update(
      { idempotency_key: idempotencyKey },
      { status: 'success', vendaze_deal_id: deal.data.id, resolved_at: new Date() },
    );

    return deal;
  } catch (err) {
    await db.dealCreations.update(
      { idempotency_key: idempotencyKey },
      { status: 'failed', resolved_at: new Date() },
    );
    throw err;
  }
}
```

<Note>
  A chave de idempotência persiste por 24 horas. Use sempre um UUID v4 gerado no momento da
  operação. Nunca reutilize chaves entre operações diferentes.
</Note>

## Conflito de chave

Se você enviar uma chave já usada com um body diferente, a API retorna:

```json theme={null}
{
  "error": {
    "code": "idempotency_conflict",
    "message": "An idempotency key was reused with a different request body.",
    "request_id": "uuid"
  }
}
```

Isso protege contra bugs onde a mesma chave é acidentalmente reutilizada para duas operações distintas.
