---
title: Controle de Idempotência
slug: controle-de-idempotencia
docTags: 
createdAt: 2025-09-19T18:27:10.535Z
---

O controle de **idempotência** no gateway Fluid permite que você envie a mesma requisição múltiplas vezes de forma segura, garantindo que apenas uma execução será processada mesmo em casos de falhas de rede, timeouts ou tentativas acidentais de reenvio.

## Quando usar idempotência

A idempotência é essencial em cenários onde é crítico evitar processamento duplicado:

- **Pagamentos e transações financeiras**
- **Criação de pedidos ou registros únicos**
- **Integrações com sistemas externos que podem falhar**
- **Webhooks que podem ser reenviados**

## Como funciona

### Headers de request

Para usar idempotência, inclua o header `X-Idempotency-Key` em sua requisição ao gateway:

:::BlockQuote
curl --location '\{gateway\_host}/\{path}/\{flow\_name}?key=\{api\_key}' \\
\--header 'Content-Type: application/json' \\
\--header 'X-Idempotency-Key: pagamento-usuario123-20240115' \\
\--data '\{
&#x20;   "valor": 100.00,
&#x20;   "usuario\_id": "123"
}'
:::

:::hint{type="info"}
**Formato da chave de idempotência:**

- **Tamanho**: Entre 3 e 128 caracteres
- **Caracteres permitidos**: Letras, números, hífen (-), underscore (\_) e ponto (.)
- **Recomendação**: Use um identificador único e descritivo do contexto
:::

**Exemplos válidos:**

- pagamento-123-20240115
- pedido\_usuario456\_v2
- webhook.retry.001

**Chaves inválidas retornam erro 400:**

:::BlockQuote
\# Resposta para chave inválida:
HTTP/1.1 400 Bad Request
\{
&#x20; "code": "400",
&#x20; "message": "invalid idempotency key: invalid characters"
}
:::

### Headers de response

O Gateway retorna informações sobre o status da idempotência:

:::BlockQuote
X-Idempotency-Key: pagamento-usuario123-20240115
X-Idempotency-Status: new
:::

**Status possíveis:**

- `new`: Primeira execução desta chave
- `processing`: Execução em andamento
- `duplicate`: Requisição duplicada (já processada)

:::hint{type="info"}
O controle de idempotência não verifica os detalhes da requisição (como o corpo) para identificar se já foi ou não processada. Esse controle é realizado exclusivamente pelo cabeçalho `X-Idempotency-Key` e `X-Force-Retry`.
:::

### TTL Policies

A política de tempo de vida do controle de idempotência segue a tabela abaixo:

| Status do fluxo | TTL   | Motivo                           |
| --------------- | ----- | -------------------------------- |
| Sucesso \| Info | 24h   | Sucesso - evitar reprocessamento |
| Erro \| Falha   | 5 min | Erro temporário - retry rápido   |
| Divergência     | 2h    | Erro cliente - evitar spam       |

## Comportamento por tipo de execução de fluxo

### Fluxos Assíncronos (padrão)

**Primeira requisição:**

:::BlockQuote
curl --location '\{gateway\_host}/v2/flows/processar-pagamento?key=\{api\_key}' \\
\--header 'X-Idempotency-Key: pag-123' \\
\--data '\{"valor": 100}'

\# Resposta: 200 OK
\# Headers:
\# X-Idempotency-Key: pag-123
\# X-Idempotency-Status: new

\{
&#x20; "event\_id": "evt\_abc123"
}
:::

**Requisição duplicada:**

:::BlockQuote
\# Mesma requisição enviada novamente
curl --location '\{gateway\_host}/v2/flows/processar-pagamento?key=\{api\_key}' \\
\--header 'X-Idempotency-Key: pag-123' \\
\--data '\{"valor": 100}'

\# Resposta: 200 OK (mesmo event\_id)
\# Headers:
\# X-Idempotency-Key: pag-123
\# X-Idempotency-Status: duplicate

\{
&#x20; "event\_id": "evt\_abc123"
}
:::

### Fluxos Síncronos

Para fluxos síncronos, requisições duplicadas *não retornam o resultado original*:

:::BlockQuote
curl --location '\{gateway\_host}/v2/flows/validar-dados?sync=true\&key=\{api\_key}' \\
\--header 'X-Idempotency-Key: validacao-456' \\
\--data '\{"documento": "123456789"}'
:::

**Primeira execução:**

:::BlockQuote
HTTP/1.1 200 OK
X-Idempotency-Key: validacao-456
X-Idempotency-Status: new

\{"resultado": "documento\_valido"}
:::

**Execução duplicada:**

:::BlockQuote
HTTP/1.1 200 OK
X-Idempotency-Key: validacao-456
X-Idempotency-Status: duplicate

(vazio)
:::

**Execução ainda processando:**

:::BlockQuote
HTTP/1.1 200 OK
X-Idempotency-Key: validacao-456
X-Idempotency-Status: processing

(vazio)
:::

:::hint{type="warning"}
**Limitação atual**: Para fluxos síncronos, requisições duplicadas retornam body vazio. O resultado original não é cacheado, apenas os metadados da execução.
:::

## Forçando nova execução: Force Retry

Em casos onde você deseja reprocessar algum evento que já foi executado com uma chave de idempotência, use o header `X-Force-Retry`:

