Pular para o conteúdo principal

API REST

A API REST da Frihet permite acessar e manipular os recursos da sua conta de forma programática. Toda a comunicação é realizada sobre HTTPS e as respostas são JSON.

URL base

https://api.frihet.io/v1

Todos os endpoints descritos nesta página são relativos a esta URL base.

Descoberta: Uma requisição GET à raiz (https://api.frihet.io/) retorna os links principais sem a necessidade de autenticação:

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

A especificação OpenAPI 3.1 está disponível em https://api.frihet.io/openapi.yaml.

SDK disponível

Se você usa TypeScript ou JavaScript, o SDK oficial simplifica a integração:

npm install @frihet/sdk
import Frihet from '@frihet/sdk';
const frihet = new Frihet({ apiKey: 'fri_...' });
const invoices = await frihet.invoices.list({ status: 'overdue' });

Repositório: github.com/Frihet-io/frihet-sdk


Autenticação

Cada requisição deve incluir uma chave de API no cabeçalho X-API-Key. As chaves são criadas em Configurações > Desenvolvedores > Chaves de API no painel do Frihet.

As chaves têm o prefixo fri_ seguido de 32 bytes aleatórios codificados em base64url. Exemplo: fri_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345678.

curl https://api.frihet.io/v1/clients \
-H "X-API-Key: fri_tu-clave-aqui"

Alternativamente, você pode enviar a chave como Bearer token no cabeçalho Authorization:

curl https://api.frihet.io/v1/clients \
-H "Authorization: Bearer fri_tu-clave-aqui"

Segurança das chaves

  • A chave em texto simples é exibida apenas uma vez, no momento da criação. Não pode ser recuperada depois.
  • O servidor armazena um hash SHA-256 da chave. Mesmo em caso de violação de dados, a chave original não é recuperável.
  • Você pode criar chaves com data de expiração configurável.
  • Se você suspeita que uma chave foi comprometida, revogue-a imediatamente no painel.

Ciclo de vida das chaves

Criar uma chave:

  1. Vá para Configurações > Desenvolvedores > Chaves de API
  2. Clique em Criar chave
  3. Atribua um nome descritivo (por exemplo, integracao-contabilidade)
  4. Opcionalmente, defina uma data de expiração em dias. Se você deixar vazio, a chave não expira
  5. Copie a chave imediatamente -- você não poderá vê-la novamente

Expiração:

As chaves com data de expiração param de funcionar automaticamente quando o prazo expira. As requisições com uma chave expirada recebem um 401 Unauthorized.

Revogação:

Você pode revogar uma chave a qualquer momento em Configurações > Desenvolvedores > Chaves de API. A revogação é imediata e irreversível: as requisições em andamento com essa chave falharão a partir daquele instante.

Rotação de chaves:

Para rotacionar uma chave sem interromper o serviço:

  1. Crie uma nova chave com o mesmo escopo
  2. Atualize sua integração para usar a nova chave
  3. Verifique se as requisições funcionam corretamente
  4. Revogue a chave anterior

Monitoramento:

Cada chave exibe a data de último uso no painel de Configurações. Revise periodicamente as chaves inativas e revogue as que não são mais utilizadas.


Rate limiting

Cada chave de API tem um limite de 100 requisições por minuto. Se excedido, a API responde com um código 429:

{
"error": "Rate limit exceeded",
"message": "Maximum 100 requests per minute",
"retryAfter": 60
}

Cabeçalhos de rate limiting

Todas as respostas da API incluem cabeçalhos para que você possa gerenciar o limite de forma proativa:

CabeçalhoDescriçãoExemplo
X-RateLimit-LimitRequisições permitidas por minuto100
X-RateLimit-RemainingRequisições restantes na janela atual87
X-RateLimit-ResetTimestamp Unix (segundos) em que a janela é reiniciada1709312400

Exemplo de resposta com cabeçalhos de rate limiting:

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1709312400
Content-Type: application/json

Gerenciamento do código 429:

Quando você recebe um 429, use os cabeçalhos para calcular quanto esperar antes de tentar novamente:

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;
}

Recomendações:

  • Se você receber um 429, espere até o timestamp indicado em X-RateLimit-Reset antes de tentar novamente.
  • Monitore X-RateLimit-Remaining para desacelerar as requisições antes de atingir o limite.
  • Distribua as requisições ao longo do tempo em vez de enviar rajadas.

Tamanho da requisição

