Saltar al contenido principal

API REST

La API REST de Frihet permite acceder y manipular los recursos de tu cuenta de forma programatica. Toda la comunicacion se realiza sobre HTTPS y las respuestas son JSON.

URL base

https://api.frihet.io/v1

Todos los endpoints descritos en esta pagina son relativos a esta URL base.

Descubrimiento: Una peticion GET a la raiz (https://api.frihet.io/) devuelve los enlaces principales sin necesidad de autenticacion:

{
"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 especificacion OpenAPI 3.1 esta disponible en https://api.frihet.io/openapi.yaml.

SDK disponible

Si usas TypeScript o JavaScript, el SDK oficial simplifica la integracion:

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

Repositorio: github.com/Frihet-io/frihet-sdk


Autenticacion

Cada peticion debe incluir una API key en la cabecera X-API-Key. Las claves se crean desde Ajustes > Desarrolladores > API Keys en el panel de Frihet.

Las claves tienen el prefijo fri_ seguido de 32 bytes aleatorios codificados en base64url. Ejemplo: fri_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345678.

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

Alternativamente, puedes enviar la clave como Bearer token en la cabecera Authorization:

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

Seguridad de las claves

  • La clave en texto plano solo se muestra una vez, en el momento de la creacion. No se puede recuperar despues.
  • El servidor almacena un hash SHA-256 de la clave. Incluso en caso de brecha de datos, la clave original no es recuperable.
  • Puedes crear claves con fecha de expiracion configurable.
  • Si sospechas que una clave ha sido comprometida, revocala inmediatamente desde el panel.

Ciclo de vida de las claves

Crear una clave:

  1. Ve a Ajustes > Desarrolladores > API Keys
  2. Pulsa Crear clave
  3. Asigna un nombre descriptivo (por ejemplo, integracion-contabilidad)
  4. Opcionalmente, establece una fecha de expiracion en dias. Si lo dejas vacio, la clave no caduca
  5. Copia la clave inmediatamente -- no podras verla de nuevo

Expiracion:

Las claves con fecha de expiracion dejan de funcionar automaticamente al cumplirse el plazo. Las peticiones con una clave expirada reciben un 401 Unauthorized.

Revocacion:

Puedes revocar una clave en cualquier momento desde Ajustes > Desarrolladores > API Keys. La revocacion es inmediata e irreversible: las peticiones en curso con esa clave fallaran a partir de ese instante.

Rotacion de claves:

Para rotar una clave sin interrumpir el servicio:

  1. Crea una nueva clave con el mismo alcance
  2. Actualiza tu integracion para usar la nueva clave
  3. Verifica que las peticiones funcionan correctamente
  4. Revoca la clave anterior

Monitorizacion:

Cada clave muestra la fecha de ultimo uso en el panel de Ajustes. Revisa periodicamente las claves inactivas y revoca las que ya no se utilicen.


Rate limiting

Cada clave API tiene un limite de 100 peticiones por minuto. Si se excede, la API responde con un codigo 429:

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

Cabeceras de rate limiting

Todas las respuestas de la API incluyen cabeceras para que puedas gestionar el limite de forma proactiva:

CabeceraDescripcionEjemplo
X-RateLimit-LimitPeticiones permitidas por minuto100
X-RateLimit-RemainingPeticiones restantes en la ventana actual87
X-RateLimit-ResetTimestamp Unix (segundos) en que la ventana se reinicia1709312400

Ejemplo de respuesta con cabeceras de rate limiting:

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

Gestion del codigo 429:

Cuando recibes un 429, usa las cabeceras para calcular cuanto esperar antes de reintentar:

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

Recomendaciones:

  • Si recibes un 429, espera hasta el timestamp indicado en X-RateLimit-Reset antes de reintentar.
  • Monitoriza X-RateLimit-Remaining para frenar las peticiones antes de alcanzar el limite.
  • Distribuye las peticiones en el tiempo en lugar de enviar rafagas.

Tamano de peticion

El cuerpo de las peticiones POST, PUT y PATCH no puede exceder 1 MB. Peticiones mayores reciben un codigo 413.


Recursos

La API expone 8 recursos principales: invoices, expenses, clients, products, quotes, vendors, deposits y webhooks. Todos soportan operaciones CRUD completas (GET, POST, PUT/PATCH, DELETE). Ademas, los clientes tienen subcolecciones CRM: contacts, activities y notes.

PATCH vs PUT

Tanto PUT como PATCH aceptan actualizaciones parciales. No necesitas enviar el recurso completo -- solo los campos que quieras modificar.

Facturas (/invoices)

Listar facturas

GET /v1/invoices

Parametros de consulta:

ParametroTipoPor defectoDescripcion
limitinteger50Resultados por pagina (maximo 100)
offsetinteger0Numero de resultados a saltar (maximo 10.000)
statusstring--Filtrar por estado: draft, sent, partial, paid, overdue, cancelled
fromstring--Fecha inicio (ISO 8601: YYYY-MM-DD). Filtra por issueDate
tostring--Fecha fin (ISO 8601: YYYY-MM-DD). Filtra por issueDate
clientIdstring--Filtrar por ID de cliente
seriesIdstring--Filtrar por serie de numeracion
qstring--Busqueda de texto (nombre de cliente, numero, notas)
cursorstring--Cursor para paginacion basada en cursor (ignora offset)
fieldsstring--Campos a devolver, separados por coma (siempre incluye id)

Ejemplo:

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"

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

Obtener factura

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

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

Crear factura

POST /v1/invoices

Campos requeridos:

CampoTipoDescripcion
clientNamestringNombre del cliente (max 10.000 caracteres)
itemsarrayLista de lineas de la factura. Cada linea: { description, quantity, unitPrice }

Campos opcionales:

CampoTipoDescripcion
statusstringdraft (defecto), sent, partial, paid, overdue, cancelled
issueDatestringFecha de emision (ISO 8601). Por defecto: hoy
dueDatestringFecha de vencimiento (ISO 8601)
notesstringNotas internas (max 10.000 caracteres)
taxRatenumberPorcentaje de impuesto (0-100). Ej: 21 para IVA 21%

Estructura de cada linea (items[]):

CampoTipoRequeridoDescripcion
descriptionstringSiDescripcion del concepto (max 10.000 caracteres)
quantitynumberSiCantidad
unitPricenumberSiPrecio 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"
}'

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

Actualizar factura (PUT o PATCH)

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

Solo necesitas enviar los campos que quieras modificar. Los campos no incluidos permanecen sin cambios.

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

Respuesta (200): Objeto de factura actualizado.

precaución

Si envias items, debes enviar el array completo -- no se admiten actualizaciones parciales de lineas individuales.

Eliminar factura

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

Respuesta: 204 No Content

Descargar factura en PDF

GET /v1/invoices/:id/pdf

Devuelve el PDF de la factura como application/pdf con la cabecera Content-Disposition: attachment.

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

Enviar factura por email

POST /v1/invoices/:id/send

Envia la factura al destinatario especificado a traves de Resend. Si la factura esta en estado draft, se actualiza automaticamente a sent.

Campos:

CampoTipoRequeridoDescripcion
recipientEmailstringSiEmail del destinatario (max 255 caracteres)
recipientNamestringNoNombre del destinatario (max 200 caracteres)
customMessagestringNoMensaje personalizado en el cuerpo del email (max 5.000 caracteres)
localestringNoIdioma del email: es (defecto) 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"
}'

