API REST
L'API REST di Frihet consente di accedere e manipolare programmaticamente le risorse del tuo account. Tutta la comunicazione avviene su HTTPS e le risposte sono JSON.
URL base
https://api.frihet.io/v1
Tutti gli endpoint descritti in questa pagina sono relativi a questo URL base.
Discovery: Una richiesta GET alla radice (https://api.frihet.io/) restituisce i collegamenti principali senza necessità di autenticazione:
{
"name": "Frihet API",
"version": "1.0.0",
"docs": "https://docs.frihet.io/desarrolladores/api-rest",
"openapi": "https://api.frihet.io/openapi.yaml",
"mcp": "https://mcp.frihet.io",
"status": "https://status.frihet.io"
}
La specifica OpenAPI 3.1 è disponibile su https://api.frihet.io/openapi.yaml.
Se usi TypeScript o JavaScript, l'SDK ufficiale semplifica l'integrazione:
npm install @frihet/sdk
import Frihet from '@frihet/sdk';
const frihet = new Frihet({ apiKey: 'fri_...' });
const invoices = await frihet.invoices.list({ status: 'overdue' });
Repository: github.com/Frihet-io/frihet-sdk
Autenticazione
Ogni richiesta deve includere una chiave API nell'header X-API-Key. Le chiavi vengono create da Impostazioni > Sviluppatori > Chiavi API nel pannello di Frihet.
Le chiavi hanno il prefisso fri_ seguito da 32 byte casuali codificati in base64url. Esempio: fri_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345678.
curl https://api.frihet.io/v1/clients \
-H "X-API-Key: fri_tu-clave-aqui"
In alternativa, puoi inviare la chiave come Bearer token nell'header Authorization:
curl https://api.frihet.io/v1/clients \
-H "Authorization: Bearer fri_tu-clave-aqui"
Sicurezza delle chiavi
- La chiave in testo semplice viene mostrata una sola volta, al momento della creazione. Non può essere recuperata in seguito.
- Il server memorizza un hash SHA-256 della chiave. Anche in caso di violazione dei dati, la chiave originale non è recuperabile.
- Puoi creare chiavi con data di scadenza configurabile.
- Se sospetti che una chiave sia stata compromessa, revocala immediatamente dal pannello.
Ciclo di vita delle chiavi
Crea una chiave:
- Vai su Impostazioni > Sviluppatori > Chiavi API
- Premi Crea chiave
- Assegna un nome descrittivo (ad esempio,
integrazione-contabilità) - Opzionalmente, imposta una data di scadenza in giorni. Se lo lasci vuoto, la chiave non scade
- Copia la chiave immediatamente -- non potrai più vederla
Scadenza:
Le chiavi con data di scadenza smettono di funzionare automaticamente alla scadenza. Le richieste con una chiave scaduta ricevono un 401 Unauthorized.
Revoca:
Puoi revocare una chiave in qualsiasi momento da Impostazioni > Sviluppatori > Chiavi API. La revoca è immediata e irreversibile: le richieste in corso con quella chiave falliranno da quell'istante.
Rotazione delle chiavi:
Per ruotare una chiave senza interrompere il servizio:
- Crea una nuova chiave con lo stesso ambito
- Aggiorna la tua integrazione per usare la nuova chiave
- Verifica che le richieste funzionino correttamente
- Revoca la chiave precedente
Monitoraggio:
Ogni chiave mostra la data di ultimo utilizzo nel pannello Impostazioni. Controlla periodicamente le chiavi inattive e revoca quelle che non sono più utilizzate.
Rate limiting
Ogni chiave API ha un limite di 100 richieste al minuto. Se superato, l'API risponde con un codice 429:
{
"error": "Rate limit exceeded",
"message": "Maximum 100 requests per minute",
"retryAfter": 60
}
Header di rate limiting
Tutte le risposte dell'API includono header per consentirti di gestire il limite in modo proattivo:
| Header | Descrizione | Esempio |
|---|---|---|
X-RateLimit-Limit | Richieste consentite al minuto | 100 |
X-RateLimit-Remaining | Richieste rimanenti nella finestra attuale | 87 |
X-RateLimit-Reset | Timestamp Unix (secondi) in cui la finestra si riavvia | 1709312400 |
Esempio di risposta con header di rate limiting:
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1709312400
Content-Type: application/json
Gestione del codice 429:
Quando ricevi un 429, usa gli header per calcolare quanto aspettare prima di riprovare:
async function fetchWithRateLimit(url, options) {
const response = await fetch(url, options);
if (response.status === 429) {
const resetTimestamp = response.headers.get('X-RateLimit-Reset');
const waitMs = (Number(resetTimestamp) * 1000) - Date.now();
await new Promise(resolve => setTimeout(resolve, Math.max(waitMs, 1000)));
return fetch(url, options);
}
return response;
}
Raccomandazioni:
- Se ricevi un
429, attendi fino al timestamp indicato inX-RateLimit-Resetprima di riprovare. - Monitora
X-RateLimit-Remainingper rallentare le richieste prima di raggiungere il limite. - Distribuisci le richieste nel tempo anziché inviare raffiche.
Dimensione della richiesta
Il corpo delle richieste POST, PUT e PATCH non può eccedere 1 MB. Richieste maggiori ricevono un codice 413.
Risorse
L'API espone 7 risorse principali: invoices, expenses, clients, products, quotes, vendors e webhooks. Tutti supportano operazioni CRUD complete (GET, POST, PUT/PATCH, DELETE). Inoltre, i clienti hanno sottocollezioni CRM: contacts, activities e notes.
Sia PUT che PATCH accettano aggiornamenti parziali. Non è necessario inviare l'intera risorsa -- solo i campi che desideri modificare.
Fatture (/invoices)
Elencare fatture
GET /v1/invoices
Parametri di query:
| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
limit | integer | 50 | Risultati per pagina (massimo 100) |
offset | integer | 0 | Numero di risultati da saltare (massimo 10.000) |
status | string | -- | Filtra per stato: bozza, inviata, pagata, scaduta, annullata |
from | string | -- | Data inizio (ISO 8601: YYYY-MM-DD). Filtra per issueDate |
to | string | -- | Data fine (ISO 8601: YYYY-MM-DD). Filtra per issueDate |
Esempio:
curl "https://api.frihet.io/v1/invoices?limit=10&status=paid&from=2026-01-01&to=2026-03-31" \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta (200):
{
"data": [
{
"id": "abc123",
"clientName": "Acme S.L.",
"items": [
{ "description": "Consultoria", "quantity": 10, "unitPrice": 75 }
],
"status": "pagata",
"issueDate": "2026-01-15",
"dueDate": "2026-02-15",
"taxRate": 21,
"notes": "",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-20T14:00:00.000Z"
}
],
"total": 42,
"limit": 10,
"offset": 0
}
Ottenere fattura
GET /v1/invoices/:id
curl https://api.frihet.io/v1/invoices/abc123 \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta (200):
{
"id": "abc123",
"clientName": "Acme S.L.",
"items": [
{ "description": "Consultoria", "quantity": 10, "unitPrice": 75 }
],
"status": "pagata",
"issueDate": "2026-01-15",
"dueDate": "2026-02-15",
"taxRate": 21,
"notes": "",
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-20T14:00:00.000Z"
}
Creare fattura
POST /v1/invoices
Campi richiesti:
| Campo | Tipo | Descrizione |
|---|---|---|
clientName | string | Nome del cliente (max 10.000 caratteri) |
items | array | Elenco delle righe della fattura. Ogni riga: { description, quantity, unitPrice } |
Campi opzionali:
| Campo | Tipo | Descrizione |
|---|---|---|
status | string | bozza (predefinito), inviata, pagata, scaduta, annullata |
issueDate | string | Data di emissione (ISO 8601). Predefinito: oggi |
dueDate | string | Data di scadenza (ISO 8601) |
notes | string | Note interne (max 10.000 caratteri) |
taxRate | number | Percentuale di imposta (0-100). Es: 21 per IVA 21% |
Struttura di ogni riga (items[]):
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
description | string | Sì | Descrizione del concetto (max 10.000 caratteri) |
quantity | number | Sì | Quantità |
unitPrice | number | Sì | Prezzo unitario |
curl -X POST https://api.frihet.io/v1/invoices \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"clientName": "Acme S.L.",
"items": [
{ "description": "Desarrollo web", "quantity": 40, "unitPrice": 60 }
],
"dueDate": "2026-03-01",
"taxRate": 21,
"notes": "Proyecto Q1 2026"
}'
Risposta (201):
{
"id": "def456",
"clientName": "Acme S.L.",
"items": [
{ "description": "Desarrollo web", "quantity": 40, "unitPrice": 60 }
],
"status": "bozza",
"issueDate": "2026-02-12",
"dueDate": "2026-03-01",
"taxRate": 21,
"notes": "Proyecto Q1 2026",
"createdAt": "2026-02-12T09:00:00.000Z",
"updatedAt": "2026-02-12T09:00:00.000Z"
}
Aggiornare fattura (PUT o PATCH)
PUT /v1/invoices/:id
PATCH /v1/invoices/:id
Devi inviare solo i campi che desideri modificare. I campi non inclusi rimangono invariati.
curl -X PATCH https://api.frihet.io/v1/invoices/def456 \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"status": "inviata",
"notes": "Inviata al cliente"
}'
Risposta (200): Oggetto fattura aggiornato.
Se invii items, devi inviare l'array completo -- non sono ammessi aggiornamenti parziali di singole righe.
Eliminare fattura
DELETE /v1/invoices/:id
Risposta: 204 No Content
Scaricare fattura in PDF
GET /v1/invoices/:id/pdf
Restituisce il PDF della fattura come application/pdf con l'header Content-Disposition: attachment.
curl -o fattura.pdf https://api.frihet.io/v1/invoices/abc123/pdf \
-H "X-API-Key: fri_tu-clave-aqui"
Inviare fattura via email
POST /v1/invoices/:id/send
Invia la fattura al destinatario specificato tramite Resend. Se la fattura è in stato bozza, viene automaticamente aggiornata a inviata.
Campi:
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
recipientEmail | string | Sì | Email del destinatario (max 255 caratteri) |
recipientName | string | No | Nome del destinatario (max 200 caratteri) |
customMessage | string | No | Messaggio personalizzato nel corpo dell'email (max 5.000 caratteri) |
locale | string | No | Lingua dell'email: es (predefinito) o en |
curl -X POST https://api.frihet.io/v1/invoices/abc123/send \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"recipientEmail": "admin@acme.es",
"recipientName": "Departamento de Contabilidad",
"locale": "es"
}'
Risposta (200):
{ "success": true, "messageId": "re_abc123..." }
Contrassegnare fattura come pagata
POST /v1/invoices/:id/paid
| Campo | Tipo | Richiesto | Descrizione |
|---|---|---|---|
paidDate | string | No | Data di pagamento (ISO 8601). Predefinito: oggi |
curl -X POST https://api.frihet.io/v1/invoices/abc123/paid \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{ "paidDate": "2026-03-15" }'
Risposta (200):
{ "success": true, "status": "paid", "paidAt": "2026-03-15" }
Spese (/expenses)
Elencare spese
GET /v1/expenses
Parametri di query:
| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
limit | integer | 50 | Risultati per pagina (massimo 100) |
offset | integer | 0 | Numero di risultati da saltare |
from | string | -- | Data inizio (ISO 8601). Filtra per date |
to | string | -- | Data fine (ISO 8601). Filtra per date |
curl "https://api.frihet.io/v1/expenses?limit=20&from=2026-01-01" \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta (200):
{
"data": [
{
"id": "exp789",
"description": "Licencia Adobe Creative Cloud",
"amount": 59.99,
"category": "software",
"date": "2026-02-01",
"vendor": "Adobe Inc.",
"taxDeductible": true,
"createdAt": "2026-02-01T10:00:00.000Z",
"updatedAt": "2026-02-01T10:00:00.000Z"
}
],
"total": 15,
"limit": 20,
"offset": 0
}
Ottenere spesa
GET /v1/expenses/:id
Creare spesa
POST /v1/expenses
Campi richiesti:
| Campo | Tipo | Descrizione |
|---|---|---|
description | string | Descrizione della spesa (max 10.000 caratteri) |
amount | number | Importo della spesa |
Campi opzionali:
| Campo | Tipo | Descrizione |
|---|---|---|
category | string | Categoria della spesa (max 10.000 caratteri) |
date | string | Data della spesa (ISO 8601). Predefinito: oggi |
vendor | string | Fornitore (max 10.000 caratteri) |
taxDeductible | boolean | Se la spesa è deducibile |
curl -X POST https://api.frihet.io/v1/expenses \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"description": "Licencia Adobe Creative Cloud",
"amount": 59.99,
"category": "software",
"date": "2026-02-01",
"vendor": "Adobe Inc.",
"taxDeductible": true
}'
Risposta (201):
{
"id": "exp789",
"description": "Licencia Adobe Creative Cloud",
"amount": 59.99,
"category": "software",
"date": "2026-02-01",
"vendor": "Adobe Inc.",
"taxDeductible": true,
"createdAt": "2026-02-12T09:15:00.000Z",
"updatedAt": "2026-02-12T09:15:00.000Z"
}
Aggiornare spesa (PUT o PATCH)
PUT /v1/expenses/:id
PATCH /v1/expenses/:id
curl -X PATCH https://api.frihet.io/v1/expenses/exp789 \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{ "amount": 65.99, "taxDeductible": false }'
Risposta (200): Oggetto spesa aggiornato.
Eliminare spesa
DELETE /v1/expenses/:id
Risposta: 204 No Content
Clienti (/clients)
Elencare clienti
GET /v1/clients
Accetta limit, offset, from e to (filtra per createdAt).
curl "https://api.frihet.io/v1/clients?limit=50" \
-H "X-API-Key: fri_tu-clave-aqui"
Ottenere cliente
GET /v1/clients/:id
Creare cliente
POST /v1/clients
Campi richiesti:
| Campo | Tipo | Descrizione |
|---|---|---|
name | string | Nome del cliente (max 10.000 caratteri) |
Campi opzionali:
| Campo | Tipo | Descrizione |
|---|---|---|
email | string | Email di contatto |
phone | string | Telefono |
taxId | string | NIF/CIF/VAT |
address | object | Indirizzo (vedi struttura sotto) |
Struttura di address:
| Campo | Tipo | Descrizione |
|---|---|---|
street | string | Via e numero |
city | string | Città |
state | string | Provincia o stato |
postalCode | string | Codice postale |
country | string | Paese |
Tutti i campi di address sono opzionali.
curl -X POST https://api.frihet.io/v1/clients \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme S.L.",
"email": "admin@acme.es",
"taxId": "B12345678",
"address": {
"street": "Calle Gran Via 42",
"city": "Madrid",
"postalCode": "28013",
"country": "ES"
}
}'
Risposta (201):
{
"id": "cli001",
"name": "Acme S.L.",
"email": "admin@acme.es",
"taxId": "B12345678",
"address": {
"street": "Calle Gran Via 42",
"city": "Madrid",
"postalCode": "28013",
"country": "ES"
},
"createdAt": "2026-02-12T09:30:00.000Z",
"updatedAt": "2026-02-12T09:30:00.000Z"
}
Aggiornare cliente (PUT o PATCH)
PUT /v1/clients/:id
PATCH /v1/clients/:id
curl -X PATCH https://api.frihet.io/v1/clients/cli001 \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{ "phone": "+34 912 345 678" }'
Risposta (200): Oggetto cliente aggiornato.
Eliminare cliente
DELETE /v1/clients/:id
Risposta: 204 No Content
CRM: Persone di contatto, Attività e Note
I clienti hanno tre sottocollezioni per gestire le relazioni CRM: persone di contatto, attività e note. Tutti gli endpoint richiedono un clientId valido nell'URL.
Persone di contatto (/v1/clients/:id/contacts)
Elencare contatti
GET /v1/clients/:id/contacts
curl "https://api.frihet.io/v1/clients/cli001/contacts" \
-H "X-API-Key: fri_tu-clave-aqui"
Ottenere contatto
GET /v1/clients/:id/contacts/:contactId
curl "https://api.frihet.io/v1/clients/cli001/contacts/con001" \
-H "X-API-Key: fri_tu-clave-aqui"
Creare contatto
POST /v1/clients/:id/contacts
Campi richiesti:
| Campo | Tipo | Descrizione |
|---|---|---|
name | string | Nome della persona di contatto |
Campi opzionali:
| Campo | Tipo | Descrizione |
|---|---|---|
email | string | Email del contatto |
phone | string | Telefono |
role | string | Carica o ruolo (es. "Direttore finanziario") |
isPrimary | boolean | Se è il contatto principale del cliente |
curl -X POST https://api.frihet.io/v1/clients/cli001/contacts \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria Garcia",
"email": "maria@acme.es",
"phone": "+34 612 345 678",
"role": "Directora financiera",
"isPrimary": true
}'
Risposta (201):
{
"id": "con001",
"name": "Maria Garcia",
"email": "maria@acme.es",
"phone": "+34 612 345 678",
"role": "Directora financiera",
"isPrimary": true,
"createdAt": "2026-03-15T10:00:00.000Z",
"updatedAt": "2026-03-15T10:00:00.000Z"
}
Aggiornare contatto
PATCH /v1/clients/:id/contacts/:contactId
curl -X PATCH https://api.frihet.io/v1/clients/cli001/contacts/con001 \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{ "role": "CEO" }'
Risposta (200): Oggetto contatto aggiornato.
Eliminare contatto
DELETE /v1/clients/:id/contacts/:contactId
curl -X DELETE https://api.frihet.io/v1/clients/cli001/contacts/con001 \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta: 204 No Content
Attività (/v1/clients/:id/activities)
La timeline delle attività registra le interazioni con un cliente. Le attività di sistema (come invoice_created, quote_sent, ecc.) vengono generate automaticamente. Puoi anche creare attività manuali.
Le attività sono immutabili. Non possono essere aggiornate o eliminate una volta create.
Elencare attività
GET /v1/clients/:id/activities
curl "https://api.frihet.io/v1/clients/cli001/activities" \
-H "X-API-Key: fri_tu-clave-aqui"
Ottenere attività
GET /v1/clients/:id/activities/:activityId
curl "https://api.frihet.io/v1/clients/cli001/activities/act001" \
-H "X-API-Key: fri_tu-clave-aqui"
Creare attività
POST /v1/clients/:id/activities
Campi richiesti:
| Campo | Tipo | Descrizione |
|---|---|---|
type | string | Tipo di attività: call, email, meeting o task |
title | string | Titolo descrittivo dell'attività |
Campi opzionali:
| Campo | Tipo | Descrizione |
|---|---|---|
description | string | Descrizione dettagliata |
metadata | object | Dati aggiuntivi in formato libero |
I tipi call, email, meeting e task sono per attività create manualmente. I tipi di sistema come invoice_created, quote_sent o expense_linked vengono generati automaticamente e non possono essere creati tramite API.
curl -X POST https://api.frihet.io/v1/clients/cli001/activities \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"type": "call",
"title": "Llamada de seguimiento presupuesto Q2",
"description": "Comentamos las condiciones del presupuesto. Pendiente de confirmar.",
"metadata": {
"duration": "15min",
"outcome": "pending"
}
}'
Risposta (201):
{
"id": "act001",
"type": "call",
"title": "Llamada de seguimiento presupuesto Q2",
"description": "Comentamos las condiciones del presupuesto. Pendiente de confirmar.",
"metadata": {
"duration": "15min",
"outcome": "pending"
},
"createdAt": "2026-03-15T14:30:00.000Z"
}
Note (/v1/clients/:id/notes)
Elencare note
GET /v1/clients/:id/notes
curl "https://api.frihet.io/v1/clients/cli001/notes" \
-H "X-API-Key: fri_tu-clave-aqui"
Ottenere nota
GET /v1/clients/:id/notes/:noteId
curl "https://api.frihet.io/v1/clients/cli001/notes/note001" \
-H "X-API-Key: fri_tu-clave-aqui"
Creare nota
POST /v1/clients/:id/notes
Campi richiesti:
| Campo | Tipo | Descrizione |
|---|---|---|
content | string | Contenuto della nota |
curl -X POST https://api.frihet.io/v1/clients/cli001/notes \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"content": "Cliente interesado en plan Business. Contactar en abril para renovacion."
}'
Risposta (201):
{
"id": "note001",
"content": "Cliente interesado en plan Business. Contactar en abril para renovacion.",
"createdAt": "2026-03-15T16:00:00.000Z",
"updatedAt": "2026-03-15T16:00:00.000Z"
}
Aggiornare nota
PATCH /v1/clients/:id/notes/:noteId
curl -X PATCH https://api.frihet.io/v1/clients/cli001/notes/note001 \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"content": "Cliente interesado en plan Business. Reunion confirmada 5 de abril."
}'
Risposta (200): Oggetto nota aggiornato.
Eliminare nota
DELETE /v1/clients/:id/notes/:noteId
curl -X DELETE https://api.frihet.io/v1/clients/cli001/notes/note001 \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta: 204 No Content
Prodotti (/products)
Elencare prodotti
GET /v1/products
Accetta limit, offset, from e to (filtra per createdAt).
Ottenere prodotto
GET /v1/products/:id
Creare prodotto
POST /v1/products
Campi richiesti:
| Campo | Tipo | Descrizione |
|---|---|---|
name | string | Nome del prodotto o servizio (max 10.000 caratteri) |
unitPrice | number | Prezzo unitario |
Campi opzionali:
| Campo | Tipo | Descrizione |
|---|---|---|
description | string | Descrizione (max 10.000 caratteri) |
taxRate | number | Percentuale di imposta (0-100) |
curl -X POST https://api.frihet.io/v1/products \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"name": "Hora de consultoria",
"unitPrice": 75,
"description": "Consultoria estrategica",
"taxRate": 21
}'
Risposta (201):
{
"id": "prod001",
"name": "Hora de consultoria",
"unitPrice": 75,
"description": "Consultoria estrategica",
"taxRate": 21,
"createdAt": "2026-02-12T10:00:00.000Z",
"updatedAt": "2026-02-12T10:00:00.000Z"
}
Aggiornare prodotto (PUT o PATCH)
PUT /v1/products/:id
PATCH /v1/products/:id
curl -X PATCH https://api.frihet.io/v1/products/prod001 \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{ "unitPrice": 85 }'
Risposta (200): Oggetto prodotto aggiornato.
Eliminare prodotto
DELETE /v1/products/:id
Risposta: 204 No Content
Preventivi (/quotes)
Elencare preventivi
GET /v1/quotes
Parametri di query:
| Parametro | Tipo | Predefinito | Descrizione |
|---|---|---|---|
limit | integer | 50 | Risultati per pagina (massimo 100) |
offset | integer | 0 | Numero di risultati da saltare |
status | string | -- | Filtra per stato: bozza, inviata, accettata, rifiutata, scaduta |
from | string | -- | Data inizio (ISO 8601). Filtra per issueDate |
to | string | -- | Data fine (ISO 8601). Filtra per issueDate |
curl "https://api.frihet.io/v1/quotes?status=sent" \
-H "X-API-Key: fri_tu-clave-aqui"
Ottenere preventivo
GET /v1/quotes/:id
Creare preventivo
POST /v1/quotes
Campi richiesti:
| Campo | Tipo | Descrizione |
|---|---|---|
clientName | string | Nome del cliente (max 10.000 caratteri) |
items | array | Righe del preventivo. Ogni riga: { description, quantity, unitPrice } |
Campi opzionali:
| Campo | Tipo | Descrizione |
|---|---|---|
validUntil | string | Data di validità (ISO 8601) |
notes | string | Note o condizioni (max 10.000 caratteri) |
status | string | bozza (predefinito), inviata, accettata, rifiutata, scaduta |
curl -X POST https://api.frihet.io/v1/quotes \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"clientName": "Design Studio SL",
"items": [
{ "description": "Desarrollo web", "quantity": 80, "unitPrice": 60 },
{ "description": "Diseno UX", "quantity": 20, "unitPrice": 55 }
],
"validUntil": "2026-04-01",
"notes": "Incluye 2 rondas de revision"
}'
Risposta (201):
{
"id": "quo001",
"clientName": "Design Studio SL",
"items": [
{ "description": "Desarrollo web", "quantity": 80, "unitPrice": 60 },
{ "description": "Diseno UX", "quantity": 20, "unitPrice": 55 }
],
"status": "bozza",
"validUntil": "2026-04-01",
"notes": "Incluye 2 rondas de revision",
"createdAt": "2026-02-12T11:00:00.000Z",
"updatedAt": "2026-02-12T11:00:00.000Z"
}
Aggiornare preventivo (PUT o PATCH)
PUT /v1/quotes/:id
PATCH /v1/quotes/:id
Eliminare preventivo
DELETE /v1/quotes/:id
Risposta: 204 No Content
Scaricare preventivo in PDF
GET /v1/quotes/:id/pdf
Funziona come /invoices/:id/pdf. Restituisce application/pdf.
curl -o preventivo.pdf https://api.frihet.io/v1/quotes/quo001/pdf \
-H "X-API-Key: fri_tu-clave-aqui"
Inviare preventivo via email
POST /v1/quotes/:id/send
Stessi campi di /invoices/:id/send (recipientEmail, recipientName, customMessage, locale). Se il preventivo è in stato bozza, viene automaticamente aggiornato a inviata.
Operazioni batch (/batch)
Tutte le risorse principali supportano la creazione batch. Invia un array di massimo 50 elementi in una singola richiesta.
POST /v1/{resource}/batch
Risorse supportate: fatture, spese, clienti, prodotti, preventivi
Corpo della richiesta:
{
"items": [
{ "clientName": "Acme SL", "items": [{ "description": "Hora consultoria", "quantity": 1, "unitPrice": 95 }] },
{ "clientName": "TechStart SL", "items": [{ "description": "Desarrollo web", "quantity": 8, "unitPrice": 60 }] }
]
}
curl -X POST https://api.frihet.io/v1/invoices/batch \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "clientName": "Acme SL", "items": [{ "description": "Consultoria", "quantity": 1, "unitPrice": 95 }] },
{ "clientName": "TechStart SL", "items": [{ "description": "Desarrollo", "quantity": 8, "unitPrice": 60 }] }
]
}'
Risposta (207 Multi-Status):
{
"results": [
{ "status": 201, "data": { "id": "inv_001", "clientName": "Acme SL", "status": "draft" } },
{ "status": 201, "data": { "id": "inv_002", "clientName": "TechStart SL", "status": "draft" } }
],
"summary": { "total": 2, "succeeded": 2, "failed": 0 }
}
Se uno degli elementi fallisce la validazione, il resto viene comunque creato. Gli errori individuali vengono restituiti nell'array results:
{
"results": [
{ "status": 201, "data": { "id": "inv_001", "clientName": "Acme SL" } },
{ "status": 400, "error": { "message": "Missing required field: items" } }
],
"summary": { "total": 2, "succeeded": 1, "failed": 1 }
}
Limiti:
| Concetto | Limite |
|---|---|
| Elementi per batch | 50 massimo |
| Dimensione della richiesta | 1 MB massimo |
Idempotenza
Le richieste POST accettano un header Idempotency-Key per evitare la creazione duplicata di risorse in caso di reintentamenti di rete. Se la stessa chiave viene inviata entro le successive 24 ore, l'API restituisce la risposta originale senza eseguire nuovamente l'operazione.
curl -X POST https://api.frihet.io/v1/invoices \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"clientName": "Acme SL",
"items": [{ "description": "Consultoria", "quantity": 10, "unitPrice": 95 }]
}'
Comportamento:
| Scenario | Risultato |
|---|---|
| Prima richiesta con la chiave | La risorsa viene creata normalmente |
| Richiesta ripetuta con la stessa chiave (entro 24h) | Viene restituita la risposta originale, senza duplicare |
| Stessa chiave dopo 24h | Viene trattata come una nuova richiesta |
Header di risposta:
Quando l'API rileva una chiave ripetuta, include l'header X-Idempotent-Replayed: true in modo che il consumatore sappia che la risposta è una replica.
HTTP/1.1 201 Created
X-Idempotent-Replayed: true
Content-Type: application/json
Requisiti:
- La chiave deve avere un massimo di 64 caratteri
- Consigliamo di usare UUID v4
- Si applica solo alle richieste
POST(creare risorse)
Ricerca
Gli endpoint di elencazione supportano la ricerca full-text tramite il parametro q:
curl "https://api.frihet.io/v1/invoices?q=acme" \
-H "X-API-Key: fri_tu-clave-aqui"
La ricerca viene applicata sui campi di testo principali della risorsa (nome del cliente, descrizione, note, ecc.). Può essere combinata con i filtri esistenti (status, from, to).
Endpoint di intelligenza
Questi endpoint forniscono dati aggregati e contesto di business. Sono particolarmente utili per agenti AI e dashboard esterni.
Contesto di business (/context)
GET /v1/context
Restituisce un riepilogo completo del business, pensato per alimentare gli agenti AI con il contesto necessario per prendere decisioni informate. Include riepilogo finanziario, attività recente, avvisi e configurazione fiscale.
curl https://api.frihet.io/v1/context \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta (200):
{
"business": {
"name": "BRTHLS Studio",
"taxId": "12345678A",
"fiscalZone": "canarias",
"currency": "EUR"
},
"summary": {
"revenue": { "invoiced": 15000, "paid": 12000, "pending": 2000, "overdue": 1000 },
"expenses": { "total": 4500 },
"profit": 7500,
"counts": { "invoices": 25, "clients": 12, "products": 5 }
},
"recentActivity": [
{ "type": "invoice.created", "id": "inv_001", "description": "Factura para Acme SL", "timestamp": "2026-03-18T10:00:00Z" },
{ "type": "expense.created", "id": "exp_042", "description": "Adobe Creative Cloud", "timestamp": "2026-03-17T14:30:00Z" }
],
"alerts": [
{ "type": "overdue", "count": 2, "amount": 1000 },
{ "type": "tax_deadline", "model": "303", "dueDate": "2026-04-20" }
]
}
Conto economico mensile (/monthly)
GET /v1/monthly?month=YYYY-MM
Restituisce il conto economico (P&L) di un mese specifico: ricavi fatturati, spese per categoria, utile netto e comparazione con il mese precedente.
curl "https://api.frihet.io/v1/monthly?month=2026-02" \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta (200):
{
"month": "2026-02",
"revenue": {
"invoiced": 8500.00,
"collected": 6200.00,
"outstanding": 2300.00
},
"expenses": {
"total": 3100.00,
"byCategory": {
"software": 450.00,
"marketing": 800.00,
"office": 350.00,
"professional_services": 1500.00
}
},
"profit": 5400.00,
"comparison": {
"revenueChange": 12.5,
"expenseChange": -5.2,
"profitChange": 22.1
}
}
Dati fiscali trimestrali (/quarterly)
GET /v1/quarterly?quarter=YYYY-Q1
Restituisce i dati fiscali di un trimestre, preparati per la presentazione del Modello 303 (IVA) e Modello 130 (IRPEF). Include basi imponibili, imposte dovute, imposte deducibili e risultato della liquidazione.
curl "https://api.frihet.io/v1/quarterly?quarter=2026-Q1" \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta (200):
{
"quarter": "2026-Q1",
"period": { "from": "2026-01-01", "to": "2026-03-31" },
"modelo303": {
"baseImponible21": 12000.00,
"cuotaDevengada21": 2520.00,
"baseImponible10": 0,
"cuotaDevengada10": 0,
"baseImponible4": 0,
"cuotaDevengada4": 0,
"totalDevengado": 2520.00,
"ivaDeducible": 980.00,
"resultado": 1540.00
},
"modelo130": {
"ingresos": 12000.00,
"gastos": 4500.00,
"rendimientoNeto": 7500.00,
"porcentaje": 20,
"cuota": 1500.00,
"retenciones": 0,
"pagosAnteriores": 0,
"resultado": 1500.00
}
}
Gli endpoint /context, /monthly e /quarterly sono progettati per essere il punto di ingresso ideale per gli agenti AI. Forniscono le informazioni necessarie in una singola chiamata, senza la necessità di consultare più endpoint individuali.
Dashboard finanziario (/summary)
GET /v1/summary
Restituisce un riepilogo finanziario del business: ricavi, spese, profitto e contatori.
Parametri di query:
| Parametro | Tipo | Descrizione |
|---|---|---|
from | string | Data inizio (ISO 8601) |
to | string | Data fine (ISO 8601) |
curl "https://api.frihet.io/v1/summary?from=2026-01-01&to=2026-03-31" \
-H "X-API-Key: fri_tu-clave-aqui"
Risposta (200):
{
"period": { "from": "2026-01-01", "to": "2026-03-31" },
"revenue": {
"invoiced": 15000.00,
"paid": 12000.00,
"pending": 2000.00,
"overdue": 1000.00
},
"expenses": { "total": 4500.00 },
"profit": 7500.00,
"counts": {
"invoices": 25,
"quotes": 8,
"expenses": 42,
"clients": 12,
"products": 5
},
"invoicesByStatus": {
"draft": 3,
"sent": 5,
"paid": 15,
"overdue": 2
},
"overdue": { "count": 2, "amount": 1000.00 }
}
Codici di errore
L'API utilizza codici HTTP standard. Le risposte di errore includono un oggetto JSON con i campi error e, opzionalmente, message e details.
| Codice | Significato | Descrizione |
|---|---|---|
400 | Richiesta non valida | Manca un campo richiesto, formato errato o campo non consentito |
401 | Non autorizzato | La chiave API non è stata fornita, non è valida, ha un formato errato o è scaduta |
403 | Proibito | La chiave API non ha i permessi per accedere a questa risorsa |
404 | Non trovato | La risorsa richiesta non esiste |
405 | Metodo non consentito | Il metodo HTTP non è supportato per questo endpoint |
413 | Payload troppo grande | Il corpo della richiesta eccede 1 MB |
422 | Entità non elaborabile | Dati validi ma il server non può elaborarli (es: profilo fiscale non configurato) |
429 | Troppe richieste | È stato superato il limite di 100 richieste al minuto |
500 | Errore interno del server | Errore interno del server |
Struttura delle risposte di errore
Errore di validazione (400):
{
"error": "Validation error",
"details": [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["clientName"],
"message": "Required"
}
]
}
Gli errori di validazione usano il formato di Zod. Il campo path indica quale campo contiene l'errore e message descrive il problema.
Chiave non valida o scaduta (401):
{
"error": "Invalid or expired API key"
}
Formato chiave non valido (401):
{
"error": "Invalid API key format"
}
Risorsa non trovata (404):
{
"error": "Resource not found"
}
Stato non valido nel filtro (400):
{
"error": "Invalid status filter",
"message": "Valid values: bozza, inviata, pagata, scaduta, annullata"
}
Limite di richieste superato (429):
{
"error": "Rate limit exceeded",
"message": "Maximum 100 requests per minute",
"retryAfter": 60
}
Errore interno (500):
{
"error": "Internal server error"
}
Paginazione
Gli endpoint di elencazione restituiscono risultati paginati con la seguente struttura:
{
"data": [],
"total": 142,
"limit": 50,
"offset": 0
}
total: numero totale di record che soddisfano i filtri applicatilimit: numero di record restituiti in questa pagina (massimo 100)offset: numero di record saltati (massimo 10.000)
Per ottenere la pagina successiva:
curl "https://api.frihet.io/v1/invoices?limit=50&offset=50" \
-H "X-API-Key: fri_tu-clave-aqui"
I risultati sono ordinati per la data naturale della risorsa in ordine decrescente (i più recenti per primi):
- Fatture e preventivi:
issueDate - Spese:
date - Clienti e prodotti:
createdAt
Validazione stretta
L'API usa la validazione stretta (Zod strict mode). Le richieste con campi sconosciuti vengono rifiutate con un errore 400:
{
"error": "Validation error",
"details": [
{
"code": "unrecognized_keys",
"keys": ["campoInventado"],
"path": [],
"message": "Unrecognized key(s) in object: 'campoInventado'"
}
]
}
Questo previene errori silenziosi dovuti a errori di battitura nei nomi dei campi.
CORS
L'API supporta CORS per richieste dal browser. Le origini consentite sono:
https://app.frihet.iohttps://frihet.iohttps://www.frihet.io
Per le integrazioni server-to-server, CORS non è rilevante. Se hai bisogno di accedere all'API da un dominio diverso nel browser, utilizza un proxy nel tuo backend.
Header di sicurezza
Tutte le risposte includono header di sicurezza:
| Header | Valore |
|---|---|
X-Content-Type-Options | nosniff |
X-Frame-Options | DENY |
X-XSS-Protection | 1; mode=block |
X-Request-Id | ID unico della richiesta (utile per il debug) |
OAuth per MCP
L'endpoint POST /api/oauth/api-key consente di fornire automaticamente una chiave API tramite il flusso OAuth di MCP. Il server MCP utilizza questo endpoint internamente -- non è necessario chiamarlo direttamente.
Il flusso:
- L'utente si autentica tramite Firebase Auth
- Il client MCP invia il token Firebase a
/api/oauth/api-key - Il server restituisce una nuova chiave
fri_xxx...etichettata come "MCP OAuth" - La chiave scade dopo 365 giorni
Limite: 5 chiavi attive per utente. Se il limite viene superato, l'endpoint risponde con un 429.
Buone pratiche
- Archivia la chiave API in modo sicuro. Non includerla mai nel codice frontend, nei repository pubblici o nei log.
- Gestisci il rate limiting. Implementa un backoff esponenziale se ricevi un
429. - Usa la paginazione. Non richiedere tutti i record in una volta sola; itera con
limiteoffset. - Verifica i codici di risposta. Non assumere che tutte le richieste avranno successo.
- Ruota le chiavi periodicamente. Crea una nuova chiave, aggiorna la tua integrazione e revoca la precedente.
- Usa HTTPS sempre. Tutte le richieste all'API devono avvenire su HTTPS.
- Usa i filtri. I parametri
status,frometoriducono la quantità di dati trasferiti. - Sfrutta PATCH. Per gli aggiornamenti parziali, invia solo i campi modificati.