CommunityRedacción y edicióngithub.com

FIZ Facturación

Emitir facturas en Portugal con IA — Skill para Claude Code / Agent de la API de facturación FIZ (IVA, exención, CAE, NIF, AT)

¿Qué es FIZ Facturación?

Este skill permite a un agente de IA (como Claude Code o Codex) emitir facturas legalmente vinculantes en Portugal a través de la API pública de FIZ. Automatiza el ciclo completo de facturación: buscar o crear clientes y artículos, construir borradores, emitir documentos fiscales (paso irreversible que sincroniza con la Autoridad Tributaria) y descargar PDFs. Su diseño prioriza la corrección fiscal: incluye lógica para seleccionar el tipo de IVA correcto (NORMAL, INTERMEDIO, REDUCIDO, EXENTO) según el territorio, exige un código de motivo de exención (M) cuando corresponda y valida campos críticos como el CAE o el NIF. El skill requiere confirmación explícita del usuario antes de cualquier escritura y una confirmación separada antes de emitir, momento en que el documento se vuelve legal. Se apoya en un script shell (`fiz.sh`) que gestiona la autenticación mediante clave API y verifica códigos de estado HTTP para evitar confundir errores con éxitos. Además, cuenta con dos archivos complementarios (`domain.md` y `reference.md`) que proporcionan la normativa fiscal portuguesa detallada y la referencia completa de la API. Está diseñado para entornos que provean `CLAUDE_SKILL_DIR` o una ruta equivalente, y su invocación por defecto es automática, aunque puede configurarse como manual. Soporta todos los tipos de documento portugués: factura, factura-recibo, factura simplificada, recibo, nota de crédito y nota de débito.

Compatible con✓Claude Code✓Codex CLI~Cursor
npx skills add FIZ-co/fiz-invoicing-skill

Installed? Explore more Redacción y edición skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Facturación FIZ

Emite facturas con la API pública de FIZ — una puerta de enlace REST en https://api.fiz.co. Este skill guía a través del ciclo de vida completo: buscar o crear el cliente, buscar o crear los artículos, construir un borrador de factura, emitirla (este es el paso legalmente vinculante) y, opcionalmente, descargar el PDF.

Configuración

Cada solicitud necesita la clave API del cliente en la cabecera x-api-key. Obtén una clave en https://app.fiz.co/settings/integrations.

Guárdala en una variable de entorno para que nunca se codifique directamente en los comandos:

export FIZ_API_KEY="fiz_api_..."     # la clave del usuario desde app.fiz.co
export FIZ_API_URL="https://api.fiz.co"   # opcional; este es el valor por defecto

Todos los ejemplos siguientes usan $FIZ_API_KEY y $FIZ_API_URL. Si FIZ_API_URL no está definido, usa https://api.fiz.co. Si FIZ_API_KEY no está definido, detente e indica al usuario que la configure en su entorno (export FIZ_API_KEY=… en su terminal, o un gestor de secretos) — no le pidas que pegue la clave en el chat y nunca imprimas ni muestres el valor de la clave. Una clave API es una credencial; mantenla fuera de la transcripción de la conversación.

Se proporciona un script reutilizable curl en scripts/fiz.sh. Los comandos se ejecutan desde el directorio de trabajo de la sesión (la raíz de tu proyecto), no desde el directorio del skill, así que carga el script usando su ruta absoluta a través de ${CLAUDE_SKILL_DIR} (Claude Code establece esta variable en el directorio propio del skill):

source "${CLAUDE_SKILL_DIR}/scripts/fiz.sh"   # se resuelve desde cualquier directorio de trabajo
fiz GET /invoices

En un entorno de ejecución que no proporcione CLAUDE_SKILL_DIR, sustituye esa variable por la ruta equivalente al directorio del skill (una ruta relativa simple scripts/fiz.sh no se resolverá desde la raíz del proyecto).

El script auxiliar imprime el código de estado HTTP y devuelve un valor distinto de cero en errores 4xx/5xx, por lo que una respuesta de error nunca se confunde con un éxito; consulta la sección "Manejo de errores" más abajo. También se muestran ejemplos con curl sin formato; si usas curl directamente, comprueba siempre el código de estado HTTP, no solo si el comando terminó con código 0.

