Sello by Kaltiro
Documentación API de facturación electrónica
Integra Sello by Kaltiro con el SRI de Ecuador: cabeceras, body, factura de prueba y los 6 comprobantes. Base: https://sello.kaltiro.com
Índice1. Empieza en 5 minutos
Comprobantes
1. Empieza en 5 minutos
Sigue estos 4 pasos en orden. No hace falta leer el resto todavía.
Alcance producción (GA): factura (01) IVA 0%/15%. Los tipos 03–07 están en la API; pruébalos con ks_test_ antes de volumen. Sin ICE/IRBP. El RIDE al comprador final lo genera tu sistema (XML con includeXml=1).
Paso 1 — Cuenta + email
Ve a /signup. Confirma el correo de
info@kaltiro.com. Al confirmar te mostramos una sola vez tu claveks_test_…. Guárdala.Paso 2 — Emisor real + .p12
En Emisores crea tu RUC y sube el .p12. Luego en Pruebas rápidas generas
ks_test_+ JSON. Claves también en /console/keys.Paso 3 — Un request completo (cabeceras + body)
Las líneas
-Hson cabeceras. El-des el body JSON.# MÉTODO + URL curl -sS -X POST 'https://sello.kaltiro.com/api/v1/invoices' \ # CABECERAS (headers) — no van en el JSON -H 'Authorization: Bearer ks_test_TU_CLAVE' \ -H 'Content-Type: application/json' \ # BODY (JSON) — aquí van emitterId, receptor, items… -d '{ "emitterId": "TU_EMITTER_ID", "establecimiento": "001", "puntoEmision": "001", "receptor": { "tipoDoc": "CEDULA", "numDoc": "0102030405", "razonSocial": "Cliente Demo", "email": "cliente@ejemplo.com" }, "items": [{ "codigo": "P001", "descripcion": "Servicio de demostración", "cantidad": 1, "precioUnitario": 10, "ivaCodigoPorcentaje": 4, "ivaTarifa": 15 }], "metodoPago": "transferencia" }'Paso 4 — Lee la respuesta
Código
201conid,estadoyclaveAcceso. Conks_test_no se descuenta crédito.
Siguiente: cabeceras vs body (1 minuto) → luego el comprobante que necesites.
El SRI exige RUC y .p12 reales
ks_test_ + JSON. Las pruebas no consumen créditos.2. Cabeceras vs body (léelo una vez)
Cada llamada a la API tiene 3 partes. Si mezclas la clave dentro del JSON, fallará.
Parte A
Método + URL
Ej. POST https://sello.kaltiro.com/api/v1/invoices
Parte B
Cabeceras (headers)
Van fuera del JSON. Aquí va el token.
Parte C
Body (JSON)
Emisor, receptor, ítems… Lo que cambia por comprobante.
Parte B — Cabeceras obligatorias
En Postman: pestaña Headers. En fetch/axios: objeto headers. Nunca dentro del body.
Authorization: Bearer ks_test_xxxxxxxx Content-Type: application/json # Producción (cobra crédito si autoriza): Authorization: Bearer ks_live_xxxxxxxx
Parte C — Body (ejemplo mínimo)
Esto es lo único que va en el JSON. Sin Authorization aquí.
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"receptor": { "tipoDoc": "CEDULA", "numDoc": "0102030405", "razonSocial": "Demo" },
"items": [{
"codigo": "P001",
"descripcion": "Servicio",
"cantidad": 1,
"precioUnitario": 10,
"ivaCodigoPorcentaje": 4,
"ivaTarifa": 15
}],
"metodoPago": "transferencia"
}Postman / JavaScript
| Dónde | Qué pones |
|---|---|
| Postman → Authorization | Type: Bearer Token → tu ks_test_… |
| Postman → Headers | Content-Type: application/json (suele ir solo) |
| Postman → Body → raw JSON | Solo el objeto con emitterId, receptor, items… |
| fetch / axios | headers: { Authorization, Content-Type } + body: JSON.stringify(…) |
Sin Bearer → 401
3. Conceptos clave
- Base URL
https://sello.kaltiro.com— paths/api/v1/…- Emisor
- El RUC que factura. Lo identificas con
emitterIden el body. - Punto de emisión
001-001→ body:establecimiento+puntoEmision, oemissionPointId.- Clave de acceso
- 49 dígitos del SRI. Sirve para consultar sin reemitir.
- Sandbox vs prod
- Header
ks_test_= pruebas gratis.ks_live_= prod, 1 crédito si autoriza. - precioUnitario
- Siempre base sin IVA. El IVA sale de
ivaTarifa.
4. Checklist: qué debe ir sí o sí
Si falta algo de esta lista, Sello responde 400 o el SRI rechaza el XML. Léelo antes de integrar en producción.
En tu cuenta / emisor
- Emisor con certificado .p12 y clave cargados (consola → Emisores).
- Al menos un punto de emisión (ej. 001-001).
- Clave API
ks_test_(pruebas) oks_live_(producción) + créditos si usas live. - Email de la cuenta verificado (si no, la clave no autentica).
En cada request de emisión
| Campo | Tipo | Qué va aquí |
|---|---|---|
| Authorization | CABECERA | Bearer ks_test_… o ks_live_…. Va en Headers, NO en el JSON. |
| Content-Type | CABECERA | application/json. Va en Headers. |
| emitterId | body | ID del emisor de TU cuenta. No inventes IDs de otro tenant. |
| establecimiento + puntoEmision | 3+3 dígitos | O emissionPointId. Si hay varios puntos y no mandas ninguno → 400 ambiguo. |
| receptor / proveedor / sujeto | objeto | tipoDoc + numDoc + razonSocial. CF solo en factura (tope $50). Liquidación/retención: sin CF. |
| items[] (01/03/04) | array ≥1 | codigo, descripcion, cantidad>0, precioUnitario≥0, ivaCodigoPorcentaje + ivaTarifa coherentes. |
| IVA 0↔0 o 4↔15 | pareja fija | Nunca 4 con tarifa 0 ni 0 con tarifa 15. Solo esas dos tarifas vigentes. |
| descuento | opcional | Si va, ≤ precioUnitario × cantidad (si no → 400). |
Lo que Sello pone solo (no lo mandes a pisar)
- RUC Proveedor (Anexo 26) en
infoAdicional. Lo inyectamos nosotros. Si lo mandas encamposAdicionales, se ignora. - Leyenda RIMPE si el emisor (o el body) tiene régimen RIMPE.
- Clave de acceso 49 dígitos, secuencial, firma XAdES, envío al SRI.
Por tipo de comprobante (extra)
| Campo | Tipo | Qué va aquí |
|---|---|---|
| Factura CF | 01 | numDoc fijo 9999999999999, total con IVA ≤ $50. No combinar con comercioExterior. |
| NC / ND | 04 / 05 | codDocModificado 01|03 + numDocModificado + fechaEmisionDocSustento. |
| ND impuestos | 05 | impuestos[] obligatorio. Suma baseImponible ≈ suma motivos[].valor. valor ≈ base×tarifa/100. |
| Retención docsSustento | 07 | periodoFiscal MM/AAAA. importeTotal ≥ totalSinImpuestos. IVA del sustento = 0% o 15% (o manda impuestos[]). |
| Guía | 06 | dirPartida, transportista, fechas, placa, destinatarios[].items[]. |
| Reembolso | 01 | impuestos del reembolso solo 0↔0 o 4↔15 y valor cuadrado. |
Lo que NO soportamos (aún)
Factura electrónica (codDoc 01)
Es el caso más común: vendes un producto o servicio y quieres la autorización SRI (claveAcceso + estado).
POST /api/v1/invoices
Cabecera: Authorization: Bearer ks_… + Content-Type: application/json — abajo solo el body.
Request completo (curl)
curl -sS -X POST 'https://sello.kaltiro.com/api/v1/invoices' \
-H 'Authorization: Bearer ks_test_TU_CLAVE' \
-H 'Content-Type: application/json' \
-d '{ …body de abajo… }'Body (cédula + IVA 15%)
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"receptor": {
"tipoDoc": "CEDULA",
"numDoc": "0102030405",
"razonSocial": "María Pérez",
"email": "maria@ejemplo.com",
"direccion": "Cuenca"
},
"items": [
{
"codigo": "SERV-01",
"descripcion": "Consultoría",
"cantidad": 1,
"precioUnitario": 100,
"descuento": 0,
"ivaCodigoPorcentaje": 4,
"ivaTarifa": 15
}
],
"metodoPago": "transferencia",
"externalRef": "pedido-4587",
"camposAdicionales": [
{ "nombre": "Pedido", "valor": "4587" },
{ "nombre": "Observación", "valor": "Entrega en 48h" }
]
}Qué significa cada campo
| Campo | Tipo | Qué va aquí |
|---|---|---|
| emitterId | string | ID del emisor (consola → Emisores o Sandbox). |
| establecimiento | 3 dígitos | Ej. 001. Opcional si mandas emissionPointId. |
| puntoEmision | 3 dígitos | Ej. 001. |
| receptor.tipoDoc | enum | RUC | CEDULA | PASAPORTE | CONSUMIDOR_FINAL | IDENTIFICACION_EXTERIOR |
| receptor.numDoc | string | Cédula 10 dígitos, RUC 13, etc. En CF usa 9999999999999. |
| items[].precioUnitario | number | Base sin IVA. Aquí 100 → IVA 15 = 15 → total 115. |
| items[].ivaCodigoPorcentaje | 0 | 4 | 0 = 0%, 4 = IVA 15% (tabla SRI actual). |
| items[].ivaTarifa | 0 | 15 | Debe coincidir con el código (0↔0, 4↔15). |
| metodoPago | enum | efectivo | transferencia | tarjeta |
| externalRef | string? | Tu ID interno (pedido, ticket). No va al SRI. |
| regimenTributario | enum? | Opcional: GENERAL | RIMPE_EMPRENDEDOR | RIMPE_NEGOCIO_POPULAR |
| camposAdicionales | {nombre,valor}[]? | Hasta 15 campos en infoAdicional. No pueden sobrescribir "RUC Proveedor" (lo pone Sello). |
Respuesta típica (201)
{
"id": "clx_doc_abc",
"estado": "autorizada",
"claveAcceso": "1603202601179999999900110010010000000011234567812",
"numeroAutorizacion": "1603202601179999999900110010010000000011234567812",
"tipo": "01",
"ambiente": "pruebas",
"secuencial": 1,
"externalRef": "pedido-4587",
"credited": false,
"creditsLeft": 0
}Estados posibles
autorizada · rechazada · en_proceso · error. Si queda en_proceso, no reemitas: usa el endpoint de reautorizar (más abajo). En ks_test_, credited siempre es false aunque el SRI autorice. En producción solo pasa a true si se descontó 1 crédito.Variantes de factura (mismo endpoint)
POST /api/v1/invoices
Cabecera: Authorization: Bearer ks_… + Content-Type: application/json — abajo solo el body.
Mismas cabeceras. Solo cambia el body según el caso.
Consumidor final (tope $50 con IVA)
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"receptor": {
"tipoDoc": "CONSUMIDOR_FINAL",
"numDoc": "9999999999999",
"razonSocial": "CONSUMIDOR FINAL"
},
"items": [{
"codigo": "P01",
"descripcion": "Producto",
"cantidad": 1,
"precioUnitario": 20,
"ivaCodigoPorcentaje": 4,
"ivaTarifa": 15
}],
"metodoPago": "efectivo"
}Cliente con RUC + RIMPE emprendedor
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"regimenTributario": "RIMPE_EMPRENDEDOR",
"receptor": {
"tipoDoc": "RUC",
"numDoc": "1790012345001",
"razonSocial": "Cliente SA"
},
"items": [{
"codigo": "S01",
"descripcion": "Servicio",
"cantidad": 1,
"precioUnitario": 50,
"ivaCodigoPorcentaje": 4,
"ivaTarifa": 15
}],
"metodoPago": "transferencia"
}Exportación (comercio exterior)
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"receptor": {
"tipoDoc": "IDENTIFICACION_EXTERIOR",
"numDoc": "US123456789",
"razonSocial": "Buyer LLC",
"direccion": "Miami, FL"
},
"items": [{
"codigo": "EXP1",
"descripcion": "Producto exportación",
"cantidad": 1,
"precioUnitario": 1000,
"ivaCodigoPorcentaje": 0,
"ivaTarifa": 0
}],
"metodoPago": "transferencia",
"comercioExterior": {
"incoTermFactura": "FOB",
"lugarIncoTerm": "GUAYAQUIL",
"paisOrigen": "593",
"puertoEmbarque": "GUAYAQUIL",
"puertoDestino": "MIAMI",
"paisDestino": "139",
"incoTermTotalSinImpuestos": "FOB"
}
}Factura de reembolso
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"receptor": {
"tipoDoc": "RUC",
"numDoc": "1790012345001",
"razonSocial": "Cliente Reembolso"
},
"items": [{
"codigo": "R001",
"descripcion": "Reembolso de gastos",
"cantidad": 1,
"precioUnitario": 100,
"ivaCodigoPorcentaje": 4,
"ivaTarifa": 15
}],
"metodoPago": "transferencia",
"reembolsos": [{
"tipoIdentificacionProveedor": "RUC",
"identificacionProveedor": "1790012345001",
"codPaisPago": "593",
"tipoProveedor": "02",
"codDocReembolso": "01",
"estabDocReembolso": "001",
"ptoEmiDocReembolso": "001",
"secuencialDocReembolso": "000000001",
"fechaEmisionDocReembolso": "2026-03-10",
"numeroAutorizacionDocReemb": "1234567890",
"impuestos": [{
"codigo": "2",
"codigoPorcentaje": "4",
"tarifa": 15,
"baseImponibleReembolso": 100,
"impuestoReembolso": 15
}]
}]
}Nota de crédito (codDoc 04)
Úsala para anular o devolver (total o parcial) una factura o liquidación ya emitida. Debes indicar el documento modificado.
POST /api/v1/credit-notes
Cabecera: Authorization: Bearer ks_… + Content-Type: application/json — abajo solo el body.
Body
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"receptor": {
"tipoDoc": "CEDULA",
"numDoc": "0102030405",
"razonSocial": "María Pérez"
},
"items": [{
"codigo": "P001",
"descripcion": "Devolución",
"cantidad": 1,
"precioUnitario": 10,
"ivaCodigoPorcentaje": 4,
"ivaTarifa": 15
}],
"motivo": "Devolución total",
"codDocModificado": "01",
"numDocModificado": "001-001-000000001",
"fechaEmisionDocSustento": "2026-03-15"
}numDocModificado
estab-punto-secuencial, ej. 001-001-000000001. codDocModificado: 01 factura o 03 liquidación.Nota de débito (codDoc 05)
Para cargos adicionales (intereses, recargos) sobre una factura o liquidación.
POST /api/v1/debit-notes
Cabecera: Authorization: Bearer ks_… + Content-Type: application/json — abajo solo el body.
Body
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"receptor": {
"tipoDoc": "CEDULA",
"numDoc": "0102030405",
"razonSocial": "María Pérez"
},
"motivos": [{ "razon": "Interés por mora", "valor": 5 }],
"impuestos": [{
"ivaCodigoPorcentaje": 4,
"ivaTarifa": 15,
"baseImponible": 5,
"valor": 0.75
}],
"codDocModificado": "01",
"numDocModificado": "001-001-000000001",
"fechaEmisionDocSustento": "2026-03-15"
}Cuadre obligatorio en ND
impuestos no puede ir vacío. La suma de baseImponible debe coincidir con la suma de motivos[].valor (±$0.02). Cada valor de IVA ≈ base × tarifa / 100. Parejas válidas: 0↔0 o 4↔15.Liquidación de compra (codDoc 03)
Cuando compras a un proveedor informal (sin factura). Aquí el “receptor” es el proveedor (no admite consumidor final).
POST /api/v1/purchase-liquidations
Cabecera: Authorization: Bearer ks_… + Content-Type: application/json — abajo solo el body.
Body
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"proveedor": {
"tipoDoc": "CEDULA",
"numDoc": "0102030405",
"razonSocial": "Proveedor Informal"
},
"items": [{
"codigo": "C001",
"descripcion": "Compra de insumos",
"cantidad": 1,
"precioUnitario": 20,
"ivaCodigoPorcentaje": 4,
"ivaTarifa": 15
}],
"metodoPago": "efectivo"
}Guía de remisión (codDoc 06)
Traslado de mercadería. Puedes omitir el documento sustento si aún no hay factura.
POST /api/v1/waybills
Cabecera: Authorization: Bearer ks_… + Content-Type: application/json — abajo solo el body.
Body
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"dirPartida": "Cuenca, Av. Principal 100",
"transportista": {
"tipoDoc": "RUC",
"numDoc": "1790012345001",
"razonSocial": "Transporte Demo SA"
},
"fechaIniTransporte": "2026-03-15",
"fechaFinTransporte": "2026-03-16",
"placa": "ABC1234",
"destinatarios": [{
"tipoDoc": "CEDULA",
"numDoc": "0102030405",
"razonSocial": "Destinatario Demo",
"direccion": "Quito",
"motivoTraslado": "Venta",
"items": [
{ "codigo": "P1", "descripcion": "Caja de producto", "cantidad": 2 }
]
}]
}Con factura de sustento (opcional)
codDocSustento: "01", numDocSustento, numAutDocSustento y fechaEmisionDocSustento.Retención (codDoc 07)
Cuando actúas como agente de retención. El periodoFiscal va como MM/AAAA. El sustento puede ser factura (01) o liquidación (03).
POST /api/v1/retentions
Cabecera: Authorization: Bearer ks_… + Content-Type: application/json — abajo solo el body.
Body (liquidación + renta 311 @ 3%)
{
"emitterId": "clx_tu_emisor",
"establecimiento": "001",
"puntoEmision": "001",
"sujetoRetenido": {
"tipoDoc": "CEDULA",
"numDoc": "1710034065",
"razonSocial": "Proveedor Informal"
},
"periodoFiscal": "09/2026",
"docsSustento": [{
"codDocSustento": "03",
"numDocSustento": "001-001-000000001",
"fechaEmisionDocSustento": "2026-09-19",
"numAutDocSustento": "1909202603…clave49digitos…",
"totalSinImpuestos": 10,
"importeTotal": 11.5,
"codSustento": "01",
"impuestos": [{
"codigoPorcentaje": 4,
"tarifa": 15,
"baseImponible": 10,
"valorImpuesto": 1.5
}],
"retenciones": [{
"tipo": "renta",
"codigo": "1",
"codigoRetencion": "311",
"baseImponible": 10,
"tarifa": 3,
"valorRetenido": 0.3
}]
}]
}Códigos y porcentajes (vigentes desde marzo 2026)
311 = 3% (pagos vía liquidación); 312 = 2% (bienes muebles). valorRetenido debe cuadrar con base × tarifa / 100. Los % los fija el SRI; revisa la resolución vigente antes de producción.IVA del documento sustento
importeTotal − totalSinImpuestos es exactamente IVA 15% (o 0), lo inferimos. Si no cuadra, manda impuestos[] con codigoPorcentaje / tarifa / baseImponible / valorImpuesto (solo 0↔0 o 4↔15).Qué arma Sello en el XML (no lo envías en el JSON)
numDocSustento: puedes mandar001-001-…; al XML van 15 dígitos sin guiones.parteRel=NO(siempre).tipoSujetoRetenidosolo si el sujeto esIDENTIFICACION_EXTERIOR(id 08).pagos(formaPago + total = importeTotal): lo inyectamos nosotros.- Exterior (
pagoLocExt=02): solo entonces vantipoRegi,paisEfecPago,aplicConvDobTrib,pagExtSujRetNorLeg. Con sujeto exterior ponemospagoLocExt=02por defecto.
Retención IVA + renta
retenciones con tipo: "iva" o "renta".Consultar y reintentar (sin reemitir)
Si el SRI tarda o se cae, no crees otra factura. Consulta o reautoriza. Así no quemas secuencial ni crédito.
Listar comprobantes
GET /api/v1/invoices?limit=20
Cabecera: Authorization: Bearer ks_… — este GET no lleva body.
curl -sS 'https://sello.kaltiro.com/api/v1/invoices?estado=autorizada&limit=20' \ -H 'Authorization: Bearer ks_test_TU_CLAVE'
Por ID interno
GET /api/v1/invoices/:id
Cabecera: Authorization: Bearer ks_… — este GET no lleva body.
curl -sS 'https://sello.kaltiro.com/api/v1/invoices/clx_doc_abc' \ -H 'Authorization: Bearer ks_test_TU_CLAVE'
Por clave de acceso (49 dígitos)
GET /api/v1/documents/by-clave/:clave
Cabecera: Authorization: Bearer ks_… — este GET no lleva body.
curl -sS 'https://sello.kaltiro.com/api/v1/documents/by-clave/16032026…1234567812?refresh=1' \ -H 'Authorization: Bearer ks_test_TU_CLAVE' # Query opcionales: # refresh=1 → reconsulta al SRI si está en_proceso # includeXml=1 → incluye XML en la respuesta
Reautorizar (quedó en_proceso)
POST /api/v1/documents/:id/authorize
Cabecera: Authorization: Bearer ks_… — este GET no lleva body.
curl -sS -X POST 'https://sello.kaltiro.com/api/v1/documents/clx_doc_abc/authorize' \ -H 'Authorization: Bearer ks_test_TU_CLAVE'
Cobro en reintento
autorizada (aunque sea en el reintento). Sandbox sigue gratis.Webhook y límites
Si configuras webhookUrl en tu cuenta (pídelo por WhatsApp), Sello hace POST best-effort tras emitir o reautorizar. Solo HTTPS (o localhost). Timeout 5s.
Headers:
Content-Type: application/json
X-Sello-Event: document.updated
X-Sello-Timestamp: 1726800000
X-Sello-Signature: sha256=<hmac_hex>
Body:
{
"id": "clx_doc_abc",
"tipo": "01",
"estado": "autorizada",
"claveAcceso": "…49 dígitos…",
"numeroAutorizacion": "…",
"credited": false,
"externalRef": "pedido-4587"
}Verificar la firma (HMAC)
WEBHOOK_HMAC_SECRET al configurar la URL. Calcula HMAC-SHA256(secret, timestamp + "." + rawBody) y compáralo con X-Sello-Signature (prefijo sha256=). Rechaza si el timestamp tiene más de ~5 minutos de desfase.Qué no entrega (aún) la API
claveAcceso / autorización; el XML firmado lo obtienes con ?includeXml=1 en by-clave. Entregar RIDE/XML al cliente final es responsabilidad del integrador (ver ADR 0009).Cuenta y créditos
GET /api/v1/account
Cabecera: Authorization: Bearer ks_… — este GET no lleva body.
curl -sS 'https://sello.kaltiro.com/api/v1/account' \ -H 'Authorization: Bearer ks_live_TU_CLAVE'
Te devuelve saldo, uso reciente y últimos documentos.
ks_test_nunca descuenta.ks_live_descuenta 1 crédito solo si el SRI autoriza.- Si el SRI rechaza → no hay cobro.
- Sin saldo en producción → HTTP
402. - Aviso automático por correo (desde info@kaltiro.com) cuando el saldo baja del umbral (por defecto 50).
Errores y cómo solucionarlos
Respuesta típica de error: { "error": "…", "details": { … } }. Mira details (Zod) para el campo exacto.
HTTP de Sello (antes del SRI)
| HTTP | Mensaje / causa | Qué haces |
|---|---|---|
| 401 | Sin Bearer, clave inválida/revocada, email sin verificar | Header correcto; verifica correo; nueva clave en consola |
| 400 | Payload inválido, sin .p12, punto ambiguo, reglas IVA/CF/ND | Lee details; corrige el campo (tabla abajo) |
| 402 | Sin créditos (solo producción / ks_live_) | Recarga pack (+ IVA) por WhatsApp / consola |
| 404 | Emisor o documento de otra cuenta / ID inventado | Copia IDs desde consola o Sandbox |
| 409 | Reautorizar un doc ya autorizado/rechazado | Solo en_proceso / error |
| 429 | Rate limit (~180 req/min por clave) | Backoff; no dispares bucles de reintento ciego |
| 500 | RUC Proveedor (Anexo 26) no configurado en servidor | Avisa a soporte Sello; no es un error de tu JSON |
400 de validación (los más comunes)
| Texto / síntoma | Solución |
|---|---|
| ivaCodigo… deben coincidir 0↔0 o 4↔15 | Usa 0+0 o 4+15 |
| descuento no puede superar precio×cantidad | Baja el descuento o sube el precio/cantidad |
| Consumidor final no puede superar $50 | Baja el total o usa cédula/RUC del cliente |
| CF no admite exportación | Quita comercioExterior o cambia tipoDoc |
| Suma motivos ≠ suma baseImponible (ND) | Iguala bases de impuestos a los valores de motivos |
| valor IVA ≠ base×tarifa/100 | Recalcula: base 5 y 15% → valor 0.75 |
| No se puede inferir el IVA del doc sustento | En retención manda docsSustento[].impuestos[] |
| El emisor no tiene certificado .p12 | Consola → Emisores → subir .p12 y clave |
| Punto de emisión requerido o ambiguo | Manda establecimiento+puntoEmision o emissionPointId |
Estados del comprobante (después del SRI)
| Campo | Tipo | Qué va aquí |
|---|---|---|
| autorizada | OK | Listo. En prod se descontó 1 crédito. Guarda claveAcceso y númeroAutorizacion. |
| rechazada | SRI | Mira errores[] (códigos SRI). Corrige datos y emite de nuevo (nuevo secuencial). No reuses el mismo POST “ciego”. |
| en_proceso | espera | No reemitas. POST …/authorize o by-clave?refresh=1. El worker también reintenta. |
| error | técnico | Fallo XML/firma/local. Lee errores[]; no es un rechazo fiscal del SRI. |
| creditWarning | raro | Autorizó el SRI pero falló el cobro de crédito. Avisa a soporte; no ignores el campo. |
Errores típicos del SRI (en errores[])
- [65] fecha extemporánea — fecha de emisión vs reloj Ecuador; no mandes fechas futuras.
- [39] firma inválida — .p12 o clave incorrectos / certificado vencido.
- Clave / secuencial duplicado — no reutilices el mismo intento; consulta por clave.
- Identificación comprador — cédula/RUC inválidos o CF mal armado (debe ser 9999999999999).
¿La factura quedó en_proceso?
POST …/documents/:id/authorize o by-clave?refresh=1. Nosotros también reintentamos en background.Tablas de referencia rápida
tipoDoc del receptor
| Campo | Tipo | Qué va aquí |
|---|---|---|
| RUC | 04 | 13 dígitos. Empresas / personas con RUC. |
| CEDULA | 05 | 10 dígitos. |
| PASAPORTE | 06 | Documento de viaje. |
| CONSUMIDOR_FINAL | 07 | Solo factura. Tope $50 con IVA. |
| IDENTIFICACION_EXTERIOR | 08 | Cliente / sujeto extranjero. |
IVA en ítems
| Campo | Tipo | Qué va aquí |
|---|---|---|
| ivaCodigoPorcentaje: 0 | tarifa 0 | Exento / 0%. Pareja fija: código 0 + tarifa 0. |
| ivaCodigoPorcentaje: 4 | tarifa 15 | IVA vigente 15%. Pareja fija: código 4 + tarifa 15. Mezclar → 400. |
infoAdicional automático
| Campo | Tipo | Qué va aquí |
|---|---|---|
| RUC Proveedor | Anexo 26 | Lo pone Sello (software house). No lo sobrescribas. |
| Leyenda RIMPE | si aplica | Según regimenTributario del emisor o del body. |
| email / direccion | si vienen | Del receptor; o camposAdicionales libres (hasta 15). |
Endpoints de un vistazo
| Campo | Tipo | Qué va aquí |
|---|---|---|
| POST /api/v1/invoices | 01 | Factura (+ export / reembolso) |
| POST /api/v1/purchase-liquidations | 03 | Liquidación de compra |
| POST /api/v1/credit-notes | 04 | Nota de crédito |
| POST /api/v1/debit-notes | 05 | Nota de débito |
| POST /api/v1/waybills | 06 | Guía de remisión |
| POST /api/v1/retentions | 07 | Retención |
| GET /api/v1/invoices | — | Listar |
| GET /api/v1/invoices/:id | — | Detalle |
| GET …/documents/by-clave/:clave | — | Por clave SRI |
| POST …/documents/:id/authorize | — | Reintentar autorización |
| GET /api/v1/account | — | Saldo y uso |
SDK Node — @kaltiro/sello
Cliente tipado, cero dependencias, Node ≥ 18. Ideal si tu backend ya está en TypeScript/JavaScript.
# Desde el repo k-facturador:
npm install ./sdk/node
import { createSelloClient } from '@kaltiro/sello';
const sello = createSelloClient({
apiKey: process.env.SELLO_API_KEY!, // ks_test_… o ks_live_…
});
const doc = await sello.createInvoice({
emitterId: 'clx_tu_emisor',
establecimiento: '001',
puntoEmision: '001',
receptor: {
tipoDoc: 'CEDULA',
numDoc: '0102030405',
razonSocial: 'Cliente Demo',
},
items: [{
codigo: 'P001',
descripcion: 'Servicio',
cantidad: 1,
precioUnitario: 10,
ivaCodigoPorcentaje: 4,
ivaTarifa: 15,
}],
metodoPago: 'transferencia',
});
console.log(doc.estado, doc.claveAcceso);¿Te trabaste en algún paso? Pruebas rápidas · WhatsApp · Entrar a la consola