O corpo das requisições POST, PUT e PATCH não pode exceder 1 MB. Requisições maiores recebem um código 413.


Recursos

A API expõe 7 recursos principais: invoices, expenses, clients, products, quotes, vendors e webhooks. Todos suportam operações CRUD completas (GET, POST, PUT/PATCH, DELETE). Além disso, os clientes têm subcoleções CRM: contacts, activities e notes.

PATCH vs PUT

Tanto PUT quanto PATCH aceitam atualizações parciais. Você não precisa enviar o recurso completo -- apenas os campos que deseja modificar.

Faturas (/invoices)

Listar faturas

GET /v1/invoices

Parâmetros de consulta:

ParâmetroTipoPor padrãoDescrição
limitinteger50Resultados por página (máximo 100)
offsetinteger0Número de resultados a pular (máximo 10.000)
statusstring--Filtrar por status: rascunho, enviada, paga, vencida, cancelada
fromstring--Data de início (ISO 8601: AAAA-MM-DD). Filtra por issueDate
tostring--Data de fim (ISO 8601: AAAA-MM-DD). Filtra por issueDate

Exemplo:

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"

Resposta (200):

{
"data": [
{
"id": "abc123",
"clientName": "Acme S.L.",
"items": [
{ "description": "Consultoria", "quantity": 10, "unitPrice": 75 }
],
"status": "paid",
"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
}

Obter fatura

GET /v1/invoices/:id
curl https://api.frihet.io/v1/invoices/abc123 \
-H "X-API-Key: fri_tu-clave-aqui"

Resposta (200):

{
"id": "abc123",
"clientName": "Acme S.L.",
"items": [
{ "description": "Consultoria", "quantity": 10, "unitPrice": 75 }
],
"status": "paid",
"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"
}

Criar fatura

POST /v1/invoices

Campos requeridos:

CampoTipoDescrição
clientNamestringNome do cliente (máx. 10.000 caracteres)
itemsarrayLista de linhas da fatura. Cada linha: { description, quantity, unitPrice }

Campos opcionais:

CampoTipoDescrição
statusstringrascunho (padrão), enviada, paga, vencida, cancelada
issueDatestringData de emissão (ISO 8601). Por padrão: hoje
dueDatestringData de vencimento (ISO 8601)
notesstringNotas internas (máx. 10.000 caracteres)
taxRatenumberPorcentagem de imposto (0-100). Ex: 21 para IVA 21%

Estrutura de cada linha (items[]):

CampoTipoRequeridoDescrição
descriptionstringSimDescrição do conceito (máx. 10.000 caracteres)
quantitynumberSimQuantidade
unitPricenumberSimPreço unitário
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"
}'

Resposta (201):

{
"id": "def456",
"clientName": "Acme S.L.",
"items": [
{ "description": "Desarrollo web", "quantity": 40, "unitPrice": 60 }
],
"status": "draft",
"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"
}

Atualizar fatura (PUT ou PATCH)

PUT /v1/invoices/:id
PATCH /v1/invoices/:id

Você só precisa enviar os campos que deseja modificar. Os campos não incluídos permanecem inalterados.

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": "sent",
"notes": "Enviada al cliente"
}'

Resposta (200): Objeto de fatura atualizado.

cuidado

Se você enviar items, deve enviar o array completo -- não são permitidas atualizações parciais de linhas individuais.

Excluir fatura

DELETE /v1/invoices/:id
curl -X DELETE https://api.frihet.io/v1/invoices/def456 \
-H "X-API-Key: fri_tu-clave-aqui"

Resposta: 204 No Content

Baixar fatura em PDF

GET /v1/invoices/:id/pdf

Retorna o PDF da fatura como application/pdf com o cabeçalho Content-Disposition: attachment.

curl -o factura.pdf https://api.frihet.io/v1/invoices/abc123/pdf \
-H "X-API-Key: fri_tu-clave-aqui"

Enviar fatura por e-mail

POST /v1/invoices/:id/send

Envia a fatura ao destinatário especificado através do Resend. Se a fatura estiver no status rascunho, é atualizada automaticamente para enviada.

Campos:

CampoTipoRequeridoDescrição
recipientEmailstringSimE-mail do destinatário (máx. 255 caracteres)
recipientNamestringNãoNome do destinatário (máx. 200 caracteres)
customMessagestringNãoMensagem personalizada no corpo do e-mail (máx. 5.000 caracteres)
localestringNãoIdioma do e-mail: es (padrão) ou 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"
}'