El modelo mental

  • Una factura referencia un cliente (por su id) y uno o más artículos (por su id). Por lo tanto, los clientes y los artículos deben existir antes de que puedas construir la factura.
  • POST /invoices crea un borrador. Un borrador aún no es un documento legal — no se envía a la autoridad tributaria y se puede editar o eliminar libremente.
  • POST /invoices/:id/issue lo emite — asigna el número oficial y el ATCUD, sincroniza con la Autoridad Tributaria portuguesa (AT) y lo hace definitivo. La emisión es irreversible; confirma con el usuario antes de emitir. Una factura emitida no se puede eliminar; la única forma de deshacerla es una nota de crédito.
  • La única excepción a "crear y luego emitir": si pasas un objeto payment al crear para los tipos INVOICE_RECEIPT o SIMPLIFIED_INVOICE, el documento registra el pago — pero igualmente tienes que emitirlo para finalizarlo.

Confirmación antes de escrituras

Estos endpoints cambian el estado en la cuenta del usuario. Dos niveles de cuidado:

  • Planifica, luego previsualiza todas las escrituras antes de la primera. Una vez que sepas lo que necesita la solicitud, enumera todas las escrituras que planeas realizar para esta tarea — cada endpoint más su carga JSON (el cliente a crear, cada artículo, el borrador de factura) — y muestra ese lote al usuario en una sola previsualización. Procede con las escrituras solo después de que el usuario apruebe el plan. Si el plan cambia durante la ejecución (por ejemplo, una búsqueda de artículo falla y ahora necesitas crear uno), previsualiza la nueva escritura antes de enviarla. Nunca inventes datos del cliente, campos fiscales o cantidades; usa solo lo que el usuario te haya dado.
  • Antes de POST /invoices/:id/issue — esto es irreversible y se reporta a la autoridad tributaria. Solicita siempre una confirmación explícita y separada (más allá de la aprobación del plan anterior), mostrando el cliente del borrador, las líneas de detalle, el IVA y el total. No emitas por iniciativa propia.

Política de invocación (deliberada): este skill mantiene la auto-invocación del modelo habilitada — la description es la superficie de descubrimiento, y la red de seguridad real es la previsualización de todas las escrituras + la confirmación separada de emisión anterior, no la restricción del descubrimiento. El manifiesto de Codex (agents/openai.yaml) está configurado para coincidir (allow_implicit_invocation: true). Un operador que quiera un uso solo manual puede establecer disable-model-invocation: true en la portada (se carga solo en /fiz-invoicing).

Esto es facturación fiscal portuguesa — por qué la corrección es importante

FIZ emite documentos fiscales legalmente vinculantes que se reportan a la Autoridad Tributaria portuguesa (Autoridade Tributária, "AT"). Un tipo de IVA incorrecto o un motivo de exención faltante no es un error cosmético — produce un documento fiscal incorrecto que, una vez emitido, solo puede corregirse con una nota de crédito. Asegúrate de que los campos fiscales sean correctos antes de emitir.

Las dos cosas que tienen más probabilidades de estar mal, y sobre las que debes razonar:

1. Tipo de IVA (vatRate en cada artículo) — nunca lo adivines. La banda de tasa (NORMAL | INTERMEDIATE | REDUCED | EXEMPT) y el porcentaje al que corresponde dependen del territorio (Continental / Açores / Madeira). Usa NORMAL por defecto — REDUCED/INTERMEDIATE se aplican solo a categorías legalmente definidas específicas (ciertos alimentos, restaurantes, eventos culturales…). Si el usuario no ha nombrado dicha categoría, usa NORMAL e indícalo, o pregunta; nunca apliques una tasa reducida en silencio. La tabla de porcentajes por territorio está en domain.md.

2. La exención de IVA requiere un código de motivo (vatExemptionReason). Si vatRate es EXEMPT, la ley portuguesa exige un motivo de isenção — un código M que indique la base legal. Establécelo siempre; un artículo exento sin un motivo válido es un documento inválido y puede ser rechazado al emitir. El código debe reflejar el motivo real: régimen de pequeños empresarios → M10, actividad exenta por naturaleza (salud/educación) → M07, B2B intracomunitario / inversión del sujeto pasivo → M40/M16/M19. Si el usuario está exento pero no sabe el código, pregunta por qué y haz el mapeo — no elijas arbitrariamente. La lista completa de códigos con referencias legales está en domain.md.

Otras reglas del dominio que vale la pena conocer (detalles en domain.md):

  • CAE es un código portugués de actividad económica de 5 dígitos que debe ser uno de los que la empresa tiene registrados. No lo inventes — pregúntalo al usuario, o reutiliza el CAE de una factura anterior. Debe ser un código actual (Rev. 4).
  • NIF (taxpayerNumber) es un número de identificación fiscal portugués de 9 dígitos con dígito de control; obligatorio para B2B, opcional para consumidores finales. Para B2B intracomunitario, el número de IVA del cliente es relevante para el tratamiento de inversión del sujeto pasivo.
  • Retención en la fuente (retenção na fonte: IRS/IRC/IS) se configura en el artículo; la tasa común es del 25% para servicios de autónomos. Es retenida por el pagador, reduciendo el importe realmente pagado.
  • El tipo de documento afecta las reglas: un SIMPLIFIED_INVOICE es para pequeñas ventas al por menor y puede omitir los datos completos del cliente; una INVOICE a una empresa por más de 1.000 € necesita el NIF del cliente.

