# Moloni API v2 - Reference ## Empresa Descomplicar | Campo | Valor | |-------|-------| | Company ID | 69473 | | Empresa | Evidente Despertar, Lda | | Client ID | evidentedespertar | | NIF | 516557498 | | MCP Server | `/opt/moloni/` em mcp-hub | | Service | `moloni.service` (systemd) | | .env | `/opt/moloni/.env` | | Token storage | `/opt/moloni/.moloni/69473/.token_metadata.json` | ## Sandbox (Desenvolvimento/Testes) Para testar integrações sem afectar dados reais: https://www.moloni.pt/dev/sandbox/ A sandbox fornece um ambiente isolado com dados fictícios. Usar sempre que: - Desenvolver novas integrações - Testar criação/modificação de documentos - Depurar erros de formato ou validação ## Autenticacao ### Password Grant (GET, nao POST!) ```python r = requests.get('https://api.moloni.pt/v2/grant/', params={ 'grant_type': 'password', 'client_id': 'evidentedespertar', 'client_secret': 'aa48e86bd574bf81ca2d7f624c8b5de0284fb97d', 'username': 'contabilidade@descomplicar.pt', 'password': '6EMVh^RHA)d5k!%s' }) token = r.json()['access_token'] ``` ### Refresh Token ```python r = requests.get('https://api.moloni.pt/v2/grant/', params={ 'grant_type': 'refresh_token', 'client_id': 'evidentedespertar', 'client_secret': 'aa48e86bd574bf81ca2d7f624c8b5de0284fb97d', 'refresh_token': '' }) ``` ### Regras de Autenticacao - Grant endpoint e **GET** (nao POST) - Password contem `%s` - em Python usar: `'6EMVh^RHA)d5k!%s'` (string literal, sem f-string) - Token expira em 3600 segundos (1 hora) - Refresh token nao expira mas so pode ser usado uma vez ## Formato das Chamadas API ### Regra Fundamental ``` access_token -> QUERY STRING (params) dados -> POST BODY (data, form-urlencoded) ``` ### Exemplo Correcto ```python r = requests.post('https://api.moloni.pt/v2/invoices/getAll/', params={'access_token': token}, # token na query string data={'company_id': '69473', 'qty': '50'} # dados no body ) ``` ### Exemplo INCORRECTO (causa 403 Forbidden) ```python # ERRADO - token no body causa 403! r = requests.post('https://api.moloni.pt/v2/invoices/getAll/', data={'company_id': '69473', 'access_token': token, 'qty': '50'} ) ``` ## Endpoints Case-Sensitive | Correcto (camelCase) | ERRADO (lowercase) | |---------------------|---------------------| | `creditNotes/` | `creditnotes/` | | `invoiceReceipts/` | `invoicereceipts/` | | `documentSets/` | `documentsets/` | | `paymentMethods/` | `paymentmethods/` | | `simplifiedInvoices/` | `simplifiedinvoices/` | | `debitNotes/` | `debitnotes/` | | `billsOfLading/` | `billsoflading/` | | `deliveryNotes/` | `deliverynotes/` | | `proFormaInvoices/` | `proformainvoices/` | Endpoints simples (lowercase OK): `invoices/`, `receipts/`, `customers/`, `products/`, `taxes/` Todos os endpoints precisam de trailing slash: `/v2/invoices/getAll/` ## Arrays Aninhados (Form-Encoded) ### Formato Correcto ```python data = { 'company_id': '69473', 'associated_documents[0][associated_id]': '595002190', 'associated_documents[0][value]': '2361.60', 'payments[0][payment_method_id]': '400878', 'payments[0][date]': '2026-02-05', 'payments[0][value]': '2361.60', } ``` ### Multiplos Items ```python data = { 'products[0][product_id]': '123', 'products[0][qty]': '2', 'products[0][price]': '50.00', 'products[1][product_id]': '456', 'products[1][qty]': '1', 'products[1][price]': '100.00', } ``` ### NUNCA Usar JSON.dumps() ```python # ERRADO - Moloni nao aceita JSON strings dentro de form data data = { 'associated_documents': json.dumps([{'associated_id': 123, 'value': 50}]) } ``` ## Restricoes de Datas - Moloni tem data minima para criacao de documentos (periodo de comunicacao AT) - Actualmente: **>= 2025-10-28** - Para documentos com datas antigas, usar a data actual no recibo/documento - Erro retornado: `["12 date >= 2025-10-28"]` ## Series Documentais | ID | Nome | Uso | |----|------|-----| | 630300 | D | Serie principal (facturas, recibos, NC) | | 118898 | (antiga) | Recibos antigos KCCG | ## Metodos de Pagamento | ID | Nome | |----|------| | 400868 | Numerario | | 400873 | Cheque | | 400878 | Transferencia Bancaria | | 400883 | Multibanco | | 1569000 | Paypal | ## Endpoints por Tipo de Documento Cada tipo de documento suporta: `count`, `getAll`, `getOne`, `insert`, `update`, `delete` ### Facturas (invoices/) ``` POST /v2/invoices/getAll/ - Listar (qty, offset, document_set_id, customer_id, year) POST /v2/invoices/getOne/ - Detalhe (document_id OU document_set_id+number) POST /v2/invoices/insert/ - Criar ``` ### Recibos (receipts/) ``` POST /v2/receipts/getAll/ - Listar POST /v2/receipts/getOne/ - Detalhe POST /v2/receipts/insert/ - Criar (requer: date, document_set_id, customer_id, net_value, associated_documents, payments) ``` **Insert fields:** - `company_id` (int, required) - `date` (YYYY-MM-DD, required) - `document_set_id` (int, required) - `customer_id` (int, required) - `net_value` (float, required) - valor total do recibo - `associated_documents[]` - array com `associated_id` e `value` - `payments[]` - array com `payment_method_id`, `date`, `value` - `status` (0=rascunho, 1=fechado) - `notes` (string, opcional) ### Notas de Credito (creditNotes/) ``` POST /v2/creditNotes/getAll/ POST /v2/creditNotes/getOne/ POST /v2/creditNotes/insert/ ``` ### Clientes (customers/) ``` POST /v2/customers/getAll/ POST /v2/customers/getOne/ POST /v2/customers/getBySearch/ POST /v2/customers/getByVat/ ``` ### Produtos (products/) ``` POST /v2/products/getAll/ POST /v2/products/getBySearch/ POST /v2/products/getByReference/ ``` ## Codigos de Erro Comuns | Codigo | Significado | |--------|-------------| | 200 + `{"valid": 1}` | Sucesso | | 200 + `["12 date >= ..."]` | Data anterior ao periodo AT | | 200 + `["10 associated_id"]` | Documento associado invalido ou ja reconciliado | | 200 + `["2 value 0 null"]` | Valor invalido ou em falta | | 401 | Token expirado ou invalido | | 403 | App sem permissoes (access_token no body em vez de query string) | | 404 | Endpoint nao encontrado (verificar case-sensitivity e trailing slash) | ## Campos de Resposta (Documentos) | Campo | Tipo | Descricao | |-------|------|-----------| | `document_id` | int | ID unico do documento | | `number` | int | Numero sequencial na serie | | `net_value` | float | **Total COM IVA** (Desk total). Para base sem IVA: net_value / 1.23. ATENCAO: nome enganador, NAO e base! | | `reconciled_value` | float | Valor ja reconciliado/pago | | `status` | int | 0=rascunho, 1=fechado | | `customer_id` | int | ID do cliente Moloni | | `document_set_id` | int | ID da serie documental | | `associated_documents` | array | Documentos associados (facturas num recibo) | | `reverse_associated_documents` | array | Documentos que referenciam este (recibos de uma factura) | | `payments` | array | Pagamentos registados | | `entity_name` | string | Nome do cliente | | `entity_vat` | string | NIF do cliente | ## MCP Tools Disponiveis O MCP Moloni expoe todas as operacoes via ferramentas prefixadas `mcp__moloni__moloni_`: ``` mcp__moloni__moloni_documents_invoices_getall mcp__moloni__moloni_documents_invoices_getone mcp__moloni__moloni_documents_receipts_insert mcp__moloni__moloni_documents_credit_notes_getall mcp__moloni__moloni_entities_customers_getall mcp__moloni__moloni_settings_paymentmethods_getall mcp__moloni__moloni_settings_documentsets_getall ``` **Nota:** O MCP server ja coloca o `access_token` na query string automaticamente. Quando usar via MCP, nao e necessario gerir tokens manualmente. ## Mapeamento Clientes Moloni-Desk | Moloni customer_id | Nome Moloni | Desk client_id | Nome Desk | |-------------------|-------------|----------------|-----------| | 75731733 | Missao Pertinente | 10 | MIP | | 84748539 | KCCG - Karate Clube de Gaia | 2 | KCG | | 90214681 | IgnitionVortex | 14 | IGV | | 90360913 | Prestigebrokers | 176 | Prestigebrokers | | 90861853 | Cristina Pinto Santos Lda | 17 | SNT Sintricare | | 96914420 | Vasco Miguel Neves, Unipessoal | 21 | Solar FV | | 97615615 | TECIRRIGATION | 22 | TECIRRIGATION | | 98881975 | Miguel Carril | 23 | MCR | | 99321870 | Espiral Senior | 24 | Espiral Senior | | 99581273 | Alegria Todo o Dia | 25 | Alegria Todo o Dia | | 100711528 | ROSSANA FERREIRA | 26 | Family Clinic | | 105780051 | AQUISEVENDE | 28 | AQUISEVENDE | | 128784439 | Carstuff Artigos Para Carrocaria | 139 | CTF Carstuff | ## Sincronizacao Desk-Moloni (Estado 2026-02-05) | Tipo | Total Serie D | Sincronizados | |------|--------------|---------------| | Facturas | 107 | 107 importadas no Desk | | Notas de Credito | 3 | 3 importadas no Desk | | Recibos | 112 | 91 existentes + 21 criados | | Pagamentos | 113 | 110 serie D + 3 serie 118898 | | PDFs | 220 | 107 FT + 110 RC + 3 NC (330 registos: invoice+customer) | Status facturas: - Paid: 98 | Partially Paid: 2 (D/36, D/84) | Unpaid: 3 (D/98, D/105, D/106) - Cancelled: 2 (D/56, D/88) | Draft: 2 (D/18, D/66) Excluidas da sincronizacao: - FT D/8, D/56, D/88 - anuladas por notas de credito - FT D/98, D/105, D/106 - nao cobradas - FT D/18, D/66 - rascunhos - FT D/3, D/4, D/5 - recibos na serie 118898 (nao D)