CommunityEscrita e Ediçãogithub.com

FIZ Faturação

Emitir faturas em Portugal com IA — Skill para Claude Code / Agent da API de faturação FIZ (IVA, isenção, CAE, NIF, AT)

O que é FIZ Faturação?

Esta skill permite que agentes de IA (como Claude Code, Cursor ou Codex) emitam faturas legalmente vinculativas em Portugal através da API pública FIZ (https://api.fiz.co). Ela automatiza todo o ciclo de vida da faturação: localizar ou criar clientes, localizar ou criar artigos, construir um rascunho de fatura e emiti-lo — o passo que comunica com a Autoridade Tributária (AT). A skill impõe medidas de segurança críticas, como solicitar confirmação explícita antes de qualquer envio para a AT e descartar a criação de dados fiscais incorretos, exigindo que o agente valide taxas de IVA, razões de isenção e códigos de atividade económica (CAE) com o utilizador. Suporta todos os tipos de documentos portugueses (fatura, fatura-recibo, fatura simplificada, nota de crédito, etc.) e gere cenários fiscais complexos: IVA por território (Continental, Açores, Madeira), isenções com códigos M, regimes de reverse charge, retenção na fonte e validação de NIF. A configuração requer apenas uma chave de API armazenada numa variável de ambiente, e a skill disponibiliza um script bash reutilizável (scripts/fiz.sh) que simplifica os pedidos HTTP e trata erros de forma robusta.

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

Installed? Explore more Escrita e Edição skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Faturação FIZ

Emitir faturas com a API Pública FIZ — um gateway REST em https://api.fiz.co. Esta skill percorre o ciclo de vida completo: localizar ou criar o cliente, localizar ou criar os artigos, construir um rascunho de fatura, emiti-la (este é o passo legalmente vinculativo) e, opcionalmente, descarregar o PDF.

Configuração

Cada pedido necessita da chave de API do cliente no cabeçalho x-api-key. Obtenha uma chave em https://app.fiz.co/settings/integrations.

Armazene-a numa variável de ambiente para que nunca seja codificada em comandos:

export FIZ_API_KEY="fiz_api_..."     # a chave do utilizador em app.fiz.co
export FIZ_API_URL="https://api.fiz.co"   # opcional; este é o valor predefinido

Todos os exemplos abaixo usam $FIZ_API_KEY e $FIZ_API_URL. Se FIZ_API_URL não estiver definido, use https://api.fiz.co. Se FIZ_API_KEY não estiver definido, pare e diga ao utilizador para a definir no seu ambiente (export FIZ_API_KEY=… no seu terminal, ou num gestor de segredos) — não lhe peça para colar a chave no chat, e nunca imprima ou mostre o valor da chave. Uma chave de API é uma credencial; mantenha-a fora do transcrito da conversa.

Um script curl reutilizável é fornecido em scripts/fiz.sh. Os comandos são executados a partir do diretório de trabalho da sessão (a raiz do seu projeto), não do diretório da skill, por isso importe o helper pelo seu caminho absoluto via ${CLAUDE_SKILL_DIR} (o Claude Code define este caminho para o diretório da própria skill):

source "${CLAUDE_SKILL_DIR}/scripts/fiz.sh"   # resolve a partir de qualquer diretório de trabalho
fiz GET /invoices

Num runtime que não forneça CLAUDE_SKILL_DIR, substitua pelo caminho equivalente do diretório da skill desse runtime (um scripts/fiz.sh relativo simples não será resolvido a partir da raiz do projeto).

O helper imprime o estado HTTP e retorna um código diferente de zero em casos de 4xx/5xx, pelo que uma resposta de erro nunca é confundida com sucesso — veja "Tratamento de erros" abaixo. Exemplos curl simples também são mostrados; se usar curl puro, verifique sempre o estado HTTP, não apenas se o comando terminou com código 0.

O modelo mental

  • Uma fatura referencia um cliente (pelo id) e um ou mais artigos (pelo id). Portanto, clientes e artigos devem existir antes de poder construir a fatura.
  • POST /invoices cria um rascunho. Um rascunho ainda não é um documento legal — não é enviado para a autoridade fiscal e pode ser editado ou eliminado livremente.
  • POST /invoices/:id/issue emite-a — atribui o número oficial e o ATCUD, sincroniza com a Autoridade Tributária (AT) e torna-a definitiva. A emissão é irreversível; confirme com o utilizador antes de emitir. Uma fatura emitida não pode ser eliminada — a única forma de a anular é com uma nota de crédito.
  • A única exceção a "criar e depois emitir": se passar um objeto payment na criação para os tipos INVOICE_RECEIPT ou SIMPLIFIED_INVOICE, o documento regista o pagamento — mas ainda precisa de a emitir para finalizar.

Confirmação antes de escritas

Estes endpoints alteram o estado na conta do utilizador. Dois níveis de cuidado:

  • Planear e, em seguida, pré-visualizar todas as escritas antes da primeira. Quando souber o que o pedido necessita, liste todas as escritas que pretende fazer para esta tarefa — cada endpoint mais o seu payload JSON (o cliente a criar, cada artigo, o rascunho de fatura) — e mostre esse lote ao utilizador numa única pré-visualização. Prossiga com as escritas assim que eles aprovarem o plano. Se o plano mudar a meio da execução (por exemplo, uma pesquisa de artigo falha e agora precisa de criar um novo), pré-visualize a nova escrita antes de a enviar. Nunca invente dados do cliente, campos fiscais ou valores — use apenas o que o utilizador lhe forneceu.
  • Antes de POST /invoices/:id/issue — isto é irreversível e reportado à autoridade fiscal. Obtenha sempre uma confirmação explícita e separada (para além da aprovação do plano acima), mostrando o cliente do rascunho, as linhas de artigo, o IVA e o total. Não emita por sua própria iniciativa.

Política de invocação (deliberada): esta skill mantém a auto-invocação do modelo ativada — a description é a superfície de descoberta, e a verdadeira rede de segurança é a pré-visualização de todas as escritas + confirmação de emissão separada acima, e não a limitação da descoberta. O manifesto Codex (agents/openai.yaml) está configurado para corresponder (allow_implicit_invocation: true). Um operador que pretenda uso apenas manual pode definir disable-model-invocation: true no frontmatter (carrega apenas em /fiz-invoicing).

Isto é faturação fiscal portuguesa — porque a correção é importante

A FIZ emite documentos fiscais legalmente vinculativos que são reportados à autoridade fiscal portuguesa (Autoridade Tributária, "AT"). Uma taxa de IVA errada ou uma razão de isenção em falta não é um bug cosmético — produz um documento fiscal incorreto que, uma vez emitido, só pode ser corrigido com uma nota de crédito. Acerte os campos fiscais antes de emitir.

As duas coisas com maior probabilidade de estarem erradas, e sobre as quais deve refletir:

1. Taxa de IVA (vatRate em cada artigo) — nunca a adivinhe. O escalão de taxa (NORMAL | INTERMEDIATE | REDUCED | EXEMPT) e a percentagem para a qual mapeia dependem do território (Continental / Açores / Madeira). A predefinição é NORMAL — REDUCED/INTERMEDIATE aplicam-se apenas a categorias legalmente definidas (certos alimentos, restaurantes, eventos culturais…). Se o utilizador não nomeou uma dessas categorias, use NORMAL e diga-o, ou pergunte; nunca aplique silenciosamente uma taxa reduzida. A tabela de percentagens por território está em domain.md.

2. Isenção de IVA requer um código de razão (vatExemptionReason). Se vatRate for EXEMPT, a lei portuguesa exige um motivo de isenção — um código M que estabelece a base legal. Defina sempre um; um artigo isento sem uma razão válida é um documento inválido e pode ser rejeitado quando emitir. O código deve refletir a razão real: regime de pequenos negócios → M10, atividade isenta por natureza (saúde/educação) → M07, B2B intracomunitário / reverse charge → M40/M16/M19. Se o utilizador estiver isento mas não souber o código, pergunte porquê e mapeie-o — não escolha arbitrariamente. A lista completa de códigos com referências legais está em domain.md.

Outras regras de domínio que vale a pena conhecer (detalhes em domain.md):

  • CAE é um código de atividade económica português de 5 dígitos que deve corresponder a um que a empresa tenha registado. Não o invente — pergunte ao utilizador, ou reutilize o CAE de uma fatura anterior. Deve ser um código atual (Rev. 4).
  • NIF (taxpayerNumber) é um número de contribuinte português de 9 dígitos com um dígito de controlo; obrigatório para B2B, opcional para consumidores finais. Para B2B intracomunitário, o número de IVA do cliente é relevante para o tratamento de reverse charge.
  • Retenção na fonte (retenção na fonte: IRS/IRC/IS) é configurada no artigo; a taxa comum é 25% para serviços de trabalhadores independentes. É retida pelo pagador, reduzindo o montante efetivamente pago.
  • Tipo de documento afeta as regras: uma SIMPLIFIED_INVOICE é para pequenas vendas a retalho e pode omitir dados completos do cliente; uma INVOICE para uma empresa com valor superior a €1.000 necessita do NIF do cliente.

Fluxo de trabalho

Siga estes passos. Salte 1–2 se o utilizador já lhe fornecer um id de cliente e ids de artigos.

Passo 1 — Localizar o cliente

Pesquise primeiro para evitar duplicados, depois crie se necessário.

Os exemplos usam o helper fiz (veja Configuração) — ele verifica o estado HTTP por si. O curl puro equivalente está no apêndice.

Encontrar por termo de pesquisa (nome, número de contribuinte, email):

fiz GET "/customers?search=Joao"

Criar um cliente (apenas name é obrigatório):

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

Guarde o id retornado — esse é o customerId para a fatura.

Passo 2 — Localizar os artigos (produtos / serviços)

Encontrar artigos existentes:

fiz GET "/items?search=consulting"

Reutilize um artigo existente apenas se os seus campos fiscais corresponderem à venda. Um resultado de pesquisa com o nome certo não é suficiente — para um documento fiscal, os campos vatRate, vatTerritory, vatExemptionReason, taxRate e de retenção do artigo devem corresponder ao tratamento que pretende para este cliente e venda. Se algum deles diferir (por exemplo, o mesmo serviço mas o cliente está isento, noutro território, ou sujeito a retenção), crie um novo artigo com os campos corretos em vez de reutilizar o que não corresponde. Em caso de dúvida, inspecione os campos do artigo encontrado e confirme com o utilizador.

Criar um artigo. Obrigatórios: name, type (PRODUCT | SERVICE), unitPrice, vatRate (NORMAL | INTERMEDIATE | REDUCED | EXEMPT):

fiz POST /items '{
  "name": "Hora de consultoria",
  "type": "SERVICE",
  "unitPrice": 80,
  "vatRate": "NORMAL"
}'