Flujo de trabajo

Sigue estos pasos. Omite los pasos 1–2 si el usuario ya te proporciona un id de cliente e ids de artículos.

Paso 1 — Resolver el cliente

Busca primero para evitar duplicados, luego crea si es necesario.

Los ejemplos usan el script auxiliar fiz (ver Configuración) — comprueba el código de estado HTTP por ti. El equivalente con curl sin formato está en el apéndice.

Buscar por término (nombre, número fiscal, correo electrónico):

fiz GET "/customers?search=Joao"

Crear un cliente (solo name es obligatorio):

fiz POST /customers '{
  "name": "João Silva",
  "email": "[email protected]",
  "taxpayerNumber": "303741791",
  "country": "PT"
}'

Conserva el id devuelto — ese es el customerId para la factura.

Paso 2 — Resolver los artículos (productos / servicios)

Buscar artículos existentes:

fiz GET "/items?search=consulting"

Reutiliza un artículo existente solo si sus campos fiscales coinciden con la venta. Una coincidencia de búsqueda con el nombre correcto no es suficiente — para un documento fiscal, los campos vatRate, vatTerritory, vatExemptionReason, taxRate y de retención del artículo deben coincidir con el tratamiento que pretendes para este cliente y venta. Si alguno de ellos difiere (por ejemplo, el mismo servicio pero el cliente está exento, en otro territorio, o sujeto a retención), crea un nuevo artículo con los campos correctos en lugar de reutilizar el que no coincide. Cuando tengas dudas, inspecciona los campos del artículo encontrado y confírmalo con el usuario.

Crear un artículo. Obligatorio: name, type (PRODUCT | SERVICE), unitPrice, vatRate (NORMAL | INTERMEDIATE | REDUCED | EXEMPT):

fiz POST /items '{
  "name": "Consulting hour",
  "type": "SERVICE",
  "unitPrice": 80,
  "vatRate": "NORMAL"
}'

Conserva cada id devuelto — esos son los ids de los artículos para la factura.

unitPrice es el precio unitario; los totales por línea son calculados por FIZ a partir de la quantity que proporciones en la factura. Elige vatRate deliberadamente — consulta la sección de dominio anterior; usa NORMAL por defecto a menos que la categoría claramente justifique una tasa reducida. Si vatRate es EXEMPT, establece siempre un código M de vatExemptionReason que coincida con por qué la venta está exenta (por ejemplo, M10 para el régimen de pequeños empresarios). El campo es técnicamente opcional al crear el artículo, pero un artículo exento sin un motivo válido es incorrecto y puede ser rechazado más tarde cuando emitas la factura. Consulta domain.md para la lista completa de códigos.

Paso 3 — Crear el borrador de factura

Campos obligatorios: dueDate (ISO 8601), cae (código de actividad económica portugués), type, customerId e items (al menos un { id, quantity }).

fiz POST /invoices '{
  "dueDate": "2026-07-18T00:00:00.000Z",
  "cae": "62010",
  "type": "INVOICE",
  "customerId": "6863b1513117c5892ff55296",
  "items": [
    { "id": "68483e978073231c3947077c", "quantity": 10 }
  ]
}'

La respuesta incluye el nuevo id de la factura (necesario para emitir) y status (DRAFT). La date de la factura se establece a la fecha actual y currency es EUR por parte de la API — no los envías.

Opcional — descuento global (summary):

"summary": { "globalDiscountType": "PERCENT", "globalDiscountPercent": 10 }

Usa "AMOUNT" con globalDiscountAmount para un descuento de valor fijo.

Opcional — pago en la creación (payment): solo permitido cuando type es INVOICE_RECEIPT o SIMPLIFIED_INVOICE; la API devuelve 400 en caso contrario.

"payment": { "method": "mbWay", "date": "2026-07-18T00:00:00.000Z" }

method es uno de: cash, card, bankTransfer, mbWay, multibanco, spin, other.

Paso 4 — Emitir la factura

Este es el paso vinculante. Confirma con el usuario, luego:

fiz POST /invoices/{id}/issue

La respuesta incluye el number oficial, status: ISSUED y un bloque syncWithAt. Comprueba syncWithAt.status — si es FAILED, la factura se creó localmente pero no se sincronizó con la autoridad tributaria; reporta atMessage y si isRetriable es true al usuario en lugar de afirmar éxito.

