Sello

by Kaltiro

Sello by Kaltiro

Documentación API

Guía práctica, con ejemplos copiables. Empieza por el sandbox; después copia el JSON del comprobante que necesites. Base: https://sello.kaltiro.com

Índice de la guía

Empieza en 5 minutos

No necesitas leer toda la API de golpe. Haz esto y ya puedes emitir tu primera factura de pruebas:

  1. 1. Crea tu cuenta y confirma el email

    Ve a /signup. Te llega un correo de info@kaltiro.com. Al confirmar, te mostramos una sola vez tu token ks_test_…. Sin Bearer la API responde 401.

  2. 2. (Opcional) Prepara emisor demo

    En Sandbox 1 clic generas emisor demo + JSON listo. También puedes crear/revocar claves en Claves API.

  3. 3. Llama con el token

    curl -sS -X POST 'https://sello.kaltiro.com/api/v1/invoices' \
      -H 'Authorization: Bearer ks_test_TU_CLAVE' \
      -H 'Content-Type: application/json' \
      -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. 4. Lee la respuesta

    Si todo va bien recibes 201 con id, estado y claveAcceso. Con ks_test_ no se descuenta crédito.

Sin certificado .p12 todavía

El sandbox te deja integrar igual (Postman, SDK, payloads). Para que el SRI de pruebas autorice de verdad, carga el .p12 del emisor (por WhatsApp o consola).

Conceptos clave (léelos una vez)

Base URL
https://sello.kaltiro.com — todos los paths empiezan con /api/v1/…
Emisor
El RUC que factura. Lo identificas con emitterId. Una cuenta puede tener varios emisores (multi-RUC).
Punto de emisión
El 001-001 (establecimiento + punto). Puedes mandar establecimiento + puntoEmision, o emissionPointId.
Clave de acceso
49 dígitos que genera el SRI. Es el ID fiscal del comprobante. Sirve para consultar sin reemitir.
Sandbox vs prod
ks_test_ → ambiente pruebas SRI, gratis. ks_live_ → producción, 1 crédito solo si autoriza.
precioUnitario
Siempre es la base sin IVA. El IVA lo calculamos con ivaTarifa.

Autenticación

En cada request manda el header Bearer. Sin eso → 401.

Authorization: Bearer ks_test_xxxxxxxx
# o en producción:
Authorization: Bearer ks_live_xxxxxxxx

El token es obligatorio

Cada request a /api/v1/* lleva Authorization: Bearer ks_…. Sin él → 401. Hay límite de ~180 req/min por clave (429 si te pasas). Emisores y documentos de otras cuentas no son visibles (aislamiento por accountId).

Factura electrónica (codDoc 01)

POST /api/v1/invoices

Es el caso más común: vendes un producto o servicio y quieres el RIDE autorizado.

Body de ejemplo (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"
}

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

Respuesta típica (201)

{
  "id": "clx_doc_abc",
  "estado": "autorizada",
  "claveAcceso": "1603202601179999999900110010010000000011234567812",
  "numeroAutorizacion": "1603202601179999999900110010010000000011234567812",
  "tipo": "01",
  "ambiente": "pruebas",
  "secuencial": 1,
  "externalRef": "pedido-4587"
}

Estados posibles

autorizada · rechazada · en_proceso · error. Si queda en_proceso, no reemitas: usa el endpoint de reautorizar (más abajo).

Variantes de factura (mismos endpoint)

Todas van a POST /api/v1/invoices. Solo cambia el JSON.

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)

POST /api/v1/credit-notes

Úsala para anular o devolver (total o parcial) una factura o liquidación ya emitida. Debes indicar el documento modificado.

{
  "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)

POST /api/v1/debit-notes

Para cargos adicionales (intereses, recargos) sobre una factura o liquidación.

{
  "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"
}

Liquidación de compra (codDoc 03)

POST /api/v1/purchase-liquidations

Cuando compras a un proveedor informal (sin factura). Aquí el “receptor” es el proveedor (no admite consumidor final).

{
  "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)

POST /api/v1/waybills

Traslado de mercadería. Puedes omitir el documento sustento si aún no hay factura.

{
  "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)

POST /api/v1/retentions

Cuando actúas como agente de retención. El periodoFiscal va como MM/AAAA.

{
  "emitterId": "clx_tu_emisor",
  "establecimiento": "001",
  "puntoEmision": "001",
  "sujetoRetenido": {
    "tipoDoc": "RUC",
    "numDoc": "1790012345001",
    "razonSocial": "Proveedor SA"
  },
  "periodoFiscal": "03/2026",
  "docsSustento": [{
    "codDocSustento": "01",
    "numDocSustento": "001-001-000000001",
    "fechaEmisionDocSustento": "2026-03-15",
    "numAutDocSustento": "1234567890",
    "totalSinImpuestos": 100,
    "importeTotal": 115,
    "retenciones": [{
      "tipo": "renta",
      "codigo": "1",
      "codigoRetencion": "303",
      "baseImponible": 100,
      "tarifa": 8,
      "valorRetenido": 8
    }]
  }]
}

Retención IVA + renta

Puedes poner varios objetos en retenciones con tipo: "iva" o "renta". Para sujeto del exterior usa IDENTIFICACION_EXTERIOR (nosotros ponemos pagoLocExt=02 por defecto).

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

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

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

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

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.

Cuenta y créditos

GET /api/v1/account

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 frecuentes (y cómo salir)

HTTPQué pasóQué haces
401Clave mal escrita, revocada o sin BearerRevisa el header; regenera en Sandbox 1 clic
400JSON inválido o falta .p12Mira details del body; carga certificado
402Sin créditos (solo ks_live_)Recarga pack en consola / WhatsApp
404emitterId o documento inexistenteCopia el ID desde la consola
429Demasiados requests (login/signup/API)Espera unos segundos; API ~180 req/min por clave
409Reautorizar un doc ya cerradoSolo aplica a en_proceso / error

¿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%.
ivaCodigoPorcentaje: 4tarifa 15IVA vigente 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/invoicesListar
GET /api/v1/invoices/:idDetalle
GET …/documents/by-clave/:clavePor clave SRI
POST …/documents/:id/authorizeReintentar autorización
GET /api/v1/accountSaldo 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? Sandbox 1 clic · WhatsApp · Entrar a la consola