Guarde cada id retornado — esses são os ids dos artigos para a fatura.

unitPrice é o preço unitário; os totais por linha são calculados pela FIZ a partir da quantity que fornece na fatura. Escolha vatRate deliberadamente — veja a secção de domínio acima; a predefinição é NORMAL a menos que a categoria justifique claramente uma taxa reduzida. Se vatRate for EXEMPT, defina sempre um código M vatExemptionReason que corresponda a porque a venda está isenta (por exemplo, M10 para o regime de pequenos negócios). O campo é tecnicamente opcional na criação do artigo, mas um artigo isento sem uma razão válida está incorreto e pode ser rejeitado mais tarde quando emitir a fatura. Veja domain.md para a lista completa de códigos.

Passo 3 — Criar o rascunho da fatura

Campos obrigatórios: dueDate (ISO 8601), cae (código de atividade económica português), type, customerId e items (pelo menos um { id, quantity }).

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

A resposta inclui o novo id da fatura (necessário para emitir) e o status (DRAFT). A date da fatura é definida para agora e a currency é EUR pela API — não as envia.

Opcional — desconto global (summary):

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

Use "AMOUNT" com globalDiscountAmount para um desconto de valor fixo em alternativa.

Opcional — pagamento na criação (payment): apenas permitido quando type é INVOICE_RECEIPT ou SIMPLIFIED_INVOICE; a API retorna 400 caso contrário.

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

