La clave de idempotencia

Cada CFE lleva un clientEmissionId (obligatorio) elegido por el integrador. El gateway tiene un índice único sobre:
Verificado en código: es un índice único de la base, no una comprobación que pueda perderse en una condición de carrera.

Qué devuelve un reintento con el mismo clientEmissionId

Verificado en código: el gateway detecta la colisión, no emite un segundo CFE, busca el CFE original por company + branchOffice + cfeType + clientEmissionId y lo devuelve. El objeto de error trae code: "DUPLICATED_KEY" y un campo firstCfeResponse con los datos del comprobante original:
Es decir: un retry con el mismo id es seguro y te devuelve los identificadores del CFE original (serie, número, CAE, hash, QR) - podés persistirlos igual que en el éxito. firstCfeResponse trae dos campos que el éxito no trae: bulkCfesManager, que identifica el envío masivo si el CFE original vino por lote y es null si no, y TmstFirma, la marca de tiempo de la firma.

Reglas para el integrador

  1. Reintentar con el mismo clientEmissionId. Ante timeout o error de red, reenviar con el mismo id: no se crea un segundo comprobante y recibís el original en firstCfeResponse.
  2. Nunca reintentar con un id nuevo. Un clientEmissionId distinto es una emisión nueva: genera un segundo CFE fiscalmente válido (duplicado real ante DGI).
  3. El id es único por empresa + sucursal + tipo de CFE; el mismo id con otro cfeType es otro comprobante.

La excepción: CFE recibido sin CAE disponible

Hay un caso donde el mismo clientEmissionId sí se puede volver a usar para emitir de verdad, y conviene conocerlo porque contradice la regla de arriba. Si el CFE llega y la empresa no tiene un CAE disponible para ese tipo, el gateway no lo emite: lo guarda marcado como DELETED_MISSING_CAE y le renombra el id a <tuId>-DELETED-MISSING-CAE-<n>. Eso libera tu id del índice único. Consecuencia práctica: cuando se cargue el CAE que faltaba, reenviar el mismo clientEmissionId emite el comprobante, no devuelve un DUPLICATED_KEY. Es el comportamiento deseado, y es la razón por la que un DGI_MISSING_CAE no es un error terminal: se resuelve cargando el CAE y reintentando igual que un timeout. Lo que no cambia: si el CFE sí se emitió, el id queda tomado para siempre.
Verificado en el código del gateway: el índice único, el manejo del DUPLICATED_KEY, la construcción de firstCfeResponse y el renombrado por CAE faltante. No se reprodujo un retry en vivo porque requiere una emisión real.