Resposta (200):

{ "success": true, "messageId": "re_abc123..." }

Marcar fatura como paga

POST /v1/invoices/:id/paid
CampoTipoRequeridoDescrição
paidDatestringNãoData de pagamento (ISO 8601). Por padrão: hoje
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" }'

Resposta (200):

{ "success": true, "status": "paid", "paidAt": "2026-03-15" }

Despesas (/expenses)

Listar despesas

GET /v1/expenses

Parâmetros de consulta:

ParâmetroTipoPor padrãoDescrição
limitinteger50Resultados por página (máximo 100)
offsetinteger0Número de resultados a pular
fromstring--Data de início (ISO 8601). Filtra por date
tostring--Data de fim (ISO 8601). Filtra por date
curl "https://api.frihet.io/v1/expenses?limit=20&from=2026-01-01" \
-H "X-API-Key: fri_tu-clave-aqui"

Resposta (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
}

Obter despesa

GET /v1/expenses/:id

Criar despesa

POST /v1/expenses

Campos requeridos:

CampoTipoDescrição
descriptionstringDescrição da despesa (máx. 10.000 caracteres)
amountnumberValor da despesa

Campos opcionais:

CampoTipoDescrição
categorystringCategoria da despesa (máx. 10.000 caracteres)
datestringData da despesa (ISO 8601). Por padrão: hoje
vendorstringFornecedor (máx. 10.000 caracteres)
taxDeductiblebooleanSe a despesa é dedutível
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
}'

Resposta (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"
}

Atualizar despesa (PUT ou 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 }'

Resposta (200): Objeto de despesa atualizado.

Excluir despesa

DELETE /v1/expenses/:id

Resposta: 204 No Content


Clientes (/clients)

Listar clientes

GET /v1/clients

Aceita limit, offset, from e to (filtra por createdAt).

curl "https://api.frihet.io/v1/clients?limit=50" \
-H "X-API-Key: fri_tu-clave-aqui"

Obter cliente

GET /v1/clients/:id

Criar cliente

POST /v1/clients

Campos requeridos:

CampoTipoDescrição
namestringNome do cliente (máx. 10.000 caracteres)

Campos opcionais:

CampoTipoDescrição
emailstringE-mail de contato
phonestringTelefone
taxIdstringNIF/CIF/VAT
addressobjectEndereço (ver estrutura abaixo)

Estrutura de address:

CampoTipoDescrição
streetstringRua e número
citystringCidade
statestringProvíncia ou estado
postalCodestringCódigo postal
countrystringPaís

Todos os campos de address são opcionais.

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"
}
}'

Resposta (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"
}

Atualizar cliente (PUT ou 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" }'

Resposta (200): Objeto de cliente atualizado.

Excluir cliente

DELETE /v1/clients/:id

Resposta: 204 No Content


CRM: Pessoas de contato, Atividades e Notas

Os clientes têm três subcoleções para gerenciar relacionamentos de CRM: pessoas de contato, atividades e notas. Todos os endpoints requerem um clientId válido na URL.

Pessoas de contato (/v1/clients/:id/contacts)

Listar contatos
GET /v1/clients/:id/contacts
curl "https://api.frihet.io/v1/clients/cli001/contacts" \
-H "X-API-Key: fri_tu-clave-aqui"
Obter contato
GET /v1/clients/:id/contacts/:contactId
curl "https://api.frihet.io/v1/clients/cli001/contacts/con001" \
-H "X-API-Key: fri_tu-clave-aqui"
Criar contato
POST /v1/clients/:id/contacts

Campos requeridos:

CampoTipoDescrição
namestringNome da pessoa de contato

Campos opcionais:

CampoTipoDescrição
emailstringE-mail do contato
phonestringTelefone
rolestringCargo ou função (ex: "Diretor financeiro")
isPrimarybooleanSe é o contato principal do 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
}'

Resposta (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"
}
Atualizar contato
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" }'

Resposta (200): Objeto de contato atualizado.

Excluir contato
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"

Resposta: 204 No Content

Atividades (/v1/clients/:id/activities)

O timeline de atividades registra as interações com um cliente. As atividades do sistema (como invoice_created, quote_sent, etc.) são geradas automaticamente. Você também pode criar atividades manuais.

Imutáveis

As atividades são imutáveis. Não podem ser atualizadas nem excluídas uma vez criadas.

