Passa al contenuto principale

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.

SDK disponibile

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:

  1. Vai su Impostazioni > Sviluppatori > Chiavi API
  2. Premi Crea chiave
  3. Assegna un nome descrittivo (ad esempio, integrazione-contabilità)
  4. Opzionalmente, imposta una data di scadenza in giorni. Se lo lasci vuoto, la chiave non scade
  5. 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:

  1. Crea una nuova chiave con lo stesso ambito
  2. Aggiorna la tua integrazione per usare la nuova chiave
  3. Verifica che le richieste funzionino correttamente
  4. 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:

HeaderDescrizioneEsempio
X-RateLimit-LimitRichieste consentite al minuto100
X-RateLimit-RemainingRichieste rimanenti nella finestra attuale87
X-RateLimit-ResetTimestamp Unix (secondi) in cui la finestra si riavvia1709312400

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 in X-RateLimit-Reset prima di riprovare.
  • Monitora X-RateLimit-Remaining per 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.

PATCH vs PUT

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:

ParametroTipoPredefinitoDescrizione
limitinteger50Risultati per pagina (massimo 100)
offsetinteger0Numero di risultati da saltare (massimo 10.000)
statusstring--Filtra per stato: bozza, inviata, pagata, scaduta, annullata
fromstring--Data inizio (ISO 8601: YYYY-MM-DD). Filtra per issueDate
tostring--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:

CampoTipoDescrizione
clientNamestringNome del cliente (max 10.000 caratteri)
itemsarrayElenco delle righe della fattura. Ogni riga: { description, quantity, unitPrice }

Campi opzionali:

CampoTipoDescrizione
statusstringbozza (predefinito), inviata, pagata, scaduta, annullata
issueDatestringData di emissione (ISO 8601). Predefinito: oggi
dueDatestringData di scadenza (ISO 8601)
notesstringNote interne (max 10.000 caratteri)
taxRatenumberPercentuale di imposta (0-100). Es: 21 per IVA 21%

Struttura di ogni riga (items[]):

CampoTipoRichiestoDescrizione
descriptionstringDescrizione del concetto (max 10.000 caratteri)
quantitynumberQuantità
unitPricenumberPrezzo 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.

attenzione

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:

CampoTipoRichiestoDescrizione
recipientEmailstringEmail del destinatario (max 255 caratteri)
recipientNamestringNoNome del destinatario (max 200 caratteri)
customMessagestringNoMessaggio personalizzato nel corpo dell'email (max 5.000 caratteri)
localestringNoLingua 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
CampoTipoRichiestoDescrizione
paidDatestringNoData 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:

ParametroTipoPredefinitoDescrizione
limitinteger50Risultati per pagina (massimo 100)
offsetinteger0Numero di risultati da saltare
fromstring--Data inizio (ISO 8601). Filtra per date
tostring--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:

CampoTipoDescrizione
descriptionstringDescrizione della spesa (max 10.000 caratteri)
amountnumberImporto della spesa

Campi opzionali:

CampoTipoDescrizione
categorystringCategoria della spesa (max 10.000 caratteri)
datestringData della spesa (ISO 8601). Predefinito: oggi
vendorstringFornitore (max 10.000 caratteri)
taxDeductiblebooleanSe 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:

CampoTipoDescrizione
namestringNome del cliente (max 10.000 caratteri)

Campi opzionali:

CampoTipoDescrizione
emailstringEmail di contatto
phonestringTelefono
taxIdstringNIF/CIF/VAT
addressobjectIndirizzo (vedi struttura sotto)

Struttura di address:

CampoTipoDescrizione
streetstringVia e numero
citystringCittà
statestringProvincia o stato
postalCodestringCodice postale
countrystringPaese

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:

CampoTipoDescrizione
namestringNome della persona di contatto

Campi opzionali:

CampoTipoDescrizione
emailstringEmail del contatto
phonestringTelefono
rolestringCarica o ruolo (es. "Direttore finanziario")
isPrimarybooleanSe è 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.

