Comprobante de retencion POST
El comprobante de retencion acredita los impuestos retenidos a un proveedor.
Ejemplo en el ambiente de pruebas. Los comprobantes no tienen validez fiscal.
POST /v1/contribuyentes/{identificacion}/comprobantes
infoTributaria.codDoc = "07"
Usa
docsSustento en lugar de detalles. La estructura del comprobante es la misma que en
Factura; cambia el contenido de info y el cuerpo de
ejemplo (comprobante.json).
Estructura del comprobante#
| Clave | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
origenReferencia |
string | Si | Sistema de origen (max. 64) |
referenciaExterna |
string | Si | Referencia estable de tu sistema (max. 128) |
infoTributaria |
object | Si | Identidad del documento |
info |
object | Si | Cabecera de la retencion |
docsSustento |
array | Si | Documentos de sustento |
infoAdicional |
array | No | Hasta 20 objetos {nombre, valor} |
infoTributaria: ruc (13), codDoc (07), estab (3), ptoEmi (3) y
secuencial (9) obligatorios. La clave de acceso la asigna la plataforma; no la envies.
Campos de info#
| Campo | Tipo | Obligatorio | Regla |
|---|---|---|---|
fechaEmision |
string | Si | dd/MM/yyyy |
tipoIdentificacionSujetoRetenido |
string | Si | Catalogos de identificacion |
identificacionSujetoRetenido |
string | Si | Documento del sujeto retenido |
razonSocialSujetoRetenido |
string | Si | Razon social |
periodoFiscal |
string | Si | MM/yyyy |
parteRel |
string | Si | SI o NO |
docsSustento#
El comprobante lleva un unico documento de sustento.
| Campo | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
codSustento |
string | Si | Codigo de sustento (el 10 se rechaza) |
codDocSustento |
string | Si | Tipo del documento de sustento (el 41 se rechaza) |
numDocSustento |
string | Si | Numero del documento, 15 digitos sin guiones |
fechaEmisionDocSustento |
string | Si | dd/MM/yyyy |
fechaRegistroContable |
string | Si | dd/MM/yyyy |
numAutDocSustento |
string | Si | Autorizacion del sustento electronico, 49 digitos |
pagoLocExt |
string | Si | Pago local 01 o al exterior |
totalSinImpuestos |
number | Si | Base total del sustento |
importeTotal |
number | Si | Total del sustento |
impuestosDocSustento |
array | Si | Impuestos del sustento (ver abajo) |
retenciones |
array | Si | Retenciones aplicadas (ver abajo) |
pagos |
array | Si | Pagos del sustento (ver abajo) |
docsSustento[].impuestosDocSustento#
| Campo | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
codImpuestoDocSustento |
string | Si | Impuesto: 2 IVA, 3 ICE |
codigoPorcentaje |
string | Si | Codigo de tarifa |
baseImponible |
number | Si | Base del impuesto |
tarifa |
number | Si | Porcentaje de la tarifa |
valorImpuesto |
number | Si | Valor del impuesto |
docsSustento[].retenciones#
| Campo | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
codigo |
string | Si | Impuesto retenido: 1 renta, 2 IVA |
codigoRetencion |
string | Si | Codigo de retencion |
baseImponible |
number | Si | Base sobre la que se retiene |
porcentajeRetener |
number | Si | Porcentaje de retencion |
valorRetenido |
number | Si | Valor retenido |
docsSustento[].pagos#
| Campo | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
formaPago |
string | Si | Codigo de forma de pago |
total |
number | Si | Monto con esta forma de pago |
plazo |
number | No | Plazo |
unidadTiempo |
string | Condicional | Obligatorio si plazo > 0 |
infoAdicional#
| Campo | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
nombre |
string | Si | Nombre del campo adicional |
valor |
string | Si | Valor del campo adicional |
Codigos de retencion en Catalogos: retenciones.
Ejemplo de solicitud#
curl -s -X POST "https://staging-api.mynexusapi.com/v1/contribuyentes/0123456789/comprobantes" \
-H "X-Api-Key: $ECUAFACT_API_KEY" \
-H "Idempotency-Key: RET-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", "RET-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": "RET-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': 'RET-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: RET-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", "RET-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);
Console.WriteLine($"Operacion {resultado.Admission.IdOperacion} / codigo: {resultado.Admission.Codigo}");
from ecuafact import EcuafactClient
# client: cliente EcuafactClient - ver SDK Python
resultado = client.emitir(comprobante)
print(f"Operacion {resultado.admission.id_operacion} / codigo: {resultado.admission.codigo}")
import { EcuafactClient } from "@ecuafact/sdk";
// client: cliente EcuafactClient - ver SDK Node.js
const resultado = await client.emitir(comprobante);
console.log(`Operacion ${resultado.admission.idOperacion} / codigo: ${resultado.admission.codigo}`);
<?php
use Ecuafact\Sdk\EcuafactClient;
// $client: cliente EcuafactClient - ver SDK PHP
$resultado = $client->emitir($comprobante);
echo 'Operacion ' . $resultado->admission->idOperacion . ' / codigo: ' . $resultado->admission->codigo;
import com.ecuafact.sdk.EcuafactClient;
// client: cliente EcuafactClient - ver SDK Java
EmisionResultado resultado = client.emitir(comprobante);
System.out.println("Operacion " + resultado.admission().idOperacion() + " / codigo: " + resultado.admission().codigo());
Cuerpo del Comprobante de retencion#
{
"origenReferencia": "MiERP",
"referenciaExterna": "RET-2026-0001",
"infoTributaria": {
"ruc": "0123456789001",
"codDoc": "07",
"estab": "002",
"ptoEmi": "001",
"secuencial": "000000001"
},
"info": {
"fechaEmision": "07/09/2026",
"tipoIdentificacionSujetoRetenido": "04",
"identificacionSujetoRetenido": "1790012345001",
"razonSocialSujetoRetenido": "PROVEEDOR DE EJEMPLO",
"periodoFiscal": "09/2026",
"parteRel": "NO"
},
"docsSustento": [
{
"codSustento": "01",
"codDocSustento": "01",
"numDocSustento": "002001000000123",
"fechaEmisionDocSustento": "01/09/2026",
"fechaRegistroContable": "01/09/2026",
"numAutDocSustento": "0709202601179212345600110010020000001231234567818",
"pagoLocExt": "01",
"totalSinImpuestos": 100,
"importeTotal": 115,
"impuestosDocSustento": [
{ "codImpuestoDocSustento": "2", "codigoPorcentaje": "4", "baseImponible": 100, "tarifa": 15, "valorImpuesto": 15 }
],
"retenciones": [
{ "codigo": "1", "codigoRetencion": "303", "baseImponible": 100, "porcentajeRetener": 1, "valorRetenido": 1 }
],
"pagos": [
{ "formaPago": "01", "total": 115 }
]
}
]
}
Reglas especificas#
numDocSustentode 15 digitos sin guiones.numAutDocSustentoes el sustento electronico de 49 digitos.- Se rechazan
codSustento10ycodDocSustento41. - Los
codSustentocondicionales (banano) no son validos.
Respuesta#
La API responde 202 Accepted con la operacion creada:
{
"codigo": "200",
"mensaje": "Operacion exitosa.",
"idOperacion": "3f1c8c1e-6f2a-4b7e-9c3d-2a1b0c9d8e7f",
"urlEstado": "https://staging-api.mynexusapi.com/v1/operaciones/3f1c8c1e-6f2a-4b7e-9c3d-2a1b0c9d8e7f",
"uid": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}
| Campo | Significado |
|---|---|
codigo |
200 si es una emision nueva; 201 si es un reintento con la misma Idempotency-Key |
mensaje |
Descripcion del resultado |
idOperacion |
Identificador de seguimiento de la emision |
urlEstado |
Ruta para consultar el estado de esta operacion |
uid |
Identificador del documento |
El 202 no es la autorizacion del SRI: el comprobante quedo recibido. Consulta urlEstado o espera el webhook.
Estados de este endpoint#
| Codigo | HTTP | Cuando ocurre | Que hacer |
|---|---|---|---|
200 |
202 | Emision aceptada | Sigue el estado de la operacion |
201 |
202 | Reintento con la misma Idempotency-Key y el mismo contenido |
Sigue la misma operacion |
301 |
400 | Falta Idempotency-Key o hay campos mal formados |
Corrige la solicitud |
302 |
422 | El comprobante no cumple una regla fiscal o de estructura | Revisa errores |
401 |
409 | Numero o clave ya registrados (duplicado) | Consulta la operacion existente |
403 |
409 | Misma Idempotency-Key con otro contenido |
Usa la misma intencion |
501 |
429 | Cupo agotado | Amplia tu cupo |
101 |
401 | API Key invalida | Revisa la API Key |
103 |
403 | Contribuyente o tipo no habilitado | Habilita el contribuyente |
El catalogo completo esta en Errores.