Buenas practicas
Recomendaciones para integrar de forma estable: reintentos seguros, control de duplicados y buen manejo del estado fiscal.
Idempotencia#
Envia siempre Idempotency-Key en la emision: una cadena ASCII estable por
intencion (por ejemplo FACTURA-2026-0001).
curl -s -X POST \
"https://staging-api.mynexusapi.com/v1/contribuyentes/0123456789/comprobantes" \
-H "X-Api-Key: $ECUAFACT_API_KEY" \
-H "Idempotency-Key: FACTURA-2026-0001" \
-H "Content-Type: application/json" \
-d @comprobante.json
using System.Text;
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
http.DefaultRequestHeaders.Add("Idempotency-Key", "FACTURA-2026-0001");
string json = await File.ReadAllTextAsync("comprobante.json");
using var contenido = new StringContent(json, Encoding.UTF8, "application/json");
using var respuesta = await http.PostAsync(
"https://staging-api.mynexusapi.com/v1/contribuyentes/0123456789/comprobantes",
contenido);
Console.WriteLine($"{(int)respuesta.StatusCode} {await respuesta.Content.ReadAsStringAsync()}");
import json, os, requests
headers = {
"X-Api-Key": os.environ["ECUAFACT_API_KEY"],
"Idempotency-Key": "FACTURA-2026-0001",
}
with open("comprobante.json", encoding="utf-8") as archivo:
cuerpo = json.load(archivo)
r = requests.post(
"https://staging-api.mynexusapi.com/v1/contribuyentes/0123456789/comprobantes",
headers=headers, json=cuerpo, timeout=30)
print(r.status_code, r.text)
import { readFile } from 'node:fs/promises';
const cuerpo = await readFile('comprobante.json', 'utf8');
const res = await fetch(
'https://staging-api.mynexusapi.com/v1/contribuyentes/0123456789/comprobantes',
{
method: 'POST',
headers: {
'X-Api-Key': process.env.ECUAFACT_API_KEY,
'Idempotency-Key': 'FACTURA-2026-0001',
'Content-Type': 'application/json'
},
body: cuerpo
});
console.log(res.status, await res.text());
<?php
$ch = curl_init('https://staging-api.mynexusapi.com/v1/contribuyentes/0123456789/comprobantes');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-Api-Key: ' . getenv('ECUAFACT_API_KEY'),
'Idempotency-Key: FACTURA-2026-0001',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => file_get_contents('comprobante.json'),
]);
$respuesta = curl_exec($ch);
$estado = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo $estado . ' ' . $respuesta;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
HttpClient cliente = HttpClient.newHttpClient();
String cuerpo = Files.readString(Path.of("comprobante.json"));
HttpRequest peticion = HttpRequest.newBuilder()
.uri(URI.create("https://staging-api.mynexusapi.com/v1/contribuyentes/0123456789/comprobantes"))
.header("X-Api-Key", System.getenv("ECUAFACT_API_KEY"))
.header("Idempotency-Key", "FACTURA-2026-0001")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(cuerpo))
.build();
HttpResponse<String> respuesta = cliente.send(peticion, HttpResponse.BodyHandlers.ofString());
System.out.println(respuesta.statusCode() + " " + respuesta.body());
using Ecuafact.Sdk;
// client: cliente EcuafactClient - ver SDK .NET
EmisionResultado resultado = await client.EmitirAsync(comprobante);
// Si el proceso reintenta, reenvia la MISMA clave.
string clave = resultado.IdempotencyKey;
from ecuafact import EcuafactClient
# client: cliente EcuafactClient - ver SDK Python
resultado = client.emitir(comprobante)
# Si el proceso reintenta, reenvia la MISMA clave.
clave = resultado.idempotency_key
import { EcuafactClient } from "@ecuafact/sdk";
// client: cliente EcuafactClient - ver SDK Node.js
const resultado = await client.emitir(comprobante);
// Si el proceso reintenta, reenvia la MISMA clave.
const clave = resultado.idempotencyKey;
<?php
use Ecuafact\Sdk\EcuafactClient;
// $client: cliente EcuafactClient - ver SDK PHP
$resultado = $client->emitir($comprobante);
// Si el proceso reintenta, reenvia la MISMA clave.
$clave = $resultado->idempotencyKey;
import com.ecuafact.sdk.EcuafactClient;
// client: cliente EcuafactClient - ver SDK Java
EmisionResultado resultado = client.emitir(comprobante);
// Si el proceso reintenta, reenvia la MISMA clave.
String clave = resultado.idempotencyKey();
- Misma clave y mismo contenido:
202concodigo201y la misma operacion; no crea un comprobante nuevo. - Misma clave con otro contenido:
409(codigo403). - Guarda la
Idempotency-Keyy lareferenciaExternaque enviaste. Si tu proceso reintenta por un timeout, reenvia las mismas para no crear un comprobante duplicado.
Errores y reintentos#
| Situacion | Accion |
|---|---|
429 (limite o cupo), 502, 503, 504 |
Reintentar con espera exponencial (con jitter) y respetar Retry-After |
400, 401, 403, 404, 409, 422 |
No reintentar sin corregir la causa |
| Timeout de red | Reintentar con la misma Idempotency-Key |
Los limites de solicitudes y el manejo del 429 se explican en
Limite de request.
- Registra el
X-Correlation-Idde cada respuesta: identifica la peticion ante soporte. - No reintentes la emision con una referencia nueva: es lo que duplica documentos.
Emision en lote y concurrencia#
- Un comprobante por intencion: no reutilices el mismo numero
(
estab/ptoEmi/secuencial) para otra emision. - Conserva fechas e importes tal como los originaste; no los reescribas en reintentos.
- No envies campos fuera de la Estructura del comprobante: los campos desconocidos se rechazan.
- Puedes emitir en paralelo con la misma API Key; manten un numero de
solicitudes moderado y respeta
429.
Seguimiento del estado#
- La emision responde
202: la solicitud quedo recibida, no autorizada. No esperes el resultado fiscal en la misma respuesta. - Consulta
GET /v1/operaciones/{id}o espera el webhook. - Evita el sondeo agresivo: usa el webhook como fuente principal y la consulta como respaldo.
Webhooks#
- Verifica siempre la firma antes de interpretar el cuerpo (Verificacion de firma).
- Responde
2xxrapido y procesa en segundo plano. - Trata los eventos como idempotentes: un mismo
resourceIdpuede reentregarse.
Cupo#
- El cupo se descuenta al resolverse la operacion, sea autorizada o rechazada.
- Revisa el saldo en
/v1/consumoantes de lotes grandes. - Sin cupo, la emision responde
429(codigo501); no insistas en bucle.
Formatos#
- Importes: numeros JSON decimales con punto, sin comillas.
- Fechas fiscales:
dd/MM/yyyy. Periodo:MM/yyyy. - Codigos e identificaciones: cadenas.
Seguridad#
- Envia
X-Api-Keysolo desde tu backend; nunca la expongas en el navegador ni en aplicaciones cliente. - Manten autorizadas solo las IP de origen que usan la API.
- No compartas tu API Key entre integraciones distintas.
Checklist antes de produccion#
-
Idempotency-Keyestable y reutilizada en los reintentos. - Reintentos con espera exponencial para
429/502/503/504. - Numero de comprobante unico por emision.
- Webhook con verificacion de firma e idempotente.
- Guarda el
X-Correlation-Idpara reportar a soporte. - Monitoreo del saldo en
/v1/consumo.