Inmutabili

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:

CampoTipoDescrizione
typestringTipo di attività: call, email, meeting o task
titlestringTitolo descrittivo dell'attività

Campi opzionali:

CampoTipoDescrizione
descriptionstringDescrizione dettagliata
metadataobjectDati aggiuntivi in formato libero
Tipi di attività

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:

CampoTipoDescrizione
contentstringContenuto 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:

CampoTipoDescrizione
namestringNome del prodotto o servizio (max 10.000 caratteri)
unitPricenumberPrezzo unitario

Campi opzionali:

CampoTipoDescrizione
descriptionstringDescrizione (max 10.000 caratteri)
taxRatenumberPercentuale 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:

ParametroTipoPredefinitoDescrizione
limitinteger50Risultati per pagina (massimo 100)
offsetinteger0Numero di risultati da saltare
statusstring--Filtra per stato: bozza, inviata, accettata, rifiutata, scaduta
fromstring--Data inizio (ISO 8601). Filtra per issueDate
tostring--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:

CampoTipoDescrizione
clientNamestringNome del cliente (max 10.000 caratteri)
itemsarrayRighe del preventivo. Ogni riga: { description, quantity, unitPrice }

Campi opzionali:

CampoTipoDescrizione
validUntilstringData di validità (ISO 8601)
notesstringNote o condizioni (max 10.000 caratteri)
statusstringbozza (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:

ConcettoLimite
Elementi per batch50 massimo
Dimensione della richiesta1 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:

ScenarioRisultato
Prima richiesta con la chiaveLa risorsa viene creata normalmente
Richiesta ripetuta con la stessa chiave (entro 24h)Viene restituita la risposta originale, senza duplicare
Stessa chiave dopo 24hViene 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
}
}
suggerimento

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:

ParametroTipoDescrizione
fromstringData inizio (ISO 8601)
tostringData 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.

CodiceSignificatoDescrizione
400Richiesta non validaManca un campo richiesto, formato errato o campo non consentito
401Non autorizzatoLa chiave API non è stata fornita, non è valida, ha un formato errato o è scaduta
403ProibitoLa chiave API non ha i permessi per accedere a questa risorsa
404Non trovatoLa risorsa richiesta non esiste
405Metodo non consentitoIl metodo HTTP non è supportato per questo endpoint
413Payload troppo grandeIl corpo della richiesta eccede 1 MB
422Entità non elaborabileDati validi ma il server non può elaborarli (es: profilo fiscale non configurato)
429Troppe richiesteÈ stato superato il limite di 100 richieste al minuto
500Errore interno del serverErrore 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 applicati
  • limit: 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.io
  • https://frihet.io
  • https://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:

HeaderValore
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
X-XSS-Protection1; mode=block
X-Request-IdID 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:

  1. L'utente si autentica tramite Firebase Auth
  2. Il client MCP invia il token Firebase a /api/oauth/api-key
  3. Il server restituisce una nuova chiave fri_xxx... etichettata come "MCP OAuth"
  4. 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

  1. Archivia la chiave API in modo sicuro. Non includerla mai nel codice frontend, nei repository pubblici o nei log.
  2. Gestisci il rate limiting. Implementa un backoff esponenziale se ricevi un 429.
  3. Usa la paginazione. Non richiedere tutti i record in una volta sola; itera con limit e offset.
  4. Verifica i codici di risposta. Non assumere che tutte le richieste avranno successo.
  5. Ruota le chiavi periodicamente. Crea una nuova chiave, aggiorna la tua integrazione e revoca la precedente.
  6. Usa HTTPS sempre. Tutte le richieste all'API devono avvenire su HTTPS.
  7. Usa i filtri. I parametri status, from e to riducono la quantità di dati trasferiti.
  8. Sfrutta PATCH. Per gli aggiornamenti parziali, invia solo i campi modificati.