Respuesta (200):

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

Marcar factura como pagada

POST /v1/invoices/:id/paid
CampoTipoRequeridoDescripcion
paidDatestringNoFecha de pago (ISO 8601). Por defecto: hoy
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" }'

Respuesta (200):

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

Crear factura rectificativa (abono)

POST /v1/invoices/:id/credit-note

Crea un abono o nota de credito a partir de una factura existente.

CampoTipoRequeridoDescripcion
reasonstringSiMotivo: refund, discount, error, cancellation, other
typestringNoTipo R de rectificacion: R1, R2, R3, R4, R5. Si se omite, se infiere del campo reason
reasonDescriptionstringNoDescripcion libre del motivo
fullCreditbooleanNotrue = abono total (tipo S), false = parcial (tipo I). Por defecto: true
issueDatestringNoFecha de emision (YYYY-MM-DD). Por defecto: hoy
curl -X POST https://api.frihet.io/v1/invoices/abc123/credit-note \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"reason": "error",
"type": "R1",
"reasonDescription": "Correccion de importe",
"fullCredit": true
}'

Respuesta (201):

{
"success": true,
"creditNote": {
"id": "xyz789",
"documentNumber": "CN-F2026-0042",
"originalInvoiceId": "abc123",
"reason": "error",
"type": "R1",
"fullCredit": true,
"issueDate": "2026-03-28",
"createdAt": "2026-03-28T10:00:00.000Z"
}
}

