Facturiadocs
Impuestos

F29 — propuesta y declaración

Cómo calculamos la propuesta, qué código tienes que aportar tú, y las barreras que existen antes de que se declare algo.

La propuesta

curl https://api.facturia.cl/v1/tax/f29/proposals/2026-07 \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"
{
  "id": "f29_01K2RC4T8V2M",
  "object": "f29_proposal",
  "period": "2026-07",
  "status": "ready",
  "computed_at": "2026-08-14T06:15:00Z",
  "based_on": { "rcv_synced_at": "2026-08-14T06:12:03Z", "purchases": 42, "sales": 118 },
  "summary": {
    "iva_debito": 3210000,
    "iva_credito": 1985000,
    "iva_retenido": 190000,
    "remanente_anterior": 0,
    "ppm": 148000,
    "total_payable": 1373000,
    "due_date": "2026-08-20"
  },
  "codes": [
    { "code": 538, "label": "Total débitos",                          "value": 3210000 },
    { "code": 537, "label": "Total créditos",                         "value": 1985000 },
    { "code": 39,  "label": "IVA total retenido (facturas de compra)","value": 190000 },
    { "code": 519, "label": "Crédito IVA por facturas de compra",     "value": 190000 },
    { "code": 520, "label": "Crédito IVA — documentos electrónicos",  "value": 1795000 },
    { "code": 77,  "label": "Remanente crédito fiscal período siguiente", "value": 0 },
    { "code": 62,  "label": "PPM neto determinado",                   "value": 148000 },
    { "code": 91,  "label": "Total a pagar dentro del plazo",         "value": 1373000 }
  ],
  "observations": [
    { "code": "rcv_pendientes", "severity": "warning", "message": "3 documentos de compra están en estado PENDIENTE y no fueron considerados.", "entry_ids": ["rcv_...", "rcv_...", "rcv_..."] }
  ],
  "upload_file_url": "https://api.facturia.cl/v1/tax/f29/proposals/2026-07/upload_file",
  "filing_id": null
}

Dos cosas que conviene tener claras:

Esta propuesta la calcula Facturia desde tu RCV sincronizado más tus parámetros de PPM y remanente. El SII prellena su propio F29 para su propio portal, pero no publica una API para eso. Donde ambos difieran, nuestras observations normalmente te dicen por qué (compras no registradas, pendientes, huecos de caracterización) — y siempre puedes comparar contra la pantalla del SII antes de declarar.

La lista codes es lo que va a llevar el archivo de carga, código por código. No se declara nada que no puedas leer aquí primero.

El archivo de carga

curl -L https://api.facturia.cl/v1/tax/f29/proposals/2026-07/upload_file \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" -o f29-202607.txt

Es el .txt de formato fijo del SII, listo para subir a mano si prefieres hacerlo tú.

Recalcular y sobrescribir códigos

curl -X POST https://api.facturia.cl/v1/tax/f29/proposals/2026-07/recompute \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{"overrides":[{"code":62,"value":150000,"reason":"PPM ajustado por tasa variable"}]}'

El código 048 es una entrada externa

Trampa

Facturia no sincroniza el libro de remuneraciones

El código 048 (retención de impuesto único a los trabajadores, art. 74 N°1) sale de la nómina, no del RCV. Facturia no tiene acceso a esa información, así que no la puede calcular.

Si tu empresa tiene trabajadores, ese código lo aportas tú con overrides en recompute antes de declarar. Un F29 declarado con 048 en cero cuando debería tener valor es una declaración incorrecta, y no es un error que la API pueda detectar por ti.

curl -X POST https://api.facturia.cl/v1/tax/f29/proposals/2026-07/recompute \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" \
  -H "Content-Type: application/json" \
  -d '{"overrides":[{"code":48,"value":656814,"reason":"Retención art. 74 N°1 — libro de remuneraciones julio"}]}'

Todo override queda con su reason guardado, lo que importa si alguna vez hay que explicar de dónde salió una cifra.

Declarar

Trampa

No existe webservice del SII para declarar un F29

El único canal no manual es el flujo Upload del SII: un .txt de formato fijo enviado a través de una sesión autenticada en navegador. Facturia automatiza exactamente eso — una sesión autenticada con certificado manejando la carga real — y lo modela como un job asíncrono con estados honestos, porque eso es lo que es.

Si el SII cambia ese formulario, el job falla y te lo decimos. No hay una API estable debajo que nos proteja.

curl -sS https://api.facturia.cl/v1/tax/f29/filings \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" \
  -H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" \
  -d '{"period":"2026-07","proposal_id":"f29_01K2RC4T8V2M","confirm_total_payable":1373000}'
{
  "id": "f29fil_01K2RD9K3M8T",
  "object": "f29_filing",
  "period": "2026-07",
  "status": "queued",
  "declared": { "total_payable": 1373000 },
  "attempts": 0,
  "folio": null,
  "receipt_url": null,
  "created_at": "2026-08-14T14:30:02Z"
}

Las barreras

confirm_total_payable es obligatorio y debe ser igual a la cifra de la propuesta, o recibes 422 f29_amount_confirmation_mismatch. Declarar un formulario tributario no es algo que una API deba dejarte hacer por accidente.

Ese campo hace dos cosas a la vez: te obliga a leer la propuesta antes de declarar, y te obliga a que la cifra aparezca explícita en tu propio código — con lo cual aparece explícita en cualquier pantalla o mensaje que tu sistema muestre antes de apretar el botón.

Los estados del job

statusSignificado
queuedAceptado; esperando un worker de declaración
preparingGenerando y validando el archivo de carga
uploadingSesión autenticada abierta en el SII, enviando
submittedSubido; esperando el acuse del SII
acceptedDeclarado. folio y receipt_url quedan poblados
rejectedEl SII rechazó la declaración; errors[] explica
failedNo pudimos completar el envío (SII caído, sesión rechazada). Reintentable
curl https://api.facturia.cl/v1/tax/f29/filings/f29fil_01K2RD9K3M8T \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA"

curl -G https://api.facturia.cl/v1/tax/f29/filings \
  -H "Authorization: Bearer $FACTURIA_KEY" -H "Facturia-Empresa: $EMPRESA" -d "period[gte]=2026-01"

Eventos: tax.f29.filing.submitted, tax.f29.filing.succeeded, tax.f29.filing.rejected, tax.f29.filing.failed.

Una declaración no soporta PATCH ni DELETE. Un F29 declarado se corrige con una rectificatoria, que está fuera del alcance de la v1.

Declaración automática

auto_file_f29 viene apagada, como toda automatización.

AjustePor defectoEfecto
auto_file_f29falseDeclara el F29 del período en auto_file_f29_day
auto_file_f29_day18El día del mes en que se declara
auto_file_f29_max_payable0Solo declara sola si el total_payable calculado es menor o igual a este tope

Con el tope en 0 —el valor de fábrica— solo una posición cero o a favor se declara sola, y cualquier mes en que efectivamente debas IVA espera por ti. Sube el tope deliberadamente, empresa por empresa.

Trampa

Una propuesta con observaciones nunca se declara sola

Si hay observations con severity: error, la declaración automática se suprime y se dispara tax.f29.review_required. Declarar un F29 es una declaración jurídicamente vinculante: publicamos la capacidad porque una empresa chica que la quiera no debería tener que entrar al SII el día 18, pero es opt-in por empresa, con tope, y publica tax.f29.filing.succeeded / .failed para que puedas supervisarla.

En esta página