@veroao/node
v1.0.1
Published
Vero SDK for Node.js — faturação certificada AGT para Angola
Maintainers
Readme
@veroao/node
SDK oficial Vero para Node.js - emite facturas certificadas pela AGT sem sair do teu código.
Instalação
npm install @veroao/nodeRequer Node.js 18+ (usa fetch nativo, sem dependências).
Quick start
import { createVeroClient } from '@veroao/node'
const vero = createVeroClient() // lê VERO_API_KEY do ambiente
// ou: createVeroClient({ secretKey: 'vero_test_sk_...' })
const ORG_ID = process.env.VERO_ORG_ID!
// 1. Garante que o cliente existe (cria ou actualiza pelo teu ID externo)
const customer = await vero.customers.ensure(ORG_ID, {
externalId: 'user_123',
name: 'Maria Fernandes',
taxId: '003456789LA042',
phone: '923456789',
email: '[email protected]',
})
// 2. Emite a factura
const invoice = await vero.invoices.create(ORG_ID, {
customerId: customer.id,
items: [
{
description: 'Subscrição Mensal - Plano Pro',
quantity: 1,
unitPrice: 2500000, // 25 000,00 AOA em kwanzas × 100
taxRate: 14,
},
],
})
console.log(invoice.number) // "FT FT6326S62896N/1" - formato oficial AGT
console.log(invoice.pdfUrl) // link para descarregar o PDF (gerado na hora, não expira)
console.log(invoice.atcud) // código de verificação AGTA submissão à AGT acontece em segundo plano - invoice.agtStatus começa como pending e passa a
validated ou rejected alguns segundos depois. Consulta o documento novamente (vero.invoices.get)
para confirmar.
Ambiente test vs live
O ambiente é detectado automaticamente a partir do prefixo da chave - não precisas de configurar nada:
| Chave | Ambiente | Comportamento |
|--------------------|----------|-----------------------------------------------------------------------|
| vero_test_sk_... | test | Documentos gerados inteiramente em local, número marcado TESTE, nunca chegam à AGT, mesmo que a organização já tenha chaves de produção configuradas |
| vero_live_sk_... | live | Documentos certificados pela AGT em produção - valor fiscal real |
const vero = createVeroClient({ secretKey: 'vero_test_sk_...' })
console.log(vero.environment) // 'test'Existe também uma chave de sandbox pública e partilhada, sem necessidade de conta - pede-a a
GET https://api.vero.ao/v1/sandbox.
Referência da API
vero.customers
// Listar (paginado)
const { data, meta } = await vero.customers.list(orgId, { search: 'Maria', page: 1, limit: 20 })
// Obter por ID
const customer = await vero.customers.get(orgId, customerId)
// Criar
const customer = await vero.customers.create(orgId, {
name: 'Empresa XYZ',
taxId: '5000123456',
phone: '923456789',
email: '[email protected]',
addressLine1: 'Rua Amílcar Cabral, 45',
city: 'Luanda',
})
// Actualizar (campos parciais)
await vero.customers.update(orgId, customerId, { email: '[email protected]' })
// Upsert pelo externalId - idempotente, seguro chamar a cada pedido
const customer = await vero.customers.ensure(orgId, {
externalId: 'user_42',
name: 'João Baptista',
taxId: '123456789LA001',
})name e taxId são mutuamente opcionais - omite name se isConsumidorFinal: true ou se taxId
já identifica a empresa.
vero.invoices
// Emitir (idempotencyKey gerada automaticamente se omitida)
const invoice = await vero.invoices.create(orgId, {
customerId: 'uuid...',
documentType: 'FT', // FT = Factura (a prazo), FR = Factura-Recibo (pagamento imediato)
items: [{ description: 'Serviço', quantity: 1, unitPrice: 100000, taxRate: 14 }],
idempotencyKey: 'pedido_789', // passa o teu próprio para evitar duplicados em retries
})
// Listar (paginado, filtros opcionais)
const { data, meta } = await vero.invoices.list(orgId, { status: 'issued', documentType: 'FT', page: 1 })
// Obter por ID
const invoice = await vero.invoices.get(orgId, invoiceId)
// Cancelar (não elimina - fica com status "cancelled")
await vero.invoices.cancel(orgId, invoice.id, 'Pedido do cliente')
// Enviar por email ao cliente (usa o email guardado no cliente)
await vero.invoices.send(orgId, invoice.id)documentType: 'FR' fica com status: 'paid' imediatamente (já está pago); 'FT' fica 'issued'
até seres tu a marcar como pago via vero.receipts.create.
vero.proformas
const proforma = await vero.proformas.create(orgId, {
customerId: 'uuid...',
items: [{ description: 'Orçamento', quantity: 1, unitPrice: 500000, taxRate: 14 }],
validUntil: '2026-12-31',
})
// Converter em factura definitiva certificada - sem corpo, usa os dados já na proforma
const invoice = await vero.proformas.convert(orgId, proforma.id)
// Anular (só enquanto estiver em draft, não pode ser desfeito)
await vero.proformas.cancel(orgId, proforma.id)Proformas não têm valor fiscal - servem só de orçamento até seres convertidas em factura.
vero.creditNotes
// Emitir nota de crédito contra uma factura - cancela a factura original
const cn = await vero.creditNotes.create(orgId, invoice.id, 'Devolução parcial')
await vero.creditNotes.send(orgId, cn.id) // por email ao clientevero.debitNotes
// Autónoma ou associada a uma factura existente - ao contrário da NC, não exige factura de origem
const dn = await vero.debitNotes.create(orgId, {
customerId: customer.id,
items: [{ description: 'Custos administrativos', quantity: 1, unitPrice: 500000, taxRate: 14 }],
invoiceId: invoice.id, // opcional - usa o número real dessa factura como referência na AGT
})vero.receipts
// Marca uma factura FT como paga e emite o recibo (documento interno, não vai à AGT)
const receipt = await vero.receipts.create(orgId, invoice.id, { paymentMethod: 'transfer' })vero.products
const product = await vero.products.create(orgId, {
name: 'Plano Pro', // único campo obrigatório
code: 'PLAN-PRO', // opcional
unitPrice: 2500000,
taxRate: 14, // default 14 se omitido
unitOfMeasure: 'UN', // default 'UN' se omitido
})
const products = await vero.products.list(orgId) // devolve todos, sem paginação nem filtros
await vero.products.update(orgId, product.id, { unitPrice: 2700000 })
await vero.products.deactivate(orgId, product.id) // soft delete - fica active: falsevero.webhooks
const webhook = await vero.webhooks.register(orgId, {
url: 'https://teusite.ao/webhooks/vero',
events: ['invoice.issued', 'invoice.cancelled', 'proforma.converted', 'proforma.cancelled'],
})
console.log(webhook.secret) // segredo HMAC - só é mostrado nesta resposta, guarda-o
await vero.webhooks.list(orgId)
await vero.webhooks.delete(orgId, webhook.id)O payload enviado ao teu endpoint tem a forma { event, orgId, timestamp, data } - data é o próprio
documento (mesma forma da resposta da API). Para eventos de factura, data.pdfUrl é um link público,
sem necessidade de autenticação.
Tratamento de erros
import { VeroAPIError, VeroAuthError, VeroNotFoundError, VeroRateLimitError } from '@veroao/node'
try {
const invoice = await vero.invoices.create(orgId, input)
} catch (err) {
if (err instanceof VeroAuthError) {
// chave inválida ou revogada
} else if (err instanceof VeroNotFoundError) {
// organização ou cliente não encontrado
} else if (err instanceof VeroRateLimitError) {
// demasiados pedidos - err.retryAfter (segundos), se o servidor o indicou
} else if (err instanceof VeroAPIError) {
console.error(err.status, err.code, err.message)
}
}Erros de rede e respostas 5xx são automaticamente repetidos (até maxRetries, default 3, com
backoff exponencial) antes de lançarem excepção.
Configuração do cliente
const vero = createVeroClient({
secretKey: 'vero_live_sk_...',
baseUrl: 'https://api.vero.ao', // default
maxRetries: 3, // tentativas em erros 5xx/rede
timeout: 30_000, // ms por pedido
})Preços em kwanzas
Os valores monetários usam kwanzas × 100 para evitar problemas de vírgula flutuante:
unitPrice: 2_500_000 // 25 000,00 AOA
unitPrice: 150_050 // 1 500,50 AOAVariáveis de ambiente
| Variável | Descrição |
|--------------------|--------------------------------------------------------------------|
| VERO_API_KEY | Chave secreta da API (recomendado) |
| VERO_SECRET_KEY | Alias legado - usado só se VERO_API_KEY não estiver definida |
A implementar com Claude Code ou outro agente de IA?
Aponta o teu assistente para vero.ao/vero-skill.md - um skill com o essencial da API da Vero (autenticação, endpoints, formatos e erros comuns) pronto a carregar, sem teres de colar documentação à mão.
O teu backend não é Node?
Este pacote é só uma comodidade - por baixo é tudo pedidos HTTPS normais (Authorization: Bearer
vero_..._sk_..., corpo JSON). Se o teu backend é PHP, Python, Ruby, Java, Go, ou qualquer outra
linguagem, tens acesso a exactamente as mesmas operações chamando a API REST directamente com
o cliente HTTP dessa linguagem - nada fica limitado por não usares este SDK. Exemplos completos em
cURL, Python e PHP estão em vero.ao/docs.