Listar atividades
GET /v1/clients/:id/activities
curl "https://api.frihet.io/v1/clients/cli001/activities" \
-H "X-API-Key: fri_tu-clave-aqui"
Obter atividade
GET /v1/clients/:id/activities/:activityId
curl "https://api.frihet.io/v1/clients/cli001/activities/act001" \
-H "X-API-Key: fri_tu-clave-aqui"
Criar atividade
POST /v1/clients/:id/activities

Campos requeridos:

CampoTipoDescrição
typestringTipo de atividade: call, email, meeting ou task
titlestringTítulo descritivo da atividade

Campos opcionais:

CampoTipoDescrição
descriptionstringDescrição detalhada
metadataobjectDados adicionais em formato livre
Tipos de atividade

Os tipos call, email, meeting e task são para atividades criadas manualmente. Os tipos de sistema como invoice_created, quote_sent ou expense_linked são gerados automaticamente e não podem ser criados via 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"
}
}'

Resposta (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"
}

Notas (/v1/clients/:id/notes)

Listar notas
GET /v1/clients/:id/notes
curl "https://api.frihet.io/v1/clients/cli001/notes" \
-H "X-API-Key: fri_tu-clave-aqui"
Obter 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"
Criar nota
POST /v1/clients/:id/notes

Campos requeridos:

CampoTipoDescrição
contentstringConteúdo da 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."
}'

Resposta (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"
}
Atualizar 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."
}'

Resposta (200): Objeto de nota atualizado.

Excluir 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"

Resposta: 204 No Content


Produtos (/products)

Listar produtos

GET /v1/products

Aceita limit, offset, from e to (filtra por createdAt).

Obter produto

GET /v1/products/:id

Criar produto

POST /v1/products

Campos requeridos:

CampoTipoDescrição
namestringNome do produto ou serviço (máx. 10.000 caracteres)
unitPricenumberPreço unitário

Campos opcionais:

CampoTipoDescrição
descriptionstringDescrição (máx. 10.000 caracteres)
taxRatenumberPorcentagem de imposto (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
}'

Resposta (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"
}

Atualizar produto (PUT ou 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 }'

Resposta (200): Objeto de produto atualizado.

Excluir produto

DELETE /v1/products/:id

Resposta: 204 No Content


Orçamentos (/quotes)

Listar orçamentos

GET /v1/quotes

Parâmetros de consulta:

ParâmetroTipoPor padrãoDescrição
limitinteger50Resultados por página (máximo 100)
offsetinteger0Número de resultados a pular
statusstring--Filtrar por status: draft, sent, accepted, rejected, expired
fromstring--Data de início (ISO 8601). Filtra por issueDate
tostring--Data de fim (ISO 8601). Filtra por issueDate
curl "https://api.frihet.io/v1/quotes?status=sent" \
-H "X-API-Key: fri_tu-clave-aqui"

Obter orçamento

GET /v1/quotes/:id

Criar orçamento

POST /v1/quotes

Campos requeridos:

CampoTipoDescrição
clientNamestringNome do cliente (máx. 10.000 caracteres)
itemsarrayLinhas do orçamento. Cada linha: { description, quantity, unitPrice }

Campos opcionais:

CampoTipoDescrição
validUntilstringData de validade (ISO 8601)
notesstringNotas ou condições (máx. 10.000 caracteres)
statusstringdraft (padrão), sent, accepted, rejected, expired
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"
}'

Resposta (201):

{
"id": "quo001",
"clientName": "Design Studio SL",
"items": [
{ "description": "Desarrollo web", "quantity": 80, "unitPrice": 60 },
{ "description": "Diseno UX", "quantity": 20, "unitPrice": 55 }
],
"status": "draft",
"validUntil": "2026-04-01",
"notes": "Incluye 2 rondas de revision",
"createdAt": "2026-02-12T11:00:00.000Z",
"updatedAt": "2026-02-12T11:00:00.000Z"
}

Atualizar orçamento (PUT ou PATCH)

PUT /v1/quotes/:id
PATCH /v1/quotes/:id

Excluir orçamento

DELETE /v1/quotes/:id

Resposta: 204 No Content

Baixar orçamento em PDF

GET /v1/quotes/:id/pdf

Funciona da mesma forma que /invoices/:id/pdf. Retorna application/pdf.

curl -o presupuesto.pdf https://api.frihet.io/v1/quotes/quo001/pdf \
-H "X-API-Key: fri_tu-clave-aqui"

Enviar orçamento por e-mail

POST /v1/quotes/:id/send

Mesmos campos que /invoices/:id/send (recipientEmail, recipientName, customMessage, locale). Se o orçamento estiver no status rascunho, é atualizado automaticamente para enviada.


Operações em lote (/batch)

Todos os recursos principais suportam criação em lote. Envie um array de até 50 elementos em uma única requisição.

POST /v1/{resource}/batch

Recursos suportados: invoices, expenses, clients, products, quotes

Corpo da requisição:

{
"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 }] }
]
}'

Resposta (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 algum dos elementos falhar a validação, o restante é criado da mesma forma. Os erros individuais são retornados no 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 }
}

Limites:

ConceitoLimite
Elementos por lote50 máximo
Tamanho da requisição1 MB máximo

Idempotência

As requisições POST aceitam um cabeçalho Idempotency-Key para evitar a criação duplicada de recursos em caso de novas tentativas de rede. Se a mesma chave for enviada dentro das próximas 24 horas, a API retorna a resposta original sem executar a operação novamente.

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:

CenárioResultado
Primeira requisição com a chaveO recurso é criado normalmente
Requisição repetida com a mesma chave (dentro de 24h)A resposta original é retornada, sem duplicar
Mesma chave após 24hÉ tratada como uma nova requisição

Cabeçalho de resposta:

Quando a API detecta uma chave repetida, inclui o cabeçalho X-Idempotent-Replayed: true para que o consumidor saiba que a resposta é uma réplica.

HTTP/1.1 201 Created
X-Idempotent-Replayed: true
Content-Type: application/json

Requisitos:

  • A chave deve ter no máximo 64 caracteres
  • Recomendamos usar UUID v4
  • Aplica-se apenas a requisições POST (criar recursos)

Busca

Os endpoints de listagem suportam busca de texto completo através do parâmetro q:

curl "https://api.frihet.io/v1/invoices?q=acme" \
-H "X-API-Key: fri_tu-clave-aqui"

A busca é aplicada sobre os campos de texto principais do recurso (nome de cliente, descrição, notas, etc.). Pode ser combinada com os filtros existentes (status, from, to).


Endpoints de inteligência

Estes endpoints fornecem dados agregados e contexto de negócio. São especialmente úteis para agentes de IA e dashboards externos.

Contexto de negócio (/context)

GET /v1/context

Retorna um resumo completo do negócio, projetado para alimentar agentes de IA com o contexto necessário para tomar decisões informadas. Inclui resumo financeiro, atividade recente, alertas e configuração fiscal.

curl https://api.frihet.io/v1/context \
-H "X-API-Key: fri_tu-clave-aqui"

Resposta (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" }
]
}

P&L mensal (/monthly)

GET /v1/monthly?month=YYYY-MM

Retorna a demonstração de resultados (P&L) de um mês específico: receitas faturadas, despesas por categoria, lucro líquido e comparação com o mês anterior.

curl "https://api.frihet.io/v1/monthly?month=2026-02" \
-H "X-API-Key: fri_tu-clave-aqui"

Resposta (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
}
}

Dados fiscais trimestrais (/quarterly)

GET /v1/quarterly?quarter=YYYY-Q1

Retorna os dados fiscais de um trimestre, preparados para a apresentação do Modelo 303 (IVA) e Modelo 130 (IRPF). Inclui bases tributáveis, valores devidos, valores dedutíveis e resultado da liquidação.

curl "https://api.frihet.io/v1/quarterly?quarter=2026-Q1" \
-H "X-API-Key: fri_tu-clave-aqui"

Resposta (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
}
}
dica

Os endpoints /context, /monthly e /quarterly são projetados para serem o ponto de entrada ideal para agentes de IA. Eles fornecem as informações necessárias em uma única chamada, sem a necessidade de consultar múltiplos endpoints individuais.


Dashboard financeiro (/summary)

GET /v1/summary

Retorna um resumo financeiro do negócio: receitas, despesas, lucro e contadores.

Parâmetros de consulta:

ParâmetroTipoDescrição
fromstringData de início (ISO 8601)
tostringData de fim (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"

Resposta (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 }
}

Códigos de erro

A API utiliza códigos HTTP padrão. As respostas de erro incluem um objeto JSON com os campos error e, opcionalmente, message e details.

