# Endpoints

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

Nos exemplos, a chave está numa variável de ambiente — é onde ela deve estar:

```bash
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-Key` que aparecem nos exemplos são ilustrativos. **Gere um UUID novo** em cada
> pedido seu: reaproveitar um destes com outro corpo devolve `409 idempotency_key_reuse`.

---

## GET /ping

O primeiro pedido de qualquer integração.

```bash
curl -s "$FINTRA_API/ping" -H "Authorization: Bearer $FINTRA_API_KEY"
```

```json
{
  "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`.

```bash
curl -s "$FINTRA_API/customers?search=Talho&per_page=2" \
  -H "Authorization: Bearer $FINTRA_API_KEY"
```

```json
{
  "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}

```bash
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.

```bash
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.**

```json
{
  "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.

```bash
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`.

```bash
curl -s "$FINTRA_API/products?search=Farinha&per_page=1" \
  -H "Authorization: Bearer $FINTRA_API_KEY"
```

```json
{
  "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}

```bash
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) ou `NON_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ê em `tax_rate` nos seus produtos — não os invente.

```bash
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}

```bash
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`.

```bash
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}

```bash
curl -s "$FINTRA_API/invoices/992168" -H "Authorization: Bearer $FINTRA_API_KEY"
```

```json
{
  "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.

```bash
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`.

```bash
curl -s "$FINTRA_API/payments?per_page=1" -H "Authorization: Bearer $FINTRA_API_KEY"
```

```json
{
  "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}

```bash
curl -s "$FINTRA_API/payments/990637" -H "Authorization: Bearer $FINTRA_API_KEY"
```

---

## Uma integração típica, por ordem

1. `GET /ping` — confirma chave, empresa e scopes.
2. `GET /products` — traz o catálogo (pagine até `last_page`).
3. `GET /customers?search=…` — procura o cliente; se não existir, `POST /customers`.
4. `POST /invoices` — cria o rascunho, com `Idempotency-Key`.
5. Uma pessoa emite o documento dentro da Fintra.
6. `GET /invoices/{id}` mais tarde — para ver o `status` e o `invoice_code` fiscal.
7. `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](./erros)**.
