Docs / SDKs oficiales
Empezar

SDKs oficiales

Emitoo tiene SDKs oficiales para tu lenguaje — Python, Node/TypeScript y Go — construidos y verificados contra la misma API: mismos endpoints, misma semántica de reintentos, idempotencia y errores. Para cualquier otro lenguaje, la API es REST + JSON y publicamos su especificación OpenAPI 3.1 para generar un cliente tipado.

Todos son capas finas sobre el cliente HTTP nativo de cada lenguaje: los métodos aceptan y devuelven diccionarios/objetos planos (sin modelos generados por schema). Todos leen la API key de la variable de entorno EMITOO_API_KEY (y la URL base de EMITOO_BASE_URL) si no las pasas explícitas, y envían el header X-API-Key por ti.

Aún no publicados en los registries

Los paquetes todavía no están disponibles en PyPI, npm ni como módulo de Go versionado: los comandos de instalación de abajo funcionarán en cuanto los publiquemos («próximamente»).

SDK de Python

Cliente síncrono sobre httpx (única dependencia en runtime). Requiere Python ≥ 3.10.

Instalación

pip install emitoo  # próximamente en PyPI

Quickstart

from emitoo import EmitooClient

client = EmitooClient(api_key="fpk_...")  # o export EMITOO_API_KEY=fpk_...

# 1. Emitir una factura. Con idempotency_key, reintentar el mismo POST
#    devuelve la MISMA factura (no la duplica).
factura = client.facturas.crear(
    {
        "tipo_identificacion": "04",  # 04 = RUC, 05 = cédula
        "identificacion": "0991234567001",
        "razon_social": "ACME S.A.",
        "email": "facturacion@acme.example",
        "detalles": [
            {"codigo_principal": "LIC-1", "descripcion": "Licencia mensual",
             "cantidad": 1, "precio_unitario": 75.0, "tarifa_iva": 15},
        ],
    },
    idempotency_key="orden-8912",
)
print(factura["id"], factura["estado"])  # 177 "PENDIENTE"

# 2. El envío al SRI es asíncrono: espera a un estado final
#    (lanza TimeoutError si se agota el tiempo).
factura = client.facturas.esperar_autorizacion(factura["id"], timeout=60.0)
print(factura["estado"])  # "AUTORIZADO"

# 3. Descargar el RIDE (PDF) — devuelve bytes.
pdf = client.facturas.ride(factura["id"])

client.close()

También funciona como context manager (cierra la conexión HTTP al salir):

with EmitooClient(api_key="fpk_...") as client:
    facturas = client.facturas.listar()

Manejo de errores

from emitoo import EmitooError, ValidationError, RateLimitError

try:
    client.facturas.crear(payload, idempotency_key="orden-8912")
except ValidationError as exc:
    print(exc.status_code, exc.errors)   # 422, detail de FastAPI/Pydantic
except RateLimitError as exc:
    print("reintenta en", exc.retry_after, "segundos")
except EmitooError as exc:
    print("error de Emitoo:", exc.status_code, exc.message)

SDK de Node/TypeScript

Cliente sobre el fetch global — cero dependencias en runtime. Requiere Node ≥ 18. Se publica como ESM + CJS, con tipos incluidos.

Instalación

npm install emitoo  # próximamente en npm

Quickstart

import { Emitoo } from "emitoo";

const client = new Emitoo({ apiKey: "fpk_..." }); // o export EMITOO_API_KEY=fpk_...

// 1. Emitir una factura. Con idempotencyKey, reintentar el mismo POST
//    devuelve la MISMA factura (no la duplica).
const factura = await client.facturas.crear(
  {
    tipo_identificacion: "04", // 04 = RUC, 05 = cédula
    identificacion: "0991234567001",
    razon_social: "ACME S.A.",
    email: "facturacion@acme.example",
    detalles: [
      { codigo_principal: "LIC-1", descripcion: "Licencia mensual",
        cantidad: 1, precio_unitario: 75.0, tarifa_iva: 15 },
    ],
  },
  { idempotencyKey: "orden-8912" },
);
console.log(factura.id, factura.estado); // 177 "PENDIENTE"

// 2. El envío al SRI es asíncrono: espera a un estado final
//    (lanza TimeoutError si se agota el tiempo).
const autorizada = await client.facturas.esperarAutorizacion(factura.id, {
  timeoutMs: 60_000,
});
console.log(autorizada.estado); // "AUTORIZADO"

