SDK Node.js
A biblioteca oficial Vero para integrações backend. Emite facturas certificadas pela AGT directamente do teu servidor - sem burocracia, com total segurança de tipos.
Ambiente
Node.js 18+ · Bun · Deno
Compliance
DFe + ATCUD + JWS RS256 automáticos
Resiliência
Retry automático · Idempotência
Instalação
npm install @veroao/nodepnpm add @veroao/nodeInicialização
Cria uma instância do cliente uma vez e reutiliza-a em toda a aplicação. O ambiente (test/live) é detectado automaticamente pelo prefixo da chave - não precisas de configurar nada extra.
import { createVeroClient } from '@veroao/node'
// Lê VERO_API_KEY do ambiente automaticamente
export const vero = createVeroClient()
// Ou passa a chave explicitamente
export const vero = createVeroClient({
// Teste: vero_test_sk_... → AGT HML (sem valor fiscal)
// Produção: vero_live_sk_... → AGT produção (valor fiscal real)
secretKey: process.env.VERO_API_KEY!,
timeout: 10000, // ms - default: 30 000
maxRetries: 3, // tentativas em erro 5xx - default: 3
})
console.log(vero.environment) // 'test' ou 'live' - detectado do prefixo da chaveEmitir uma Factura
Uma factura precisa de três elementos: organização, cliente e produto. O fluxo recomendado usa ensure para garantir que o cliente existe sem criar duplicados, e referencia produtos do teu catálogo para manter consistência.
Garantir que o cliente existe (cria ou actualiza pelo externalId)
Garantir que o produto existe no catálogo
Emitir a factura com cliente + produto
import { vero } from '@/lib/vero'
const ORG_ID = process.env.VERO_ORG_ID!
// 1. Garantir que o cliente existe
// ensure() faz upsert pelo externalId - seguro chamar a cada pedido
const customer = await vero.customers.ensure(ORG_ID, {
externalId: 'user_123', // ID do utilizador no teu sistema
name: 'Maria Fernandes',
taxId: '003456789LA042',
phone: '923456789',
email: 'maria@empresa.ao',
addressLine1: 'Rua Amílcar Cabral, 45',
city: 'Luanda',
})
// 2. Garantir que o produto existe no catálogo
// Podes criar uma vez e guardar o ID, ou referenciar sempre pelo código
const product = await vero.products.create(ORG_ID, {
code: 'PLAN-PRO', // código único por organização
name: 'Plano Pro - Mensal',
description: 'Subscrição mensal do plano Pro',
unitPrice: 2500000, // 25 000,00 AOA em kwanzas × 100
taxRate: 14, // IVA Angola: 0 | 5 | 14
unitOfMeasure: 'UN',
})
// 3. Emitir a factura
const invoice = await vero.invoices.create(ORG_ID, {
customerId: customer.id,
documentType: 'FT', // FT = Factura | FR = Factura-Recibo
items: [
{
description: product.name,
quantity: 1,
unitPrice: product.unitPrice,
taxRate: product.taxRate,
productId: product.id, // liga ao catálogo (opcional mas recomendado)
},
],
// idempotencyKey gerada automaticamente - passa a tua para evitar duplicados
idempotencyKey: `pedido_${orderId}`,
})
console.log(invoice.number) // "FT FT6326S62896N/1" - número AGT certificado
console.log(invoice.atcud) // "FT7826S214N-1" - código de verificação AGT
console.log(invoice.jwsSignature) // assinatura RS256 submetida à AGT
console.log(invoice.pdfUrl) // URL do PDF com QR code e ATCUDRecursos
Todos os métodos recebem primeiro o orgId. Os detalhes de cada campo estão na página da API.
| vero.customers | list · get · create · update · ensure |
| vero.invoices | create · list · get · cancel · send · advances |
| vero.proformas | create · list · get · update · convert · advance · cancel |
| vero.recurringInvoices | create · list · get · update · pause · resume · end · pending · issue · skip |
| vero.creditNotes | create · list · get · send |
| vero.debitNotes | create · list · get · send · cancel |
| vero.receipts | create · createForDebitNote · list · get · send · cancel |
| vero.products | list · create · update · deactivate |
| vero.webhooks | register · list · delete |
Valores Monetários
Todos os valores usam kwanzas × 100 (inteiros) para evitar erros de vírgula flutuante. Multiplica o valor em AOA por 100.
// 25 000,00 AOA
unitPrice: 2500000
// 1 500,50 AOA
unitPrice: 150050
// Converter de AOA para o formato da API
const toKwanzas = (aoa: number) => Math.round(aoa * 100)
toKwanzas(25000) // → 2500000
// Converter da API para exibição
const fromKwanzas = (k: number) => (k / 100).toFixed(2)
fromKwanzas(2500000) // → "25000.00"Tipos TypeScript
import type {
Invoice,
InvoiceStatus, // 'pending' | 'issued' | 'paid' | 'failed' | 'cancelled'
DocumentType, // 'FT' | 'FR' | 'FA'
Customer,
Product,
ProformaInvoice,
CreditNote, // kind: 'cancel' | 'advance'
Receipt,
OpenAdvance, // invoices.advances()
RecurringInvoice, // recurringInvoices.*
Webhook,
WebhookEvent,
CreateInvoiceInput,
CreateCustomerInput,
EnsureCustomerInput,
CreateProductInput,
InvoiceItem,
ListResult,
} from '@veroao/node'Tratamento de Erros
O SDK lança erros tipados - usa instanceof para tratar cada caso de forma granular.
import {
VeroAPIError,
VeroAuthError,
VeroNotFoundError,
VeroRateLimitError,
} from '@veroao/node'
try {
const invoice = await vero.invoices.create(ORG_ID, input)
} catch (err) {
if (err instanceof VeroAuthError) {
// Chave inválida ou revogada → verificar VERO_API_KEY
} else if (err instanceof VeroNotFoundError) {
// Cliente ou organização não existe
} else if (err instanceof VeroRateLimitError) {
// Limite excedido - err.retryAfter tem os segundos a aguardar
await sleep(err.retryAfter ?? 60)
} else if (err instanceof VeroAPIError) {
// Erro da API: err.status (HTTP), err.code (string), err.message
console.error(err.status, err.code)
}
}Idempotência
O SDK gera uma idempotencyKey automaticamente em cada invoices.create(). Para garantir que um pedido repetido (ex: timeout + retry) não cria uma segunda factura, passa a tua própria chave ligada ao ID do pedido.
// Garante que o pedido #789 só gera uma factura, mesmo com retries
const invoice = await vero.invoices.create(ORG_ID, {
customerId: customer.id,
items: [...],
idempotencyKey: `order_${order.id}`,
})Segurança
A chave vero_live_sk_... tem acesso total à conta. Usa sempre variáveis de ambiente e nunca a exponhas no frontend, em logs ou em repositórios públicos.