method é um de: cash, card, bankTransfer, mbWay, multibanco, spin, other.

Passo 4 — Emitir a fatura

Este é o passo vinculativo. Confirme com o utilizador, depois:

fiz POST /invoices/{id}/issue

A resposta contém o number oficial, status: ISSUED e um bloco syncWithAt. Verifique syncWithAt.status — se for FAILED, a fatura foi criada localmente mas não sincronizou com a autoridade fiscal; reporte atMessage e se isRetriable é verdadeiro ao utilizador em vez de reivindicar sucesso.

Passo 5 — Descarregar o PDF (opcional)

fiz GET /invoices/{id}/pdf

Retorna { id, name, url }; o url é uma hiperligação para descarregar o PDF. Adicione ?format=A4 ou ?format=RECEIPT para escolher o layout.

Tipos de documento (type)

Valores: INVOICE · INVOICE_RECEIPT · SIMPLIFIED_INVOICE · RECEIPT · CREDIT_NOTE · DEBIT_NOTE. A predefinição é INVOICE a menos que o utilizador peça um recibo ou fatura simplificada. Apenas INVOICE_RECEIPT e SIMPLIFIED_INVOICE aceitam um objeto payment na criação. Veja domain.md para o que cada tipo significa e quando usá-lo.

Referência de operações comuns