Tipos R disponibles:

TipoDescripcion legal
R1Art. 80.1, 80.2, 80.6 y art. 6 del Real Decreto 1496/2003
R2Art. 80.3 — Concurso de acreedores
R3Art. 80.4 — Creditos incobrables
R4Resto de causas (devolucion, descuento, anulacion)
R5Facturas simplificadas (art. 7.2 del RD 1619/2012)

Mapeo automatico de reason a tipo R cuando type se omite:

MotivoTipo R inferido
errorR1
refundR4
discountR4
cancellationR4
otherR4

Descargar factura en XML (EN16931)

GET /v1/invoices/:id/xml

Devuelve la factura como fichero XML de e-factura EN16931 en formato UBL 2.1 o CII (Cross Industry Invoice), segun la configuracion de la cuenta.

Auth: API key requerida (X-API-Key).

Respuesta: application/xml con cabecera Content-Disposition: attachment.

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

Respuesta (200) — ejemplo UBL 2.1:

<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
<cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID>
<cbc:ID>F2026-0042</cbc:ID>
<cbc:IssueDate>2026-01-15</cbc:IssueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<!-- ... -->
</Invoice>

Errores posibles:

CodigoDescripcion
404Factura no encontrada
422La factura no tiene datos suficientes para generar el XML (p.ej., sin NIF del cliente)
Formato del XML

El formato (UBL 2.1 o CII) se configura en Ajustes > Facturacion > E-factura. UBL 2.1 es el formato por defecto y el requerido para VeriFactu y Facturae en Espana.

Aplicar recargo por demora

POST /v1/invoices/:id/late-fee

Aplica un recargo por demora a una factura vencida. Genera una nota de debito con el importe calculado segun la configuracion de recargos de la cuenta (Ajustes > Facturacion > Recargos por demora).

Auth: API key requerida (X-API-Key).

Requisito: La factura debe estar en estado overdue. Si la factura no esta vencida, la API devuelve 422.

Body: {} (vacio). Los parametros del recargo se toman de la configuracion de la cuenta.

curl -X POST "https://api.frihet.io/v1/invoices/abc123/late-fee" \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{}'

Respuesta (201):

{
"success": true,
"lateFee": {
"id": "lf_abc789",
"documentNumber": "DR-F2026-0001",
"originalInvoiceId": "abc123",
"type": "debit_note",
"feeAmount": 18.15,
"currency": "EUR",
"daysOverdue": 22,
"issueDate": "2026-03-28",
"createdAt": "2026-03-28T10:00:00.000Z"
}
}

Errores posibles:

CodigoDescripcion
404Factura no encontrada
422La factura no esta en estado overdue o ya tiene un recargo por demora activo
Configuracion de recargos