:::BlockQuote
curl --location '\{gateway\_host}/v2/flows/processar-pagamento?key=\{api\_key}' \\
\--header 'X-Idempotency-Key: pag-123' \\
\--header 'X-Force-Retry: true' \\
\--data '\{"valor": 100}'
:::

## Códigos de status e erros

### Status HTTP

Todas as requisições com idempotência retornam `200 OK` quando bem-sucedidas, independentemente de ser uma execução nova ou duplicada. A diferenciação é feita através dos headers.

### Erros comuns

**Chave de idempotência inválida (400 Bad Request):**

:::BlockQuote
curl --location '\{gateway\_host}/v2/flows/test?key=\{api\_key}' \\
\--header 'X-Idempotency-Key: chave inválida!' \\
\--data '\{}'

\# Resposta:
HTTP/1.1 400 Bad Request
\{
&#x20; "code": "400",&#x20;
&#x20; "message": "invalid idempotency key: idempotency key contains invalid characters (allowed: a-z, A-Z, 0-9, ., \_, -)"
}
:::

**Chave muito pequena ou muito grande:**

:::BlockQuote
\# Chave muito pequena (\< 3 caracteres)
X-Idempotency-Key: ab

\# Chave muito grande (> 128 caracteres) &#x20;
X-Idempotency-Key: \[string com 129+ caracteres]

\# Resposta:
HTTP/1.1 400 Bad Request
\{
&#x20; "code": "400",
&#x20; "message": "invalid idempotency key: idempotency key too short (min 3 chars)"
}
:::

**Outros erros não relacionados à idempotência:**

:::BlockQuote
\# Flow não encontrado
HTTP/1.1 400 Bad Request
\{
&#x20; "code": "400",
&#x20; "message": "missing 'flow' parameter in path"
}

\# API Key inválida
HTTP/1.1 401 Unauthorized
\{
&#x20; "code": "401",&#x20;
&#x20; "message": "unauthorized"
}
:::

## Combinando com outros parâmetros

A idempotência funciona normalmente com todos os outros recursos do Gateway:

```shell
# Fluxo síncrono com idempotência e tags
curl --location '{gateway_host}/v2/flows/processar-pedido?sync=true&return_step=resposta&tags=ecommerce,urgente&key={api_key}' \
--header 'Content-Type: application/json' \
--header 'X-Idempotency-Key: pedido-789-retry-001' \
--data '{
    "produtos": [{"id": 1, "quantidade": 2}],
    "cliente_id": "user_456"
}'
```

## Boas práticas

### Gerando chaves de idempotência

**✅ Boas práticas:**

:::BlockQuote
// Incluir contexto + timestamp + identificador único
const key = \`pagamento-$\{userId}-$\{timestamp}-$\{uuid}\`;

// Para retry de webhook específico
const key = \`webhook-$\{originalEventId}-retry-$\{attemptNumber}\`;

// Baseado em dados únicos da transação
const key = \`pedido-$\{orderId}-$\{customerEmail}-$\{totalAmount}\`;
:::

**❌ Evite:**

:::BlockQuote
// Muito genérico
const key = "pagamento-123";

// Baseado apenas em timestamp (pode duplicar)
const key = Date.now().toString();

// Caracteres inválidos
const key = "pagamento user\@123!";
:::

### Tratamento de erros

:::BlockQuote
const response = await fetch('.../flows/meu-fluxo', \{
&#x20; method: 'POST',
&#x20; headers: \{
&#x20;   'Content-Type': 'application/json',
&#x20;   'X-Idempotency-Key': gerarChaveUnica(),
&#x20;   'X-Api-Key': apiKey
&#x20; },
&#x20; body: JSON.stringify(dados)
});

const idempotencyStatus = response.headers.get('X-Idempotency-Status');

if (idempotencyStatus === 'duplicate') \{
&#x20; console.log('Requisição já foi processada anteriormente');
} else if (idempotencyStatus === 'processing') \{
&#x20; console.log('Requisição ainda está sendo processada');
}
:::

### Integração com webhooks

Ao receber webhooks que podem ser reenviados, use uma chave baseada no ID único do evento:

:::BlockQuote
\# Webhook do sistema externo
curl --location '.../flows/processar-webhook?key=\{api\_key}' \\
\--header 'X-Idempotency-Key: webhook-$\{sistemaExterno}-$\{eventoId}' \\
\--data '$\{payloadWebhook}'
:::

## Limitações atuais

:::hint{type="warning"}
**Resultados síncronos**: Por ora, o sistema não cacheia o resultado de execuções síncronas. Requisições duplicadas retornam apenas metadados (event\_id, status) sem o corpo da resposta original.

**Escopo por tenant**: Chaves de idempotência são isoladas por tenant (API Key). A mesma chave pode ser usada por diferentes tenants sem conflito.
:::

## Monitoramento

Use os headers de resposta para monitorar o comportamento da idempotência:

:::BlockQuote
function logIdempotencyMetrics(response) \{
&#x20; const key = response.headers.get('X-Idempotency-Key');
&#x20; const status = response.headers.get('X-Idempotency-Status');
&#x20;&#x20;
&#x20; console.log(\`Idempotency - Key: $\{key}, Status: $\{status}\`);
&#x20;&#x20;
&#x20; // Métricas para observabilidade
&#x20; if (status === 'duplicate') \{
&#x20;   metrics.increment('gateway.idempotency.duplicate');
&#x20; } else if (status === 'new') \{
&#x20;   metrics.increment('gateway.idempotency.new');
&#x20; }
}
:::

