Folyo — SDK oficial de Python
SDK de Python para la API de Folyo: facturacion electronica chilena (DTE + SII) standalone. Del codigo al SII, sin escalas.
- Cliente tipado sobre
httpx(unica dependencia de runtime). - Errores tipados con mapeo de los codigos del servidor.
- Soporte del patron de emision asincrona (encolar + polling con backoff).
- Idempotencia, override de ambiente del SII y reintentos seguros ante 429/503.
- Redaccion de secretos (la API key/JWT nunca aparecen en
reprni en errores).
Instalacion
pip install folyo
Requiere Python 3.9 o superior.
Autenticacion
El cliente exige exactamente uno de los dos esquemas:
- API key (recomendada para integraciones server-side, no expira). Se envia
como header
X-API-Key. Fija el tenant y la empresa. - JWT Bearer (sesion de usuario). Se envia como
Authorization: Bearer ....
from folyo import Folyo
# Con API key
folyo = Folyo(api_key="<tu-api-key>")
# O con un token JWT
folyo = Folyo(token="eyJhbGciOi...")
Tambien funciona como context manager (cierra el cliente HTTP al salir):
with Folyo(api_key="<tu-api-key>") as folyo:
...
Quickstart: emitir una Factura Electronica (DTE 33)
La emision es asincrona: emitir() encola y devuelve un job_id; el helper
emitir_y_esperar() hace el polling por ti hasta que el job termina.
import uuid
from folyo import Folyo, DTERequest, Receptor, DetalleLinea, Referencia
with Folyo(api_key="<tu-api-key>") as folyo:
req = DTERequest(
tipo_dte=33, # Factura Electronica
receptor=Receptor(
rut="12.345.678-9",
razon_social="Cliente SpA",
giro="Comercio",
),
detalle=[
DetalleLinea(nombre="Servicio de consultoria", monto=100000),
],
# Referencias opcionales. tipo_doc_ref acepta codigos del SII (ej. 801 =
# orden de compra) o un codigo de texto de hasta 3 caracteres acordado
# con el receptor, como "HES" (Hoja de Entrada de Servicios). Para
# facturar al MOP usa tipo_doc_ref "UDP" (equivale al 802), el codigo de
# la unidad de pago que entrega el mandante en folio_ref y su
# descripcion en razon_ref.
referencia=[
Referencia(tipo_doc_ref=801, folio_ref="OC-2026-1487"),
Referencia(tipo_doc_ref="HES", folio_ref="4500123456"),
Referencia(tipo_doc_ref="UDP", folio_ref="2020", razon_ref="NIVEL_CENTRAL_SUBSECRETARIA"),
],
)
# Encolar y esperar el resultado (con Idempotency-Key para reintentar seguro)
job = folyo.dte.emitir_y_esperar(req, idempotency_key=str(uuid.uuid4()))
if job.estado == "completed" and job.result is not None:
print("Folio:", job.result.folio)
print("Track ID:", job.result.track_id)
print("Total:", job.result.monto_total)
# Descargar el PDF
pdf = folyo.dte.descargar_pdf(33, job.result.folio)
with open(f"factura-{job.result.folio}.pdf", "wb") as f:
f.write(pdf)
else:
print("La emision fallo:", job.estado)
Nota:
completedsignifica que el SII recibio el documento (tienes folio y track_id). El veredicto (aceptacion o rechazo) llega despues: el documento queda en estadoenviadoy Folyo lo consulta automaticamente hasta resolverlo. El SII puede demorar desde minutos hasta mas de una hora; no reemitas por eso — suscribete al webhook de cambio de estado.
Si prefieres resolver el resultado por webhook o SSE, usa emitir() directo:
encolada = folyo.dte.emitir(req)
print(encolada.job_id) # resuelve luego via webhook dte.emitido o polling
Impuestos adicionales y retenciones por linea
Una linea puede llevar impuestos con el codigo del impuesto adicional del
DL 825 (art. 42). Tu mandas el codigo; Folyo resuelve la tasa desde su catalogo,
calcula el monto sobre la misma base que el IVA y lo desglosa en el documento.
import uuid
from folyo import Folyo, DTERequest, Receptor, DetalleLinea, ImpuestoLinea
with Folyo(api_key="<tu-api-key>") as folyo:
req = DTERequest(
tipo_dte=33,
receptor=Receptor(rut="12.345.678-9", razon_social="Distribuidora SpA", giro="Comercio"),
detalle=[
DetalleLinea(
nombre="Cerveza lager 330 ml",
cantidad=120,
precio=12500,
monto=1500000,
impuestos=[ImpuestoLinea(codigo="26")], # cervezas: 20,5%
),
],
)
job = folyo.dte.emitir_y_esperar(req, idempotency_key=str(uuid.uuid4()))
# Neto 1.500.000 + IVA 285.000 + impuesto 307.500 = total 2.092.500
if job.result is not None:
for imp in job.result.impuestos:
print(imp.codigo, imp.glosa, imp.tasa, imp.monto, imp.retencion)
# 26 Cervezas y otras bebidas alcoholicas 20.5 307500 False
| Codigo | Impuesto | Tasa |
|---|---|---|
"24" |
Licores, piscos y whisky (incluye aguardientes y vinos licorosos) | 31,5% |
"25" |
Vinos | 20,5% |
"26" |
Cervezas y otras bebidas alcoholicas | 20,5% |
"27" |
Bebidas analcoholicas y minerales | 10% |
"271" |
Bebidas analcoholicas con azucar elevada | 18% |
"15" |
IVA retenido total generico por cambio de sujeto | 19% |
"30/32/33/34/36/37/48" |
Retencion parcial: legumbres, ganado, madera, trigo, arroz, hidrobiologicos y frambuesas | 10/8/8/4/10/10/14% |
"31/38/39/41/47" |
Retencion total: silvestres, chatarra, PPA, construccion y cartones | 19% IVA |
"23/44/45" |
Suntuarios | 15/15/50% |
"28/35/51/52" |
Especificos de diesel, gasolina y gases | monto fijo, sin tasa |
"17" |
IVA anticipado de faenamiento | 5% de la base especial faenamiento |
"18/19" |
IVA anticipado de carne y harina | 5/12% |
"46" |
IVA retenido oro | 100% del IVA, solo 33 y NC/ND 56/61 referenciarias |
La constante folyo.IMPUESTOS_ADICIONALES conserva el mapa de las bebidas y
folyo.IMPUESTOS_DTE expone todos los codigos habilitados; sus valores None
identifican los impuestos de monto fijo.
Los codigos se validan por tipo de DTE. Las retenciones parciales van en una
factura de compra 46 o en su NC/ND 56/61 de referencia. Para que una de
ellas retenga el IVA completo, envia el codigo base y ndf=True: Folyo usa la
variante total (30→301, 32→321, 33→331, 34→341, 36→361, 37→371,
48→481) y omite iva_no_retenido. La API no consulta nominas SII; quien emite
debe cumplir la calidad de agente retenedor que exija el regimen.
Envia normalmente el codigo parcial con ndf=True, sin tasa ni monto:
Folyo lo normaliza a la variante total. Tambien puede enviarse la variante total
canonica directamente con ndf=True; solo para revalidacion admite tasa=19
y un monto que cuadre exactamente con la base del codigo.
Una linea exenta no puede llevar impuesto. Con impuestos o retenciones, un
descuento/recargo global solo puede ser porcentual: se prorratea en cada base
antes de calcular los montos. Un descuento global en pesos responde
400 INVALID_REQUEST antes de reservar folio. El tipo 43 solo admite
adicionales, nunca retenciones ni iva_no_retenido. Los codigos 17 y 46
siguen cerrados. Mientras una familia no este validada en certificacion para tu
empresa, produccion responde 422 IMPUESTO_PENDIENTE_CERT.
El codigo 15 conserva su compatibilidad historica sin descuento ni recargo
global. Si se combina con uno porcentual, produccion exige que el codigo 15
este habilitado explicitamente; de lo contrario responde
422 IMPUESTO_PENDIENTE_CERT.
El codigo 17 exige faenamiento en cada linea afecta: codigo_cpcs (1701
a 1706), cantidad_cabezas positiva y monto_base_faena positivo. La linea
usa cantidades de hasta 6 decimales y se normaliza a micro-unidades cuyo valor
escalado no excede 9007199254740991 (maximo nominal
9007199254.740991); la base y su suma no superan 9007199254740991.
La linea usa cantidad en KG; Folyo deriva CPCS, Retenedor, QtyRef UN, el impuesto
17 y monto_base del resultado. Solo aplica a 33 y a 56/61 que referencian una
33 compatible; no admite descuentos o recargos globales ni mezcla con otros
impuestos. El emisor debe contar previamente con acreditacion operativa como
agente retenedor: Folyo no hace lookup automatico ni afirma una aprobacion
digital persistida; produccion se habilita despues de certificacion. El codigo
46 es una retencion total de oro para 33 y sus 56/61
referenciarias; no se usa en la Factura de Compra DTE 46.
tasa y monto son opcionales y normalmente no se envian: el catalogo de
Folyo resuelve la tasa vigente. Si entregas una tasa, debe ser finita, estar
entre 0,01% y 100% y tener como maximo dos decimales; debes enviarla con el
mismo valor en todas las lineas que llevan ese codigo, o en ninguna. Si
entregas monto, debes entregarlo en todas esas lineas y la suma debe ser
exactamente round(base_del_codigo * tasa / 100): no se acepta redondear cada
linea por separado. monto es obligatorio para los especificos fijos 28,
35, 51 y 52, que no llevan tasa.
Para la retencion total del IVA en una factura de compra (46), la forma
recomendada es impuestos=[ImpuestoLinea(codigo="15")] en cada linea afecta,
sin tasa ni monto: el codigo 15 siempre retiene el IVA total al 19% y
produce el mismo XML que retencion_iva_total=True, que queda obsoleto pero
sigue funcionando.
Para los servicios agricolas de la Res. Ex. SII 83/2026, el uso del codigo 15
es una inferencia pendiente de confirmacion tributaria y certificacion. La
capacidad tecnica no acredita que el emisor cumpla los requisitos de ese
regimen; la API no consulta ni acredita esa condicion ante el SII.
El desglose tambien vuelve en impuestos del listado de documentos, junto con
monto_neto, monto_exento, monto_iva e iva_no_retenido cuando la
retencion fue parcial:
for doc in folyo.dte.listar_documentos(limite=1):
for imp in doc.impuestos:
print(imp.codigo, imp.glosa, imp.tasa, imp.monto)
Manejo de errores
from folyo import (
Folyo,
FolyoAuthError,
FolyoQuotaError,
FolyoRateLimitError,
FolyoValidationError,
FolyoSiiUnavailableError,
)
try:
folyo.dte.emitir(req)
except FolyoRateLimitError as e:
print("Reintentar en", e.retry_after, "segundos")
except FolyoQuotaError as e:
print("Limite de plan o pago requerido:", e.code)
except FolyoAuthError:
print("Credenciales invalidas o sin permisos")
except FolyoValidationError as e:
print("Datos invalidos:", e.message)
except FolyoSiiUnavailableError:
print("El SII no esta disponible, reintenta mas tarde")
El SDK reintenta automaticamente (con backoff exponencial y respetando
Retry-After) ante 429 y 503 en operaciones seguras: peticiones GET y
emisiones con Idempotency-Key.
Recursos disponibles
| Recurso | Metodos principales |
|---|---|
folyo.dte |
emitir, get_emision, emitir_y_esperar, listar_documentos, descargar_xml, descargar_pdf, regenerar_pdf, consultar_estado, consultar_envio, emitidos, recibidos, contribuyente, situacion_tributaria, enviar_rcof, resumen_rcof |
folyo.folios |
info, solicitar |
folyo.rcv |
periodos, periodo, sync, resumen_iva |
folyo.clientes |
listar, upsert, importar, buscar, actualizar, eliminar |
folyo.empresa |
listar, seleccionar |
folyo.acuse |
registrar, pendientes, estado |
folyo.webhooks |
listar, crear, actualizar, eliminar |
folyo.api_keys |
listar, crear, eliminar |
Algunos endpoints (clientes, RCV, listado de documentos) requieren "panel
operativo" y responden 403 en planes solo-API.
Seguridad
- La API key y el JWT nunca se incluyen en
repr(cliente)ni en los errores. - Los errores exponen solo el mensaje sanitizado del servidor, el codigo
estable, el status HTTP y el
request_id: nunca el cuerpo de la respuesta (que puede traer datos sensibles como el XML firmado o secrets de webhook). - El SDK no escribe logs por defecto.
Licencia
MIT. Ver LICENSE.
Release files for folyo 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| folyo-0.2.0.tar.gz | 25.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| folyo-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.1 kB
Release files / folyo-0.2.0.tar.gz
| Download URL | folyo-0.2.0.tar.gz |
|---|---|
| Size | 25.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bc8c64c82f228646d266d9ec108b6f57681edb3336359d29d2e1aa24305faa5f
|
|
BLAKE2b-256 checksum How to use checksums |
2d54dad91a02baf4b08836843e37413d12042687e4770a9830f9d1a95d49a58c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / folyo-0.2.0-py3-none-any.whl
| Download URL | folyo-0.2.0-py3-none-any.whl |
|---|---|
| Size | 27.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8b4384e5e0074cf20247325e2e9208e09420bdd4cef4c2610e5fb7660793feeb
|
|
BLAKE2b-256 checksum How to use checksums |
527650a0634f08ae507ca60423a76056666f18ffdf095196a420aca6503bfc2b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency log