Sello by KaltiroSelloby Kaltiro

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

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).

  1. Paso 1 — Cuenta + email

    Ve a /signup. Confirma el correo de info@kaltiro.com. Al confirmar te mostramos una sola vez tu clave ks_test_…. Guárdala.

  2. 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.

  3. Paso 3 — Un request completo (cabeceras + body)

    Las líneas -H son cabeceras. El -d es 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"
      }'
  4. Paso 4 — Lee la respuesta

    Código 201 con id, estado y claveAcceso. Con ks_test_ no se descuenta crédito.

Siguiente: cabeceras vs body (1 minuto) → luego el comprobante que necesites.

El SRI exige RUC y .p12 reales

No hay emisor demo: el SRI de pruebas rechaza RUCs inventados. Sube tu certificado en Consola → Emisores y usa Pruebas rápidas para generar 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óndeQué pones
Postman → AuthorizationType: Bearer Token → tu ks_test_…
Postman → HeadersContent-Type: application/json (suele ir solo)
Postman → Body → raw JSONSolo el objeto con emitterId, receptor, items…
fetch / axiosheaders: { Authorization, Content-Type } + body: JSON.stringify(…)

Sin Bearer → 401

Límite ~180 req/min por clave (429). Documentos de otras cuentas no existen para ti (404). La clave no va en la URL ni en el body.

3. Conceptos clave

Base URL
https://sello.kaltiro.com — paths /api/v1/…
Emisor
El RUC que factura. Lo identificas con emitterId en el body.
Punto de emisión
001-001 → body: establecimiento + puntoEmision, o emissionPointId.
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) o ks_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

CampoTipoQué va aquí
AuthorizationCABECERABearer ks_test_… o ks_live_…. Va en Headers, NO en el JSON.
Content-TypeCABECERAapplication/json. Va en Headers.
emitterIdbodyID del emisor de TU cuenta. No inventes IDs de otro tenant.
establecimiento + puntoEmision3+3 dígitosO emissionPointId. Si hay varios puntos y no mandas ninguno → 400 ambiguo.
receptor / proveedor / sujetoobjetotipoDoc + numDoc + razonSocial. CF solo en factura (tope $50). Liquidación/retención: sin CF.
items[] (01/03/04)array ≥1codigo, descripcion, cantidad>0, precioUnitario≥0, ivaCodigoPorcentaje + ivaTarifa coherentes.
IVA 0↔0 o 4↔15pareja fijaNunca 4 con tarifa 0 ni 0 con tarifa 15. Solo esas dos tarifas vigentes.
descuentoopcionalSi 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 en camposAdicionales, 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)

CampoTipoQué va aquí
Factura CF01numDoc fijo 9999999999999, total con IVA ≤ $50. No combinar con comercioExterior.
NC / ND04 / 05codDocModificado 01|03 + numDocModificado + fechaEmisionDocSustento.
ND impuestos05impuestos[] obligatorio. Suma baseImponible ≈ suma motivos[].valor. valor ≈ base×tarifa/100.
Retención docsSustento07periodoFiscal MM/AAAA. importeTotal ≥ totalSinImpuestos. IVA del sustento = 0% o 15% (o manda impuestos[]).
Guía06dirPartida, transportista, fechas, placa, destinatarios[].items[].
Reembolso01impuestos del reembolso solo 0↔0 o 4↔15 y valor cuadrado.

Lo que NO soportamos (aún)

ICE, IRBPNR y propina > 0. No mandes esos casos: el XML no los modela. Solo IVA 0% / 15%.

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

CampoTipoQué va aquí
emitterIdstringID del emisor (consola → Emisores o Sandbox).
establecimiento3 dígitosEj. 001. Opcional si mandas emissionPointId.
puntoEmision3 dígitosEj. 001.
receptor.tipoDocenumRUC | CEDULA | PASAPORTE | CONSUMIDOR_FINAL | IDENTIFICACION_EXTERIOR
receptor.numDocstringCédula 10 dígitos, RUC 13, etc. En CF usa 9999999999999.
items[].precioUnitarionumberBase sin IVA. Aquí 100 → IVA 15 = 15 → total 115.
items[].ivaCodigoPorcentaje0 | 40 = 0%, 4 = IVA 15% (tabla SRI actual).
items[].ivaTarifa0 | 15Debe coincidir con el código (0↔0, 4↔15).
metodoPagoenumefectivo | transferencia | tarjeta
externalRefstring?Tu ID interno (pedido, ticket). No va al SRI.
regimenTributarioenum?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

Formato 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)

Dentro de cada destinatario puedes añadir 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)

Ejemplos validados en SRI pruebas: 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

