API de Parceiros Fintra · v1

API de Parceiros Fintra — v1

Esta API deixa um sistema externo ler e criar dados de uma empresa que usa a Fintra: clientes, produtos, facturas e pagamentos.

É servidor-para-servidor. Não é uma API de browser, não é uma API de utilizador final, e não substitui a Fintra: há coisas que continuam a acontecer dentro da aplicação, por uma pessoa.

O que esta API faz, e o que não faz

Faz Não faz
Ler clientes, produtos, facturas e pagamentos Emitir facturas fiscais
Criar e alterar clientes Registar pagamentos
Criar e alterar produtos Apagar seja o que for
Criar facturas em rascunho Alterar ou cancelar facturas

Três limites permanentes da v1, e é melhor sabê-los antes de desenhar a integração:

  1. As facturas nascem em rascunho (status: "draft", invoice_code: "DRAFT-XXXXXXXX"). A emissão do documento fiscal é feita por uma pessoa, dentro da Fintra. Não existe nenhum endpoint de emissão — não o procure e não tente contorná-lo com outro verbo.
  2. Pagamentos são só de leitura. GET /payments e GET /payments/{id} existem; POST /payments não existe e devolve 404.
  3. Nada se apaga. Não há DELETE em nenhum recurso.

Base

https://api.fintra.co.mz/api/v1/partner

Todas as respostas são JSON, em UTF-8. Datas com hora vêm em ISO 8601 (2026-08-03T10:14:22+00:00); datas sem hora vêm como 2026-08-03.

Autenticação

Uma chave, num cabeçalho:

Authorization: Bearer fintra_sk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

O que precisa de saber sobre a chave:

Regra número um: a chave nunca vai para um browser

Não use esta API a partir de JavaScript numa página. Nem numa app móvel que embrulhe uma página, nem "só para o painel interno", nem "só em desenvolvimento".

Uma chave fintra_sk_live_... dentro de código que corre num browser é uma chave publicada: qualquer visitante abre o inspector e fica com os livros da empresa — clientes, facturas, valores.

A API impede-o, e sem lhe perguntar a opinião: qualquer pedido que traga o cabeçalho Origin leva 403 browser_use_forbidden antes de tocar em dados. Um fetch() de browser envia sempre Origin; um cliente de servidor nunca envia.

Chame a API a partir do seu servidor, guarde a chave numa variável de ambiente, e nunca a escreva num ficheiro versionado.

Modo de teste — a verdade

As chaves fintra_sk_test_... existem, mas hoje só lêem.

Não existe empresa-sandbox: uma chave de teste aponta para os dados reais da mesma empresa, e a única forma honesta de a manter inofensiva é bloquear-lhe a escrita. Qualquer POST ou PUT com uma chave de teste devolve 403 test_mode_read_only.

Consequência prática: para desenvolver a parte de escrita da sua integração precisa de uma chave live, e o que ela criar é real (rascunhos reais, clientes reais). Faça-o numa empresa de ensaio da sua conta, não na do cliente. Um sandbox com dados próprios está previsto, mas não existe — não desenhe a sua integração a contar com ele.

Idempotência — obrigatória em todas as escritas

Todo o POST e todo o PUT exigem o cabeçalho Idempotency-Key. Sem ele: 400 idempotency_key_required.

Idempotency-Key: 3f2a9c1e-7b44-4f0e-9a1d-6c5e2b8d0417

Porquê: se a rede falhar depois de a Fintra ter gravado, mas antes de a resposta lhe chegar, você não sabe se o registo existe. Com a chave, basta repetir o pedido igual, com a mesma chave: recebe a resposta original — o mesmo corpo e o mesmo estado HTTP (um POST repetido volta a responder 201, não 200) — mais o cabeçalho Idempotency-Replay: true, e não se cria um segundo registo.

Regras:

Gerar um UUID v4:

uuidgen                                        # macOS, Linux
$key = \Illuminate\Support\Str::uuid()->toString();   // PHP / Laravel
import uuid; key = str(uuid.uuid4())                  # Python

Paginação

Todas as listas são paginadas. Não existe forma de pedir "tudo".

Parâmetro Default Máximo
page 1 —
per_page 25 100

Pedir per_page=1000 não é erro: é reduzido a 100 em silêncio. Confirme sempre o per_page que vem na resposta em vez de assumir o que pediu.

"pagination": { "total": 214, "count": 25, "per_page": 25, "current_page": 1, "last_page": 9 }

Percorra até current_page == last_page. Para sincronizações incrementais use updated_since (clientes, produtos, facturas) em vez de reler tudo.

Ordenação

sort e order (asc | desc). Cada recurso aceita uma lista fechada de campos — um campo fora da lista devolve 422 invalid_query_parameter com a lista válida dentro do hint.

Erros

Todas as falhas — todas, sem excepção — têm a mesma forma:

{
  "error": {
    "code": "insufficient_scope",
    "message": "A chave não tem permissão para esta operação.",
    "hint": "Esta chave não inclui o scope \"invoices:write\". Peça ao suporte Fintra uma chave que o inclua.",
    "doc_url": "https://developers.fintra.co.mz/erros#insufficient_scope",
    "request_id": "0f1c2f26-9b7e-4a6b-8f2a-1d3c5b7e9a01"
  }
}

Escreva um parser de erros: error.code é estável e é por ele que deve ramificar o seu código. message e hint são para humanos e podem mudar. O request_id vem também no cabeçalho X-Request-Id — guarde-o nos seus logs, é por ele que o suporte Fintra encontra o pedido.

A lista completa está no catálogo de erros.

Ids de outra empresa dão 404

Um id que não existe, um id mal formado e um id de outra empresa devolvem exactamente o mesmo 404 resource_not_found. É deliberado: se a resposta distinguisse os casos, a API dizia "existe, mas não é seu" — e isso já é informação sobre os dados de outra empresa.

Portanto: um 404 não prova que o registo não existe. Prova que a sua chave não lhe chega.

Limites de tráfego

Por chave:

Limite Valor
Pedidos por minuto 60
Escritas por minuto 10
Pedidos por dia 5 000

Por empresa (somando todas as chaves dela): 120 pedidos/minuto. Pedidos sem chave válida, por IP: 30/minuto.

Ao exceder recebe 429 rate_limit_exceeded com o cabeçalho Retry-After em segundos. Respeite-o: espere esse tempo exacto e repita. Não repita em ciclo apertado — cada tentativa recusada conta na mesma para o limite e afasta-o do momento em que voltaria a passar.

Um cliente decente faz exponential backoff a partir do Retry-After e nunca mais de 5 tentativas.

Primeiro pedido

GET /ping confirma, de uma vez, que a chave é válida, qual a empresa a que ela pertence, em que modo está e que scopes tem:

curl -s https://api.fintra.co.mz/api/v1/partner/ping \
  -H "Authorization: Bearer $FINTRA_API_KEY"
{
  "data": {
    "tenant_id": 36,
    "tenant_name": "Bar Kanimambo",
    "mode": "live",
    "scopes": ["customers:read","customers:write","products:read","products:write","invoices:read","invoices:write","payments:read"],
    "customers_visible": 12,
    "request_id": "6a2f0f4e-0e40-4a0a-9c6a-2c1b8d5f7a10"
  }
}

Se isto responder, a integração pode começar. Continue nos endpoints.


A escrever a integração com a ajuda de uma IA? Dê-lhe primeiro o ficheiro llms.txt — traz estas regras em forma de instruções.