Define el porcentaje anual o importe fijo en Ajustes > Facturacion > Recargos por demora. El calculo usa la Ley 3/2004 (LMOC) para operaciones entre empresas por defecto.


Gastos (/expenses)

Listar gastos

GET /v1/expenses

Parametros de consulta:

ParametroTipoPor defectoDescripcion
limitinteger50Resultados por pagina (maximo 100)
offsetinteger0Numero de resultados a saltar
fromstring--Fecha inicio (ISO 8601). Filtra por date
tostring--Fecha fin (ISO 8601). Filtra por date
categorystring--Filtrar por categoria
vendorIdstring--Filtrar por ID de proveedor
qstring--Busqueda de texto (proveedor, descripcion, numero)
cursorstring--Cursor para paginacion basada en cursor
fieldsstring--Campos a devolver, separados por coma
curl "https://api.frihet.io/v1/expenses?limit=20&from=2026-01-01" \
-H "X-API-Key: fri_tu-clave-aqui"

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

Obtener gasto

GET /v1/expenses/:id

Crear gasto

POST /v1/expenses

Campos requeridos:

CampoTipoDescripcion
descriptionstringDescripcion del gasto (max 10.000 caracteres)
amountnumberImporte del gasto

Campos opcionales:

CampoTipoDescripcion
categorystringCategoria del gasto (max 10.000 caracteres)
datestringFecha del gasto (ISO 8601). Por defecto: hoy
vendorstringProveedor (max 10.000 caracteres)
taxDeductiblebooleanSi el gasto es deducible
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
}'

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

Actualizar gasto (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 }'

Respuesta (200): Objeto de gasto actualizado.

Eliminar gasto

DELETE /v1/expenses/:id

Respuesta: 204 No Content

Marcar gasto como facturable

POST /v1/expenses/:id/billable

Vincula un gasto a un cliente para que se pueda facturar. Opcionalmente aplica un markup.

CampoTipoRequeridoDescripcion
clientIdstringSiID del cliente al que facturar
markupnumberNoPorcentaje de markup (0-1000%)
curl -X POST https://api.frihet.io/v1/expenses/exp789/billable \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{ "clientId": "cli001", "markup": 15 }'

Para desvincular el gasto:

DELETE /v1/expenses/:id/billable

Clientes (/clients)

Listar clientes

GET /v1/clients

Parametros de consulta:

ParametroTipoPor defectoDescripcion
limitinteger50Resultados por pagina (maximo 100)
offsetinteger0Numero de resultados a saltar
fromstring--Fecha inicio (ISO 8601). Filtra por createdAt
tostring--Fecha fin (ISO 8601). Filtra por createdAt
stagestring--Filtrar por etapa del pipeline: lead, contacted, proposal, active, inactive, lost
qstring--Busqueda de texto (nombre, email, NIF)
cursorstring--Cursor para paginacion basada en cursor
fieldsstring--Campos a devolver, separados por coma
curl "https://api.frihet.io/v1/clients?limit=50" \
-H "X-API-Key: fri_tu-clave-aqui"

Obtener cliente

GET /v1/clients/:id

Crear cliente

POST /v1/clients

Campos requeridos:

CampoTipoDescripcion
namestringNombre del cliente (max 10.000 caracteres)

Campos opcionales:

CampoTipoDescripcion
emailstringEmail de contacto
phonestringTelefono
taxIdstringNIF/CIF/VAT
addressobjectDireccion (ver estructura abajo)

Estructura de address:

CampoTipoDescripcion
streetstringCalle y numero
citystringCiudad
statestringProvincia o estado
postalCodestringCodigo postal
countrystringPais

Todos los campos de address son opcionales.

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

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

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

Respuesta (200): Objeto de cliente actualizado.

Eliminar cliente

DELETE /v1/clients/:id

Respuesta: 204 No Content


CRM: Personas de contacto, Actividades y Notas