Si 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 mandar 001-001-…; al XML van 15 dígitos sin guiones.
  • parteRel = NO (siempre). tipoSujetoRetenido solo si el sujeto es IDENTIFICACION_EXTERIOR (id 08).
  • pagos (formaPago + total = importeTotal): lo inyectamos nosotros.
  • Exterior (pagoLocExt=02): solo entonces van tipoRegi, paisEfecPago, aplicConvDobTrib, pagExtSujRetNorLeg. Con sujeto exterior ponemos pagoLocExt=02 por defecto.

Retención IVA + renta

Puedes poner varios objetos en 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

En producción, el crédito se descuenta cuando el documento pasa a 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)

Te damos el secreto 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

No hay endpoint de PDF RIDE ni email automático al comprador. Tú guardas 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)

HTTPMensaje / causaQué haces
401Sin Bearer, clave inválida/revocada, email sin verificarHeader correcto; verifica correo; nueva clave en consola
400Payload inválido, sin .p12, punto ambiguo, reglas IVA/CF/NDLee details; corrige el campo (tabla abajo)
402Sin créditos (solo producción / ks_live_)Recarga pack (+ IVA) por WhatsApp / consola
404Emisor o documento de otra cuenta / ID inventadoCopia IDs desde consola o Sandbox
409Reautorizar un doc ya autorizado/rechazadoSolo en_proceso / error
429Rate limit (~180 req/min por clave)Backoff; no dispares bucles de reintento ciego
500RUC Proveedor (Anexo 26) no configurado en servidorAvisa a soporte Sello; no es un error de tu JSON

400 de validación (los más comunes)

Texto / síntomaSolución
ivaCodigo… deben coincidir 0↔0 o 4↔15Usa 0+0 o 4+15
descuento no puede superar precio×cantidadBaja el descuento o sube el precio/cantidad
Consumidor final no puede superar $50Baja el total o usa cédula/RUC del cliente
CF no admite exportaciónQuita comercioExterior o cambia tipoDoc
Suma motivos ≠ suma baseImponible (ND)Iguala bases de impuestos a los valores de motivos
valor IVA ≠ base×tarifa/100Recalcula: base 5 y 15% → valor 0.75
No se puede inferir el IVA del doc sustentoEn retención manda docsSustento[].impuestos[]
El emisor no tiene certificado .p12Consola → Emisores → subir .p12 y clave
Punto de emisión requerido o ambiguoManda establecimiento+puntoEmision o emissionPointId

Estados del comprobante (después del SRI)

CampoTipoQué va aquí
autorizadaOKListo. En prod se descontó 1 crédito. Guarda claveAcceso y númeroAutorizacion.
rechazadaSRIMira errores[] (códigos SRI). Corrige datos y emite de nuevo (nuevo secuencial). No reuses el mismo POST “ciego”.
en_procesoesperaNo reemitas. POST …/authorize o by-clave?refresh=1. El worker también reintenta.
errortécnicoFallo XML/firma/local. Lee errores[]; no es un rechazo fiscal del SRI.
creditWarningraroAutorizó 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?

No vuelvas a hacer POST al mismo comprobante. Usa POST …/documents/:id/authorize o by-clave?refresh=1. Nosotros también reintentamos en background.

Tablas de referencia rápida

tipoDoc del receptor

CampoTipoQué va aquí
RUC0413 dígitos. Empresas / personas con RUC.
CEDULA0510 dígitos.
PASAPORTE06Documento de viaje.
CONSUMIDOR_FINAL07Solo factura. Tope $50 con IVA.
IDENTIFICACION_EXTERIOR08Cliente / sujeto extranjero.

IVA en ítems

CampoTipoQué va aquí
ivaCodigoPorcentaje: 0tarifa 0Exento / 0%. Pareja fija: código 0 + tarifa 0.
ivaCodigoPorcentaje: 4tarifa 15IVA vigente 15%. Pareja fija: código 4 + tarifa 15. Mezclar → 400.

infoAdicional automático

CampoTipoQué va aquí
RUC ProveedorAnexo 26Lo pone Sello (software house). No lo sobrescribas.
Leyenda RIMPEsi aplicaSegún regimenTributario del emisor o del body.
email / direccionsi vienenDel receptor; o camposAdicionales libres (hasta 15).

Endpoints de un vistazo

CampoTipoQué va aquí
POST /api/v1/invoices01Factura (+ export / reembolso)
POST /api/v1/purchase-liquidations03Liquidación de compra
POST /api/v1/credit-notes04Nota de crédito
POST /api/v1/debit-notes05Nota de débito
POST /api/v1/waybills06Guía de remisión
POST /api/v1/retentions07Retenció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

Documentación API facturación electrónica SRI | Sello by Kaltiro