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. 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 tokenks_test_…. Sin Bearer la API responde 401.2. (Opcional) Prepara emisor demo
En Sandbox 1 clic generas emisor demo + JSON listo. También puedes crear/revocar claves en Claves API.
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. Lee la respuesta
Si todo va bien recibes
201conid,estadoyclaveAcceso. Conks_test_no se descuenta crédito.
Sin certificado .p12 todavía
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 mandarestablecimiento+puntoEmision, oemissionPointId. - 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
/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
| 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 |
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
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)
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
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
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)
| HTTP | Qué pasó | Qué haces |
|---|---|---|
| 401 | Clave mal escrita, revocada o sin Bearer | Revisa el header; regenera en Sandbox 1 clic |
| 400 | JSON inválido o falta .p12 | Mira details del body; carga certificado |
| 402 | Sin créditos (solo ks_live_) | Recarga pack en consola / WhatsApp |
| 404 | emitterId o documento inexistente | Copia el ID desde la consola |
| 429 | Demasiados requests (login/signup/API) | Espera unos segundos; API ~180 req/min por clave |
| 409 | Reautorizar un doc ya cerrado | Solo aplica a en_proceso / error |
¿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%. |
| ivaCodigoPorcentaje: 4 | tarifa 15 | IVA vigente 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? Sandbox 1 clic · WhatsApp · Entrar a la consola