Los clientes tienen tres subcolecciones para gestionar relaciones CRM: personas de contacto, actividades y notas. Todos los endpoints requieren un clientId valido en la URL.

Personas de contacto (/v1/clients/:id/contacts)

Listar contactos
GET /v1/clients/:id/contacts
curl "https://api.frihet.io/v1/clients/cli001/contacts" \
-H "X-API-Key: fri_tu-clave-aqui"
Obtener contacto
GET /v1/clients/:id/contacts/:contactId
curl "https://api.frihet.io/v1/clients/cli001/contacts/con001" \
-H "X-API-Key: fri_tu-clave-aqui"
Crear contacto
POST /v1/clients/:id/contacts

Campos requeridos:

CampoTipoDescripcion
namestringNombre de la persona de contacto

Campos opcionales:

CampoTipoDescripcion
emailstringEmail del contacto
phonestringTelefono
rolestringCargo o rol (ej. "Director financiero")
isPrimarybooleanSi es el contacto principal 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
}'

Respuesta (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"
}
Actualizar contacto
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" }'

Respuesta (200): Objeto de contacto actualizado.

Eliminar contacto
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"

Respuesta: 204 No Content

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

El timeline de actividades registra las interacciones con un cliente. Las actividades del sistema (como invoice_created, quote_sent, etc.) se generan automaticamente. Tambien puedes crear actividades manuales.

Inmutables

Las actividades son inmutables. No se pueden actualizar ni eliminar una vez creadas.

Listar actividades
GET /v1/clients/:id/activities
curl "https://api.frihet.io/v1/clients/cli001/activities" \
-H "X-API-Key: fri_tu-clave-aqui"
Obtener actividad
GET /v1/clients/:id/activities/:activityId
curl "https://api.frihet.io/v1/clients/cli001/activities/act001" \
-H "X-API-Key: fri_tu-clave-aqui"
Crear actividad
POST /v1/clients/:id/activities

Campos requeridos:

CampoTipoDescripcion
typestringTipo de actividad: call, email, meeting o task
titlestringTitulo descriptivo de la actividad

Campos opcionales:

CampoTipoDescripcion
descriptionstringDescripcion detallada
metadataobjectDatos adicionales en formato libre
Tipos de actividad

Los tipos call, email, meeting y task son para actividades creadas manualmente. Los tipos del sistema como invoice_created, quote_sent o expense_linked se generan automaticamente y no se pueden crear 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"
}
}'

Respuesta (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"
Obtener 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"
Crear nota
POST /v1/clients/:id/notes

Campos requeridos:

CampoTipoDescripcion
contentstringContenido de la 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."
}'

Respuesta (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"
}
Actualizar 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."
}'

Respuesta (200): Objeto de nota actualizado.

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

Respuesta: 204 No Content


Productos (/products)

Listar productos

GET /v1/products

Acepta limit, offset, from y to (filtra por createdAt).

Obtener producto

GET /v1/products/:id

Crear producto

POST /v1/products

Campos requeridos:

CampoTipoDescripcion
namestringNombre del producto o servicio (max 10.000 caracteres)
unitPricenumberPrecio unitario

Campos opcionales:

CampoTipoDescripcion
descriptionstringDescripcion (max 10.000 caracteres)
taxRatenumberPorcentaje de impuesto (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
}'

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

Actualizar producto (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 }'

Respuesta (200): Objeto de producto actualizado.

Eliminar producto

DELETE /v1/products/:id

Respuesta: 204 No Content


Presupuestos (/quotes)

Listar presupuestos

GET /v1/quotes

Parametros de consulta:

ParametroTipoPor defectoDescripcion
limitinteger50Resultados por pagina (maximo 100)
offsetinteger0Numero de resultados a saltar
statusstring--Filtrar por estado: draft, sent, accepted, rejected, expired
fromstring--Fecha inicio (ISO 8601). Filtra por issueDate
tostring--Fecha fin (ISO 8601). Filtra por issueDate
curl "https://api.frihet.io/v1/quotes?status=sent" \
-H "X-API-Key: fri_tu-clave-aqui"

