Factura POST

La factura usa la misma estructura que los demas comprobantes; solo cambian los campos de info y las colecciones.

Ejemplo en el ambiente de pruebas. Los comprobantes no tienen validez fiscal.

POST /v1/contribuyentes/{identificacion}/comprobantes
infoTributaria.codDoc = "01"

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 (ver tabla)
info object Si Cabecera de la factura (ver tabla)
detalles array Si Lineas de la factura
infoAdicional array No Hasta 20 objetos {nombre, valor}

infoTributaria#

Campo Tipo Obligatorio Regla
ruc string Si 13 digitos del emisor
codDoc string Si 01
estab string Si 3 digitos
ptoEmi string Si 3 digitos
secuencial string Si 9 digitos

La clave de acceso la asigna la plataforma; no la envies.

Campos de info#

Campo Tipo Obligatorio Regla
fechaEmision string Si dd/MM/yyyy
tipoIdentificacionComprador string Si Catalogos: identificacion
identificacionComprador string Si Documento del comprador
razonSocialComprador string Si Razon social o nombres
dirEstablecimiento string No Direccion del establecimiento
direccionComprador string No Direccion del comprador
totalSinImpuestos number Si Suma de bases
totalDescuento number Si Suma de descuentos de linea
totalConImpuestos array Si codigo, codigoPorcentaje, baseImponible, valor (tarifa opcional)
propina number No Importe de propina
importeTotal number Si Total a pagar
moneda string Si Codigo de moneda (por ejemplo DOLAR)
pagos array Si formaPago y total obligatorios

detalles#

Lineas de la factura.

Campo Tipo Obligatorio Descripcion
codigoPrincipal string Si Codigo del bien o servicio
codigoAuxiliar string No Codigo auxiliar del emisor
descripcion string Si Descripcion de la linea
unidadMedida string No Unidad de medida
cantidad number Si Cantidad (hasta 6 decimales)
precioUnitario number Si Precio unitario (hasta 6 decimales)
descuento number Si Descuento de la linea (importe, no porcentaje)
precioTotalSinImpuesto number Si cantidad x precioUnitario - descuento
impuestos array Si Impuestos de la linea (ver abajo)
detallesAdicionales array No Hasta 3 objetos {nombre, valor}

detalles[].impuestos#

Campo Tipo Obligatorio Descripcion
codigo string Si Impuesto: 2 IVA, 3 ICE, 5 IRBPNR
codigoPorcentaje string Si Codigo de tarifa (ve Catalogos)
tarifa number Si Porcentaje de la tarifa
baseImponible number Si Base sobre la que se calcula
valor number Si Valor del impuesto

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

Un IVA explicito y, opcionalmente, un ICE porcentual. La tarifa de totalConImpuestos, si llega, debe coincidir con la del detalle.

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: 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);
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 de la Factura#

{
  "origenReferencia": "MiERP",
  "referenciaExterna": "FACTURA-2026-0001",
  "infoTributaria": {
    "ruc": "0123456789001",
    "codDoc": "01",
    "estab": "002",
    "ptoEmi": "001",
    "secuencial": "000000123"
  },
  "info": {
    "fechaEmision": "07/09/2026",
    "tipoIdentificacionComprador": "05",
    "identificacionComprador": "0123456789",
    "razonSocialComprador": "CONTRIBUYENTE DE EJEMPLO",
    "dirEstablecimiento": "AV. PRINCIPAL 123",
    "direccionComprador": "CALLE 1 NORTE",
    "totalSinImpuestos": 100,
    "totalDescuento": 0,
    "totalConImpuestos": [
      { "codigo": "2", "codigoPorcentaje": "4", "baseImponible": 100, "valor": 15, "tarifa": 15 }
    ],
    "propina": 0,
    "importeTotal": 115,
    "moneda": "DOLAR",
    "pagos": [
      { "formaPago": "01", "total": 115 }
    ]
  },
  "detalles": [
    {
      "codigoPrincipal": "SERV-001",
      "descripcion": "Servicio de ejemplo",
      "cantidad": 1,
      "precioUnitario": 100,
      "descuento": 0,
      "precioTotalSinImpuesto": 100,
      "impuestos": [
        { "codigo": "2", "codigoPorcentaje": "4", "tarifa": 15, "baseImponible": 100, "valor": 15 }
      ]
    }
  ],
  "infoAdicional": [
    { "nombre": "Orden", "valor": "OC-1001" }
  ]
}

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.

Siguientes pasos#

No se pudo completar la operacion. Recargar ✕