Skip to main content

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 repr ni 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: completed significa que el SII recibio el documento (tienes folio y track_id). El veredicto (aceptacion o rechazo) llega despues: el documento queda en estado enviado y 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)

Source distribution for folyo 0.2.0
File Size Uploaded
folyo-0.2.0.tar.gz 25.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for folyo 0.2.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

0.2.1

2 release files

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page