// 3. Descargar el RIDE (PDF) — devuelve Uint8Array.
const pdf = await client.facturas.ride(factura.id);

Manejo de errores

import { EmitooError, ValidationError, RateLimitError } from "emitoo";

try {
  await client.facturas.crear(payload, { idempotencyKey: "orden-8912" });
} catch (exc) {
  if (exc instanceof ValidationError) {
    console.log(exc.status, exc.errors); // 422, detail de FastAPI/Pydantic
  } else if (exc instanceof RateLimitError) {
    console.log("reintenta en", exc.retryAfter, "segundos");
  } else if (exc instanceof EmitooError) {
    console.log("error de Emitoo:", exc.status, exc.message);
  } else {
    throw exc;
  }
}

SDK de Go

Cliente sobre net/httpcero dependencias fuera de la librería estándar. Requiere Go ≥ 1.22. Todos los métodos reciben context.Context y el cliente es seguro para uso concurrente entre goroutines (no necesita Close()).

Instalación

go get github.com/Killkanacode/emitoo-go  # próximamente

Quickstart

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/Killkanacode/emitoo-go"
)

func main() {
	client, err := emitoo.New("fpk_...") // o export EMITOO_API_KEY=fpk_...
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	// 1. Emitir una factura. Con WithIdempotencyKey, reintentar el mismo
	//    POST devuelve la MISMA factura (no la duplica).
	factura, err := client.Facturas.Crear(ctx, emitoo.M{
		"tipo_identificacion": "04", // 04 = RUC, 05 = cédula
		"identificacion":      "0991234567001",
		"razon_social":        "ACME S.A.",
		"email":               "facturacion@acme.example",
		"detalles": []emitoo.M{
			{"codigo_principal": "LIC-1", "descripcion": "Licencia mensual",
				"cantidad": 1, "precio_unitario": 75.0, "tarifa_iva": 15},
		},
	}, emitoo.WithIdempotencyKey("orden-8912"))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(factura["id"], factura["estado"]) // 177 PENDIENTE

	// 2. El envío al SRI es asíncrono: espera a un estado final
	//    (retorna un error que envuelve emitoo.ErrTimeout si se agota).
	id := int(factura["id"].(float64))
	factura, err = client.Facturas.EsperarAutorizacion(ctx, id, 60*time.Second, 2*time.Second)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(factura["estado"]) // AUTORIZADO

	// 3. Descargar el RIDE (PDF) — devuelve []byte.
	pdf, err := client.Facturas.Ride(ctx, id)
	if err != nil {
		log.Fatal(err)
	}
	_ = pdf
}

Manejo de errores

En Go los errores de API son un único tipo *emitoo.APIError (.StatusCode, .Message, .Body, .RetryAfter, .Errors) que se distingue con predicados basados en errors.As:

factura, err := client.Facturas.Crear(ctx, payload)
if err != nil {
	var apiErr *emitoo.APIError
	switch {
	case emitoo.IsValidation(err): // 422
		errors.As(err, &apiErr)
		fmt.Println(apiErr.StatusCode, apiErr.Errors) // detail de FastAPI/Pydantic
	case emitoo.IsRateLimit(err): // 429
		errors.As(err, &apiErr)
		fmt.Println("reintenta en", *apiErr.RetryAfter, "segundos")
	default:
		fmt.Println("error de Emitoo:", err)
	}
}

Errores y reintentos (todos los SDKs)

La semántica es la misma en todos ellos. Python y TypeScript usan una jerarquía de excepciones; Go usa *APIError + predicados:

Status Python / TypeScript Go
401AuthenticationErrorIsAuthentication(err)
403PermissionDeniedErrorIsPermissionDenied(err)
404NotFoundErrorIsNotFound(err)
409ConflictErrorIsConflict(err)
422ValidationError (.errors)IsValidation(err) (.Errors)
429RateLimitError (.retry_after / .retryAfter)IsRateLimit(err) (.RetryAfter)
5xxServerErrorIsServer(err)
— (red)APIConnectionError*APIConnectionError

Todos reintentan automáticamente (hasta max_retries / maxRetries / WithMaxRetries, default 2, con backoff exponencial) ante 429 y errores de red/5xx — pero un POST solo se reintenta si le pasaste la idempotency key (ver Idempotency-Key): sin ella, un reintento podría duplicar el comprobante.