Obtener presupuesto

GET /v1/quotes/:id

Crear presupuesto

POST /v1/quotes

Campos requeridos:

CampoTipoDescripcion
clientNamestringNombre del cliente (max 10.000 caracteres)
itemsarrayLineas del presupuesto. Cada linea: { description, quantity, unitPrice }

Campos opcionales:

CampoTipoDescripcion
validUntilstringFecha de validez (ISO 8601)
notesstringNotas o condiciones (max 10.000 caracteres)
statusstringdraft (defecto), 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"
}'

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

Actualizar presupuesto (PUT o PATCH)

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

Eliminar presupuesto

DELETE /v1/quotes/:id

Respuesta: 204 No Content

Descargar presupuesto en PDF

GET /v1/quotes/:id/pdf

Funciona igual que /invoices/:id/pdf. Devuelve application/pdf.

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

Enviar presupuesto por email

POST /v1/quotes/:id/send

Mismos campos que /invoices/:id/send (recipientEmail, recipientName, customMessage, locale). Si el presupuesto esta en estado draft, se actualiza automaticamente a sent.


Proveedores (/vendors)

Listar proveedores

GET /v1/vendors

Acepta limit, offset, from, to (filtra por createdAt), q, cursor, fields.

Obtener proveedor

GET /v1/vendors/:id

Crear proveedor

POST /v1/vendors

Campos requeridos:

CampoTipoDescripcion
namestringNombre del proveedor

Campos opcionales:

CampoTipoDescripcion
emailstringEmail de contacto
phonestringTelefono
taxIdstringNIF/CIF/VAT
addressobjectDireccion (street, city, province, zip, country, countryCode)
curl -X POST https://api.frihet.io/v1/vendors \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"name": "Adobe Inc.",
"email": "billing@adobe.com",
"taxId": "US87-1166924"
}'

Actualizar proveedor (PUT o PATCH)

PUT /v1/vendors/:id
PATCH /v1/vendors/:id

Eliminar proveedor

DELETE /v1/vendors/:id

Respuesta: 204 No Content


Depositos (/deposits)

Los depositos permiten registrar pagos anticipados o senales de clientes antes de emitir la factura final.

Listar depositos

GET /v1/deposits

Acepta limit, offset, from, to (filtra por receivedDate), q, cursor, fields.

Obtener deposito

GET /v1/deposits/:id

Crear deposito

POST /v1/deposits

Campos requeridos:

CampoTipoDescripcion
clientIdstringID del cliente
clientNamestringNombre del cliente
amountnumberImporte del deposito (positivo)
descriptionstringDescripcion del deposito
receivedDatestringFecha de recepcion (YYYY-MM-DD)

Campos opcionales:

CampoTipoDescripcion
currencystringCodigo ISO 4217 (3 caracteres). Por defecto: EUR
paymentMethodstringMetodo de pago
paymentReferencestringReferencia del pago
curl -X POST https://api.frihet.io/v1/deposits \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"clientId": "cli001",
"clientName": "Acme S.L.",
"amount": 2000,
"description": "Señal proyecto Q2",
"receivedDate": "2026-03-15"
}'

Actualizar deposito (PUT o PATCH)

PUT /v1/deposits/:id
PATCH /v1/deposits/:id

Eliminar deposito

DELETE /v1/deposits/:id

Respuesta: 204 No Content

Aplicar deposito a una factura

POST /v1/deposits/:id/apply

Descuenta el deposito (total o parcial) del importe de una factura.

CampoTipoRequeridoDescripcion
invoiceIdstringSiID de la factura
invoiceNumberstringSiNumero de la factura
amountnumberSiImporte a aplicar
curl -X POST https://api.frihet.io/v1/deposits/dep001/apply \
-H "X-API-Key: fri_tu-clave-aqui" \
-H "Content-Type: application/json" \
-d '{
"invoiceId": "abc123",
"invoiceNumber": "F2026-0042",
"amount": 1500
}'

