Endpoints
Base: https://api.fintra.co.mz/api/v1/partner
Nos exemplos, a chave está numa variável de ambiente — é onde ela deve estar:
export FINTRA_API_KEY="fintra_sk_live_..."
export FINTRA_API="https://api.fintra.co.mz/api/v1/partner"
São catorze rotas. Não há mais nenhuma.
| Método | Rota | Scope | O que faz |
|---|---|---|---|
| GET | /ping |
customers:read |
Confirma a chave e diz de que empresa é |
| GET | /customers |
customers:read |
Lista clientes |
| GET | /customers/{id} |
customers:read |
Um cliente |
| POST | /customers |
customers:write |
Cria um cliente |
| PUT | /customers/{id} |
customers:write |
Altera um cliente |
| GET | /products |
products:read |
Lista produtos |
| GET | /products/{id} |
products:read |
Um produto |
| POST | /products |
products:write |
Cria um produto |
| PUT | /products/{id} |
products:write |
Altera um produto |
| GET | /invoices |
invoices:read |
Lista facturas |
| GET | /invoices/{id} |
invoices:read |
Uma factura, com linhas |
| POST | /invoices |
invoices:write |
Cria uma factura em rascunho |
| GET | /payments |
payments:read |
Lista recebimentos |
| GET | /payments/{id} |
payments:read |
Um recebimento |
Não existe DELETE em lado nenhum, não existe PUT /invoices/{id}, e não existe
POST /payments. Pedidos a rotas inexistentes devolvem o 404 genérico do servidor, não o
envelope da API.
Nas escritas (POST, PUT) só são gravados os campos documentados. Um campo desconhecido não é
ignorado em silêncio: devolve 422 validation_failed a dizer qual é.
Os
Idempotency-Keyque aparecem nos exemplos são ilustrativos. Gere um UUID novo em cada pedido seu: reaproveitar um destes com outro corpo devolve409 idempotency_key_reuse.
GET /ping
O primeiro pedido de qualquer integração.
curl -s "$FINTRA_API/ping" -H "Authorization: Bearer $FINTRA_API_KEY"
{
"data": {
"tenant_id": 37,
"tenant_name": "Mercearia Bom Preço",
"mode": "live",
"scopes": ["customers:read","customers:write","products:read","products:write","invoices:read","invoices:write","payments:read"],
"customers_visible": 15,
"request_id": "e6498572-62f0-4e85-ac5a-d7b71eb56445"
}
}
mode diz live ou test; scopes diz o que a chave pode fazer. Verifique-os aqui em vez de
descobrir num 403 no meio do trabalho.
Clientes
GET /customers
Parâmetros: search (nome, email, NUIT ou telefone), type (individual | company),
updated_since (ISO 8601), page, per_page, sort (name, created_at, updated_at),
order. Por omissão: created_at desc.
curl -s "$FINTRA_API/customers?search=Talho&per_page=2" \
-H "Authorization: Bearer $FINTRA_API_KEY"
{
"data": [
{
"id": 990060,
"name": "Talho Nhamavila, Lda",
"type": "company",
"email": "geral@talhonhamavila.co.mz",
"phone": "+258842223344",
"contact_number": null,
"nuit": "400123456",
"address": "Av. 24 de Julho 1520, Maputo",
"created_at": "2026-08-03T10:57:14+00:00",
"updated_at": "2026-08-03T10:57:26+00:00"
}
],
"pagination": { "total": 1, "count": 1, "per_page": 2, "current_page": 1, "last_page": 1 }
}
GET /customers/{id}
curl -s "$FINTRA_API/customers/990060" -H "Authorization: Bearer $FINTRA_API_KEY"
Devolve {"data": { … }} com o mesmo objecto de cima. Um id de outra empresa devolve
404 resource_not_found — igual a um id que não existe.
POST /customers
Campos: name (obrigatório), type (individual | company), email, phone,
contact_number, nuit, address. O email, quando enviado, tem de ser único dentro da empresa.
curl -s -X POST "$FINTRA_API/customers" \
-H "Authorization: Bearer $FINTRA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c9e1a52-3f18-4b2c-9d47-8a0f6e2b1c34" \
-d '{
"name": "Talho Nhamavila, Lda",
"type": "company",
"nuit": "400123456",
"email": "geral@talhonhamavila.co.mz",
"phone": "+258841234567",
"address": "Av. 24 de Julho 1520, Maputo"
}'
HTTP 201.
{
"data": {
"id": 990060,
"name": "Talho Nhamavila, Lda",
"type": "company",
"email": "geral@talhonhamavila.co.mz",
"phone": "+258841234567",
"contact_number": null,
"nuit": "400123456",
"address": "Av. 24 de Julho 1520, Maputo",
"created_at": "2026-08-03T10:57:14+00:00",
"updated_at": "2026-08-03T10:57:14+00:00"
}
}
PUT /customers/{id}
Os mesmos campos, todos opcionais: envia-se só o que muda.
curl -s -X PUT "$FINTRA_API/customers/990060" \
-H "Authorization: Bearer $FINTRA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 2e6f4a90-1b83-4c7d-9e05-6f8a2b4c1d37" \
-d '{"phone": "+258842223344"}'
HTTP 200, com o cliente completo já actualizado.
Produtos
⚠️ Um produto criado ou alterado por aqui chega ao balcão: entra na sincronização do POS e aparece nos telemóveis e tablets da empresa sem passar por mais nenhum ecrã. É o comportamento desejado, mas convém saber que não há um passo de aprovação pelo meio.
GET /products
Parâmetros: search (nome, código ou SKU), type, is_for_sale (true/false),
updated_since, page, per_page, sort (name, price, stock, created_at, updated_at),
order.
curl -s "$FINTRA_API/products?search=Farinha&per_page=1" \
-H "Authorization: Bearer $FINTRA_API_KEY"
{
"data": [
{
"id": 990186,
"name": "Farinha de milho 5 kg",
"product_code": "GO-0009",
"sku": "FAR-MIL-5KG",
"description": null,
"type": "GOODS",
"unity": "saco",
"price": "475.00",
"pos_price": null,
"vat_included": false,
"tax_rate": null,
"stock": 0,
"reorder_level": 0,
"tracks_inventory": true,
"is_for_sale": true,
"categories": [],
"created_at": "2026-08-03T10:57:15+00:00",
"updated_at": "2026-08-03T10:57:40+00:00"
}
],
"pagination": { "total": 1, "count": 1, "per_page": 1, "current_page": 1, "last_page": 1 }
}
Valores monetários vêm como texto ("475.00"), de propósito: em meticais, um float de
JavaScript arredonda mal. Converta para decimal, nunca para vírgula flutuante.
GET /products/{id}
curl -s "$FINTRA_API/products/990186" -H "Authorization: Bearer $FINTRA_API_KEY"
POST /products
Obrigatórios: name, type, inventory, price.
Opcionais: description, sku, unity, pos_price, vat_included, reorder_level,
is_for_sale, is_for_purchase, tax_id.
inventory:INVENTORY(move stock) ouNON_INVENTORY(não move).tax_id: o id de uma taxa da empresa ou global. Se não enviar, o produto fica sem taxa. Os ids que servem são os que já vê emtax_ratenos seus produtos — não os invente.
curl -s -X POST "$FINTRA_API/products" \
-H "Authorization: Bearer $FINTRA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: b31d47f0-6c2a-4e91-8f55-2a7d9e0c4b18" \
-d '{
"name": "Farinha de milho 5 kg",
"type": "GOODS",
"inventory": "INVENTORY",
"price": 450,
"sku": "FAR-MIL-5KG",
"unity": "saco"
}'
HTTP 201, com o produto criado. O product_code (GO-0009) é atribuído pela Fintra.
PUT /products/{id}
curl -s -X PUT "$FINTRA_API/products/990186" \
-H "Authorization: Bearer $FINTRA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 9a1c5e30-7d24-4f8b-a063-1e5c7b9d2f46" \
-d '{"price": 475}'
HTTP 200. Não há forma de apagar um produto por esta API.
Facturas
GET /invoices
Parâmetros: status (draft, unpaid, partially_paid, paid, cancelled), customer_id,
date_from, date_to, updated_since, page, per_page,
sort (invoice_date, due_date, grand_total, created_at, updated_at), order.
Por omissão: invoice_date desc.
curl -s "$FINTRA_API/invoices?status=draft&per_page=1" \
-H "Authorization: Bearer $FINTRA_API_KEY"
Cada factura traz cabeçalho, totais, o cliente e as linhas. Nunca traz lançamentos contabilísticos, custos ou margens — isso não sai desta API.
GET /invoices/{id}
curl -s "$FINTRA_API/invoices/992168" -H "Authorization: Bearer $FINTRA_API_KEY"
{
"data": {
"id": 992168,
"invoice_code": "DRAFT-821FA1DA",
"document_type": "FACTURA",
"status": "draft",
"reference": "Encomenda 2026/114",
"invoice_date": "2026-08-03",
"due_date": "2026-09-02",
"currency": "MZN",
"exchange_rate": 1,
"subtotal": "4500.00",
"discount": "0.00",
"vat_base": "0.00",
"tax": "0.00",
"grand_total": "4500.00",
"paid_amount": "0.00",
"remaining_amount": "4500.00",
"customer": { "id": 990060, "name": "Talho Nhamavila, Lda" },
"items": [
{
"id": 3523,
"product_id": 990186,
"name": "Farinha de milho 5 kg",
"quantity": 10,
"price": "450.00",
"discount": "0.00",
"discount_percentage": 0,
"tax_amount": "0.00",
"tax_rate": null,
"tax_included": false,
"total": "4500.00"
}
],
"created_at": "2026-08-03T10:57:25+00:00",
"updated_at": "2026-08-03T10:57:25+00:00"
}
}
(Resposta abreviada: o objecto real traz ainda payment_notice_code, is_payment_notice,
notice_kind, is_pos_sale, payment_reference, global_discount_amount e notes.)
POST /invoices
Cria a factura em rascunho, e só isso.
Obrigatórios: customer_id, invoice_date, items (1 a 200 linhas).
Opcionais: due_date, reference, notes, currency (MZN, USD, ZAR), exchange_rate.
Cada linha aceita exactamente estes campos: product_id, name, description, quantity
(obrigatório), price (obrigatório), discount, tax_id. Qualquer outro é recusado com o índice
da linha na mensagem.
⚠️ Toda a linha tem de trazer um product_id. A v1 não suporta linhas de texto livre: uma
linha só com name (por exemplo "Serviço de entrega") não é aceite. Se precisa de facturar um
serviço, crie-o primeiro como produto (POST /products com "inventory": "NON_INVENTORY") e use o
id dele.
discount numa linha é uma percentagem (0 a 100), não um valor em meticais.
O price de cada linha é obrigatório e é o seu. A Fintra não vai buscar o preço ao produto:
se enviar product_id sem price, o pedido é recusado com 422 validation_failed. Leia o preço em
GET /products e envie-o — é o que lhe permite facturar um preço combinado diferente do de tabela.
curl -s -X POST "$FINTRA_API/invoices" \
-H "Authorization: Bearer $FINTRA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5d8b3c17-9a04-4e6f-b21d-7c3f0e9a8b52" \
-d '{
"customer_id": 990060,
"invoice_date": "2026-08-03",
"due_date": "2026-09-02",
"currency": "MZN",
"reference": "Encomenda 2026/114",
"items": [
{ "product_id": 990186, "quantity": 10, "price": 450 }
]
}'
HTTP 201, com exactamente o mesmo objecto que um GET /invoices/{id} devolveria.
Repare no que volta:
"status": "draft""invoice_code": "DRAFT-821FA1DA"
Isto não é uma factura fiscal. O número fiscal não foi consumido, o stock não se mexeu e não houve lançamento contabilístico nenhum. Alguém da empresa abre a Fintra, confere o rascunho e emite-o — e é aí que o documento passa a existir para as Finanças.
Não existe endpoint de emissão. Se a sua integração precisa de facturas emitidas automaticamente, essa conversa é com a Fintra, não com esta API.
Se o customer_id não for da empresa da chave, recebe 404 resource_not_found.
Pagamentos (só leitura)
Não existe forma de registar um pagamento por esta API. Estas duas rotas mostram os recebimentos contra factura que já existem na Fintra.
GET /payments
Parâmetros: invoice_id, date_from, date_to, page, per_page,
sort (payment_date, amount, created_at, updated_at), order.
Por omissão: payment_date desc.
curl -s "$FINTRA_API/payments?per_page=1" -H "Authorization: Bearer $FINTRA_API_KEY"
{
"data": [
{
"id": 990637,
"payment_code": "PA-2026-0375",
"payment_date": "2026-08-01",
"amount": "1572.00",
"payment_method": "Dinheiro",
"payment_method_type": "cash",
"reference": "Payment for POS Sale #991779",
"notes": null,
"invoice": { "id": 991779, "invoice_code": "FR-2026-0377" },
"created_at": "2026-08-01T22:26:37+00:00",
"updated_at": "2026-08-01T22:26:37+00:00"
}
],
"pagination": { "total": 377, "count": 1, "per_page": 1, "current_page": 1, "last_page": 377 }
}
Só saem recebimentos ligados a uma factura da empresa. Salários, pagamentos a fornecedores e qualquer outro movimento de tesouraria não aparecem aqui, e isso é deliberado.
GET /payments/{id}
curl -s "$FINTRA_API/payments/990637" -H "Authorization: Bearer $FINTRA_API_KEY"
Uma integração típica, por ordem
GET /ping— confirma chave, empresa e scopes.GET /products— traz o catálogo (pagine atélast_page).GET /customers?search=…— procura o cliente; se não existir,POST /customers.POST /invoices— cria o rascunho, comIdempotency-Key.- Uma pessoa emite o documento dentro da Fintra.
GET /invoices/{id}mais tarde — para ver ostatuse oinvoice_codefiscal.GET /payments?invoice_id=…— para saber se já foi pago.
Para sincronizar depois, use updated_since em vez de reler tudo: é o que mantém o seu consumo
dentro dos 5 000 pedidos por dia.
Todos os erros possíveis estão no catálogo de erros.