Paso 5 — Descargar el PDF (opcional)

fiz GET /invoices/{id}/pdf

Devuelve { id, name, url }; la url es un enlace descargable al PDF. Añade ?format=A4 o ?format=RECEIPT para elegir el diseño.

Tipos de documento (type)

Valores: INVOICE · INVOICE_RECEIPT · SIMPLIFIED_INVOICE · RECEIPT · CREDIT_NOTE · DEBIT_NOTE. Usa INVOICE por defecto a menos que el usuario solicite un recibo o factura simplificada. Solo INVOICE_RECEIPT y SIMPLIFIED_INVOICE aceptan un objeto payment en la creación. Consulta domain.md para saber qué significa cada tipo y cuándo usarlo.

Referencia de operaciones comunes

AcciónMétodo y ruta
Listar/buscar clientesGET /customers?search=…
Crear clientePOST /customers
Listar/buscar artículosGET /items?search=…
Crear artículoPOST /items
Crear borradorPOST /invoices
EmitirPOST /invoices/:id/issue
Listar facturasGET /invoices
Obtener una facturaGET /invoices/:id
Descargar PDFGET /invoices/:id/pdf
Eliminar (borrador)DELETE /invoices/:id

Manejo de errores

Comprueba siempre el código de estado HTTP, no solo que el comando se ejecutó. En caso de error, la API devuelve un cuerpo JSON de apariencia normal — { "statusCode": 400, "message": "...", "timestamp": "..." } — con el código de estado HTTP correspondiente. Con curl -s sin formato, ese cuerpo se imprime y curl aún termina con código 0, por lo que es fácil confundir un error con un resultado. El script auxiliar fiz protege contra esto: imprime una línea HTTP <code> y devuelve un valor distinto de cero en errores 4xx/5xx. Si debes usar curl directamente, añade -w '\n%{http_code}' (o -o body -w '%{http_code}') e inspecciona el código.

Códigos comunes:

  • 401 — clave API faltante o inválida. Indica al usuario que configure FIZ_API_KEY en su entorno (no en el chat).
  • 400 — error de validación; el message indica qué campo. Casos comunes: items vacío, payment en un type no compatible, un campo desconocido o extra (lista blanca estricta), dueDate mal formado (debe ser ISO 8601 completa con zona horaria, por ejemplo 2026-07-18T00:00:00.000Z).
  • 5xx — problema del backend/AT; comunícalo, no reintentes la misma escritura a ciegas (una creación puede haber tenido éxito parcial — comprueba primero con un GET).

Siempre devuelve el message de la API al usuario en lugar de reintentar a ciegas.

El contrato completo y autoritativo de solicitud/respuesta es la interfaz Swagger en vivo en https://api.fiz.co/ — consúltalo si algún campo aquí parece desactualizado.

Cuando necesitas más detalle

Dos archivos complementarios en este directorio del skill:

  • domain.md — el dominio fiscal portugués: tipos de IVA por territorio, la lista completa de códigos de exención (M) y cuándo se aplica cada uno, tipos de documento, reglas de CAE/NIF, retención en la fuente y cómo funcionan la sincronización con AT y la irreversibilidad de la emisión. Léelo siempre que una decisión fiscal no sea obvia (especialmente al elegir un vatRate o un motivo de exención).
  • reference.md — la referencia completa de la API campo por campo (todos los campos de cliente/artículo/factura, todos los valores de enumeración, formas completas de solicitud/respuesta).

Apéndice — curl sin formato (sin el script auxiliar)

Prefiere el script auxiliar fiz anterior; comprueba el código de estado HTTP. Si no está disponible, usa curl directamente con -w '\n%{http_code}' y parsea la última línea. Ejemplos:

# Buscar clientes
resp=$(curl -s -w '\n%{http_code}' -H "x-api-key: $FIZ_API_KEY" \
  "$FIZ_API_URL/customers?search=Joao")
http_code=$(echo "$resp" | tail -1)
body=$(echo "$resp" | sed '$d')
if [ "$http_code" -ge 400 ]; then
  echo "Error $http_code: $body"
fi

# Crear cliente
curl -s -X POST -w '\nHTTP %{http_code}' \
  -H "x-api-key: $FIZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "João Silva" }' \
  "$FIZ_API_URL/customers"

Siempre supone que la clave API está en una variable de entorno, nunca la codifiques directamente.


Este skill es mantenido por FIZ — facturación portuguesa para desarrolladores. Documentación completa: https://docs.fiz.co

Skills relacionados