Reembolsar deposito

POST /v1/deposits/:id/refund
CampoTipoRequeridoDescripcion
amountnumberNoImporte a reembolsar. Si se omite, se reembolsa el saldo restante completo

Operaciones por lote (/batch)

Todos los recursos principales soportan creacion en lote. Envia un array de hasta 50 elementos en una sola peticion.

POST /v1/{resource}/batch

Recursos soportados: invoices, expenses, clients, vendors, products, quotes, deposits

Cuerpo de la peticion:

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

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

Si alguno de los elementos falla la validacion, el resto se crea igualmente. Los errores individuales se devuelven en el 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:

ConceptoLimite
Elementos por lote50 maximo
Tamano de peticion1 MB maximo

Idempotencia

Las peticiones POST aceptan una cabecera Idempotency-Key para evitar la creacion duplicada de recursos ante reintentos de red. Si la misma clave se envia dentro de las siguientes 24 horas, la API devuelve la respuesta original sin ejecutar la operacion de nuevo.

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

Comportamiento:

EscenarioResultado
Primera peticion con la claveSe crea el recurso normalmente
Peticion repetida con la misma clave (dentro de 24h)Se devuelve la respuesta original, sin duplicar
Misma clave despues de 24hSe trata como una peticion nueva

Cabecera de respuesta:

Cuando la API detecta una clave repetida, incluye la cabecera X-Idempotent-Replayed: true para que el consumidor sepa que la respuesta es una replica.

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

Requisitos:

  • La clave debe tener como maximo 64 caracteres
  • Recomendamos usar UUID v4
  • Solo aplica a peticiones POST (crear recursos)

Busqueda

Los endpoints de listado soportan busqueda de texto completo mediante el parametro q:

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

La busqueda se aplica sobre los campos de texto principales del recurso (nombre de cliente, descripcion, notas, etc.). Se puede combinar con los filtros existentes (status, from, to).


Endpoints de inteligencia

Estos endpoints proporcionan datos agregados y contexto de negocio. Son especialmente utiles para agentes de IA y dashboards externos.

Contexto de negocio (/context)

GET /v1/context

Devuelve un resumen completo del negocio, pensado para alimentar a agentes de IA con el contexto necesario para tomar decisiones informadas. Incluye resumen financiero, actividad reciente, alertas y configuracion fiscal.

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

Respuesta (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 mensual (/monthly)

GET /v1/monthly?month=YYYY-MM

Devuelve la cuenta de resultados (P&L) de un mes concreto: ingresos facturados, gastos por categoria, beneficio neto y comparativa con el mes anterior.

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

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

Cifras fiscales trimestrales (/quarterly)

GET /v1/quarterly?quarter=YYYY-Q1

Devuelve las cifras fiscales de un trimestre, preparadas para la presentacion del Modelo 303 (IVA) y Modelo 130 (IRPF). Incluye bases imponibles, cuotas devengadas, cuotas deducibles y resultado de la liquidacion.

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

Respuesta (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
}
}
tip

Los endpoints /context, /monthly y /quarterly estan disenados para ser el punto de entrada ideal para agentes de IA. Proporcionan la informacion necesaria en una sola llamada, sin necesidad de consultar multiples endpoints individuales.


Dashboard financiero (/summary)

GET /v1/summary

Devuelve un resumen financiero del negocio: ingresos, gastos, beneficio y contadores.

Parametros de consulta:

ParametroTipoDescripcion
fromstringFecha inicio (ISO 8601)
tostringFecha fin (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"

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

Codigos de error

La API utiliza codigos HTTP estandar. Las respuestas de error incluyen un objeto JSON con los campos error y, opcionalmente, message y details.

CodigoSignificadoDescripcion
400Bad RequestFalta un campo requerido, formato incorrecto o campo no permitido
401UnauthorizedLa API key no se ha proporcionado, es invalida, tiene formato incorrecto o ha expirado
403ForbiddenLa API key no tiene permisos para acceder a este recurso
404Not FoundEl recurso solicitado no existe
405Method Not AllowedEl metodo HTTP no esta soportado para este endpoint
413Payload Too LargeEl cuerpo de la peticion excede 1 MB
422Unprocessable EntityDatos validos pero el servidor no puede procesarlos (ej: perfil fiscal no configurado)
429Too Many RequestsSe ha excedido el limite de 100 peticiones por minuto
500Internal Server ErrorError interno del servidor

Estructura de las respuestas de error

Error de validacion (400):

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

Los errores de validacion usan el formato de Zod. El campo path indica que campo contiene el error y message describe el problema.

Clave invalida o expirada (401):

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

Formato de clave invalido (401):

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

Recurso no encontrado (404):

{
"error": "Resource not found"
}

Estado invalido en 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
}

Error interno (500):

{
"error": "Internal server error"
}

Paginacion

Los endpoints de listado devuelven resultados paginados con la siguiente estructura:

{
"data": [],
"total": 142,
"limit": 50,
"offset": 0
}
  • total: numero total de registros que cumplen los filtros aplicados
  • limit: numero de registros devueltos en esta pagina (maximo 100)
  • offset: numero de registros saltados (maximo 10.000)

Para obtener la siguiente pagina:

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

Los resultados se ordenan por la fecha natural del recurso en orden descendente (mas recientes primero):

  • Facturas y presupuestos: issueDate
  • Gastos: date
  • Clientes y productos: createdAt

Validacion estricta

La API usa validacion estricta (Zod strict mode). Las peticiones con campos desconocidos se rechazan con un error 400:

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

Esto previene errores silenciosos por typos en los nombres de los campos.


CORS

La API soporta CORS para peticiones desde el navegador. Los origenes permitidos son:

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

Para integraciones server-to-server, CORS no es relevante. Si necesitas acceder a la API desde un dominio diferente en el navegador, utiliza un proxy en tu backend.


Cabeceras de seguridad

Todas las respuestas incluyen cabeceras de seguridad:

CabeceraValor
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
X-XSS-Protection1; mode=block
X-API-VersionVersion de la API (2026-03-18)
X-Request-IdID unico de la peticion (util para depuracion)

OAuth para MCP

El endpoint POST /api/oauth/api-key permite provisionar una API key automaticamente a traves del flujo OAuth de MCP. El servidor MCP usa este endpoint internamente -- no necesitas llamarlo directamente.

El flujo:

  1. El usuario se autentica via Firebase Auth
  2. El cliente MCP envia el token de Firebase a /api/oauth/api-key
  3. El servidor devuelve una nueva clave fri_xxx... etiquetada como "MCP OAuth"
  4. La clave expira a los 365 dias

Limite: 5 claves activas por usuario. Si se supera el limite, el endpoint responde con un 429.


Buenas practicas

  1. Almacena la API key de forma segura. Nunca la incluyas en codigo frontend, repositorios publicos o logs.
  2. Gestiona el rate limiting. Implementa backoff exponencial si recibes un 429.
  3. Usa paginacion. No pidas todos los registros de golpe; itera con limit y offset.
  4. Verifica los codigos de respuesta. No asumas que todas las peticiones seran exitosas.
  5. Rota las claves periodicamente. Crea una nueva clave, actualiza tu integracion y revoca la anterior.
  6. Usa HTTPS siempre. Todas las peticiones a la API deben ser sobre HTTPS.
  7. Usa filtros. Los parametros status, from y to reducen la cantidad de datos transferidos.
  8. Aprovecha PATCH. Para actualizaciones parciales, envia solo los campos modificados.