Recursos disponibles

Los SDKs oficiales exponen los mismos 14 recursos. Todos aceptan y devuelven diccionarios/objetos planos, salvo donde se indica (PDF → bytes/Uint8Array/[]byte, CSV → str/string).

Convención de nombres por lenguaje

La tabla usa los nombres de Python (snake_case). En TypeScript son camelCase (client.notasCredito.esperarAutorizacion(...), estadoCuentaPdf) y en Go PascalCase con ctx como primer argumento (client.NotasCredito.EsperarAutorizacion(ctx, ...)). Dos diferencias puntuales en Go: estado_cuenta(id, formato="csv") es un método aparte (EstadoCuentaCSV) y el PDF del estado de cuenta es EstadoCuentaPDF.
Recurso Para qué Métodos principales
facturas Facturas electrónicas crear, listar, obtener, status, reenviar, ride, email, anular, esperar_autorizacion
notas_credito Notas de crédito iguales a facturas
notas_debito Notas de débito iguales a facturas
liquidaciones Liquidaciones de compra iguales a facturas
guias Guías de remisión como facturas sin reenviar
retenciones Comprobantes de retención como facturas sin reenviar
compras Documentos de compra de proveedores listar, crear, obtener, actualizar, eliminar, categorias, importar_xml
cobros Cuentas por cobrar (CxC) listar, crear, cartera, estado_cuenta, estado_cuenta_pdf, obtener, anular
pagos_proveedor Cuentas por pagar (CxP) listar, crear, agenda, obtener, anular
cuentas_dinero Cajas y bancos listar, crear, obtener, actualizar, eliminar, listar_movimientos, crear_movimiento, transferir
personas_empresas Clientes y proveedores (catálogo) listar, lookup, obtener, crear, actualizar, eliminar
servicios Catálogo de servicios/productos listar, obtener, crear, actualizar, eliminar
sucursales Sucursales (establecimientos) listar, crear, actualizar, eliminar
eventos Feed de eventos (reconciliación pull) listar, obtener

Los SDKs no se interponen entre tú y la API

Son capas finas: los métodos pasan y devuelven diccionarios/objetos tal cual. Cuando la API agrega un campo nuevo (de forma retrocompatible), aparece en la respuesta sin que tengas que actualizar el SDK — no hay modelos que se queden desfasados.

Otros lenguajes

La API es REST estándar con JSON: cualquier cliente HTTP funciona. Para un cliente tipado en un lenguaje sin SDK oficial, genera uno a partir de la especificación OpenAPI 3.1.

OpenAPI: genera tu cliente tipado

La especificación de la API pública vive en https://api.emitoo.io/api/v1/public/openapi.json (y su Swagger UI en /api/v1/public/docs). Con ella puedes generar tipos y clientes:

# Tipos TS a partir de la OpenAPI
npx openapi-typescript https://api.emitoo.io/api/v1/public/openapi.json \
  -o src/emitoo.d.ts

# O un cliente completo
npx @hey-api/openapi-ts \
  -i https://api.emitoo.io/api/v1/public/openapi.json \
  -o src/emitoo

¿Quieres un SDK oficial en otro lenguaje?

Ya existen Python, TypeScript y Go. Si te haría la vida más fácil un paquete oficial en PHP, Ruby, Java, .NET… dínoslo por WhatsApp: priorizamos por demanda real.

Postman

Si prefieres explorar la API sin escribir código, descarga la especificación y una colección de Postman lista para importar, generada directamente desde la misma OpenAPI que usan los SDKs:

  • Colección de Postman (emitoo.postman_collection.json) — un request por endpoint, organizados por recurso.
  • OpenAPI 3.1 (openapi.json) — la misma especificación pública de arriba, para importar en Postman, Insomnia o generar tu propio cliente.

Para importar: en Postman, Import → File y elige emitoo.postman_collection.json; luego edita la variable de colección api_key (pestaña Variables) con tu key fpk_…base_url ya viene apuntando a https://api.emitoo.io.

Versionado y compatibilidad

La API REST está versionada en la URL (/api/v1): los cambios incompatibles solo llegarían con un /api/v2, sin romper integraciones existentes. Campos y endpoints nuevos se agregan de forma retrocompatible y aparecen en la OpenAPI el mismo día.