CódigoSignificadoDescrição
400Requisição InválidaFalta um campo requerido, formato incorreto ou campo não permitido
401Não AutorizadoA chave de API não foi fornecida, é inválida, tem formato incorreto ou expirou
403ProibidoA chave de API não tem permissões para acessar este recurso
404Não EncontradoO recurso solicitado não existe
405Método Não PermitidoO método HTTP não é suportado para este endpoint
413Payload Muito GrandeO corpo da requisição excede 1 MB
422Entidade Não ProcessávelDados válidos, mas o servidor não pode processá-los (ex: perfil fiscal não configurado)
429Muitas RequisiçõesO limite de 100 requisições por minuto foi excedido
500Erro Interno do ServidorErro interno do servidor

Estrutura das respostas de erro

Erro de validação (400):

{
"error": "Validation error",
"details": [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["clientName"],
"message": "Required"
}
]
}

Os erros de validação usam o formato de Zod. O campo path indica qual campo contém o erro e message descreve o problema.

Chave inválida ou expirada (401):

{
"error": "Invalid or expired API key"
}

Formato de chave inválido (401):

{
"error": "Invalid API key format"
}

Recurso não encontrado (404):

{
"error": "Resource not found"
}

Status inválido no filtro (400):

{
"error": "Invalid status filter",
"message": "Valid values: draft, sent, paid, overdue, cancelled"
}

Rate limit excedido (429):

{
"error": "Rate limit exceeded",
"message": "Maximum 100 requests per minute",
"retryAfter": 60
}

Erro interno (500):

{
"error": "Internal server error"
}

Paginação

Os endpoints de listagem retornam resultados paginados com a seguinte estrutura:

{
"data": [],
"total": 142,
"limit": 50,
"offset": 0
}
  • total: número total de registros que atendem aos filtros aplicados
  • limit: número de registros retornados nesta página (máximo 100)
  • offset: número de registros pulados (máximo 10.000)

Para obter a próxima página:

curl "https://api.frihet.io/v1/invoices?limit=50&offset=50" \
-H "X-API-Key: fri_tu-clave-aqui"

Os resultados são ordenados pela data natural do recurso em ordem descendente (mais recentes primeiro):

  • Faturas e orçamentos: issueDate
  • Despesas: date
  • Clientes e produtos: createdAt

Validação estrita

A API usa validação estrita (Zod strict mode). As requisições com campos desconhecidos são rejeitadas com um erro 400:

{
"error": "Validation error",
"details": [
{
"code": "unrecognized_keys",
"keys": ["campoInventado"],
"path": [],
"message": "Unrecognized key(s) in object: 'campoInventado'"
}
]
}

Isso previne erros silenciosos por erros de digitação nos nomes dos campos.


CORS

A API suporta CORS para requisições do navegador. As origens permitidas são:

  • https://app.frihet.io
  • https://frihet.io
  • https://www.frihet.io

Para integrações server-to-server, CORS não é relevante. Se você precisar acessar a API de um domínio diferente no navegador, utilize um proxy no seu backend.


Cabeçalhos de segurança

Todas as respostas incluem cabeçalhos de segurança:

CabeçalhoValor
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
X-XSS-Protection1; mode=block
X-Request-IdID único da requisição (útil para depuração)

OAuth para MCP

O endpoint POST /api/oauth/api-key permite provisionar uma chave de API automaticamente através do fluxo OAuth do MCP. O servidor MCP usa este endpoint internamente -- você não precisa chamá-lo diretamente.

O fluxo:

  1. O usuário se autentica via Firebase Auth
  2. O cliente MCP envia o token do Firebase para /api/oauth/api-key
  3. O servidor retorna uma nova chave fri_xxx... rotulada como "MCP OAuth"
  4. A chave expira em 365 dias

Limite: 5 chaves ativas por usuário. Se o limite for excedido, o endpoint responde com um 429.


Boas práticas

  1. Armazene a chave de API de forma segura. Nunca a inclua em código frontend, repositórios públicos ou logs.
  2. Gerencie o rate limiting. Implemente backoff exponencial se receber um 429.
  3. Use paginação. Não solicite todos os registros de uma vez; itere com limit e offset.
  4. Verifique os códigos de resposta. Não presuma que todas as requisições serão bem-sucedidas.
  5. Rotacione as chaves periodicamente. Crie uma nova chave, atualize sua integração e revogue a anterior.
  6. Use HTTPS sempre. Todas as requisições à API devem ser sobre HTTPS.
  7. Use filtros. Os parâmetros status, from e to reduzem a quantidade de dados transferidos.
  8. Aproveite o PATCH. Para atualizações parciais, envie apenas os campos modificados.