AçãoMétodo e caminho
Listar/pesquisar clientesGET /customers?search=…
Criar clientePOST /customers
Listar/pesquisar artigosGET /items?search=…
Criar artigoPOST /items
Criar rascunhoPOST /invoices
EmitirPOST /invoices/:id/issue
Listar faturasGET /invoices
Obter uma faturaGET /invoices/:id
Descarregar PDFGET /invoices/:id/pdf
Eliminar (rascunho)DELETE /invoices/:id

Tratamento de erros

Verifique sempre o estado HTTP, não apenas se o comando foi executado. Em caso de erro, a API retorna um corpo JSON de aspeto normal — { "statusCode": 400, "message": "...", "timestamp": "..." } — com o estado HTTP correspondente. Com curl -s puro, esse corpo é impresso e o curl ainda sai com código 0, por isso é fácil confundir um erro com um resultado. O helper fiz protege contra isto: imprime uma linha HTTP <code> e retorna um código diferente de zero em casos de 4xx/5xx. Se tiver de usar curl puro, adicione -w '\n%{http_code}' (ou -o body -w '%{http_code}') e inspecione o código.

Estados comuns:

  • 401 — chave de API ausente ou inválida. Diga ao utilizador para definir FIZ_API_KEY no seu ambiente (não no chat).
  • 400 — erro de validação; o message indica qual o campo. Casos comuns: items vazio, payment num type não suportado, um campo desconhecido/extra (lista de permissões estrita), dueDate malformado (deve ser ISO 8601 completo com fuso horário, por exemplo, 2026-07-18T00:00:00.000Z).
  • 5xx — problema no backend/AT; exponha-o, não repita a mesma escrita cegamente (uma criação pode ter sido parcialmente bem-sucedida — verifique primeiro com um GET).

Devolva sempre a message da API ao utilizador em vez de repetir cegamente.

O contrato completo e autoritativo de pedido/resposta é o Swagger UI em https://api.fiz.co/ — consulte-o se algum campo aqui parecer desatualizado.

Quando precisar de mais detalhes

Dois ficheiros complementares neste diretório da skill:

  • domain.md — o domínio fiscal português: taxas de IVA por território, a lista completa de códigos de isenção (M) e quando cada um se aplica, tipos de documento, regras de CAE/NIF, retenção na fonte e como funciona a sincronização com a AT / irreversibilidade da emissão. Leia-o sempre que uma decisão fiscal não for óbvia (especialmente ao escolher uma vatRate ou uma razão de isenção).
  • reference.md — a referência completa campo-a-campo da API (todos os campos de cliente/artigo/fatura, todos os valores de enum, formas completas de pedido/resposta).

Apêndice — curl puro (sem o helper)

Prefira o helper fiz acima; ele verifica o estado HTTP. Se não estiver disponível, use curl puro com -w '\n%{http_code}' (ou -o body -w '%{http_code}') e verifique o código antes de interpretar o corpo. Exemplo:

status=$(curl -s -o body -w '%{http_code}' -H "x-api-key: $FIZ_API_KEY" \
  "$FIZ_API_URL/customers")
if [ "$status" -ge 400 ]; then
  echo "Erro $status: $(cat body)"
else
  cat body
fi

Os exemplos ao longo do texto mostram o formato do corpo tal como seria com -s; adicione explicitamente -o e -w para capturar o estado.

Habilidades Relacionadas