Descripción general
La API de Manilo es una API REST JSON para gestionar mediante código el mismo registro que usas en la app de iOS: cuentas, categorías, presupuestos, grupos compartidos y transacciones. Úsala para crear importadores, exportadores, puentes de sincronización o paneles, o para impulsar tus propias automatizaciones.
- URL base:
https://api.manilo.app— la URL base anteriorapi.ledgy.appsigue funcionando. - Prefijo de versión: todos los endpoints de este documento están montados bajo
/api/v1/. - Transporte: solo HTTPS. Las peticiones HTTP no se aceptan.
- Codificación: cuerpos de petición y respuesta en JSON. UTF-8. Nombres de propiedades en
camelCase. - Autenticación:
Authorization: Bearer …en cada petición. - Suscripción a Cloud: obligatoria en todos los endpoints de v1. Consulta Control de suscripción.
https://api.manilo.app/mcp — consulta Integraciones. La API REST documentada aquí es para el código que escribes tú.
Inicio rápido
Tres pasos hasta tu primera petición autenticada.
1. Genera un Personal Access Token
- Inicia sesión en tu panel de Manilo y abre Configuración → Acceso a la API.
- Haz clic en + Nuevo token.
- Ponle al token un nombre descriptivo (p. ej. “Zapier — exportación semanal”), elige los scopes que necesites (consulta Scopes) y, si quieres, fija una fecha de expiración.
- Copia el token. Se muestra una sola vez. Los tokens empiezan con el prefijo
lgpat_seguido de 64 caracteres hexadecimales.
Esa misma página lista tus tokens activos con la fecha de último uso y el número de permisos, muestra un aviso de “Expira en 30 días” y te permite revocar cualquier token al instante con el icono de la papelera. Cada cuenta puede tener hasta 25 tokens activos a la vez.
2. Haz una petición
# Replace lgpat_… with your token curl "https://api.manilo.app/api/v1/accounts" \ -H "Authorization: Bearer lgpat_a1b2c3d4e5…"
3. Revisa la respuesta
{
"items": [
{
"id": "acc_01HK8V…",
"name": "Wise EUR",
"currencyCode": "EUR",
"initialBalance": 1240.50,
"icon": "wallet",
"color": "#4A90E2",
"order": 0,
"createdAt": "2026-04-12T08:13:09Z",
"updatedAt": "2026-05-01T17:02:31Z"
}
],
"totalCount": 1
}
Autenticación
Toda petición a /api/v1/ debe llevar una cabecera Authorization. Se aceptan dos tipos de token:
- Personal Access Token (PAT) — token Bearer de larga duración que creas desde la página Configuración → Acceso a la API del panel. Formato:
lgpat_+ 64 caracteres hexadecimales. Con scopes, revocable y con expiración opcional. Recomendado para todas las integraciones de terceros. - JWT de sesión — token de corta duración emitido a las apps propias (iOS, panel). No tiene restricciones de scope. Puedes usarlo para pruebas puntuales si logras extraer uno de una sesión iniciada, pero los PAT son la vía soportada.
Formato de la cabecera
Authorization: Bearer lgpat_a1b2c3d4e5f6…
Límites de tokens
- Hasta 25 PAT activos por cuenta de Manilo.
- Se puede fijar una expiración opcional al momento de crearlo. Los tokens expirados devuelven
401 Unauthorized. - Los tokens revocados dejan de funcionar de inmediato: Manilo guarda solo un hash SHA-256 del token, nunca el valor, así que un token filtrado no se puede recuperar, solo revocar y reemplazar.
Fallos de autenticación comunes
- 401
- Token ausente, mal formado, expirado o revocado.
- 403
- El token es válido, pero el endpoint solicitado requiere un scope que tu PAT no tiene, o tu suscripción a Cloud no está activa.
Scopes
Los PAT siguen un modelo de denegación por defecto. Un token solo puede llamar a los endpoints cuyo scope requerido posea; todo lo demás devuelve 403 Forbidden. Otorga el conjunto de scopes más reducido que tu integración realmente necesite.
Scopes disponibles:
accounts:read
accounts:write
categories:read
categories:write
budgets:read
budgets:write
groups:read
groups:write
transactions:read
transactions:write
tags:read
tags:write
recurring:read
recurring:write
settings:read
settings:write
Cada endpoint listado abajo muestra el scope que requiere en una pequeña etiqueta morada. Los scopes :write no implican :read — solicita ambos si necesitas ambos.
Control de suscripción
Todos los endpoints de v1 — incluidos los de solo lectura — requieren que el usuario que llama tenga una suscripción activa a Manilo Cloud. Si la suscripción caducó, expiró o nunca se inició, la API responde con:
HTTP/1.1 403 Forbidden
X-Subscription-Required: true
Content-Type: application/json
{ "error": "Active cloud subscription required" }
La cabecera X-Subscription-Required permite a los clientes distinguir un bloqueo por suscripción de una denegación de permisos genérica. Restaura el acceso reactivando Cloud en la app de iOS o en dashboard.manilo.app/upgrade.
Errores
Los errores usan códigos de estado HTTP estándar. El cuerpo de la respuesta es un objeto JSON de un solo campo:
{ "error": "Human-readable message" }
Códigos de estado que deberías manejar:
- 200
- OK — recurso devuelto, o lista devuelta.
- 201
- Created — recurso nuevo creado. La cabecera
Locationapunta a la URL canónica. - 204
- No Content — eliminación exitosa; sin cuerpo.
- 400
- Falló la validación — falta un campo obligatorio, valor fuera de rango, JSON mal formado.
- 401
- Falló la autenticación — consulta Autenticación.
- 403
- Permiso denegado — scope insuficiente o suscripción inactiva.
- 404
- Recurso no encontrado, u oculto por autorización.
- 409
- Conflicto — p. ej. violación de una restricción de unicidad.
- 5xx
- Fallo del lado del servidor. Es seguro reintentar lecturas idempotentes con retroceso exponencial.
Paginación y filtros
Los endpoints de listado devuelven por defecto todos los elementos coincidentes. Las transacciones, el único recurso que puede crecer mucho, admiten paginación por cursor.
Cursor de transacciones
curl "https://api.manilo.app/api/v1/transactions?limit=50&type=expense&dateFrom=2026-01-01" \ -H "Authorization: Bearer lgpat_…"
La respuesta incluye un nextCursor. Devuélvelo en el parámetro de consulta cursor para obtener la siguiente página; cuando nextCursor sea null habrás llegado al final.
limit— tamaño de página, limitado a1..200. Por defecto50.cursor— token opaco. Trátalo como una caja negra.type,dateFrom,dateTo,categoryId,accountId,groupId— filtros opcionales; consulta el endpoint Listar transacciones.
Tipos y formatos
- IDs — cadenas opacas. No las analices; trátalas como identificadores UTF-8 sensibles a mayúsculas y minúsculas.
- Marcas de tiempo — ISO-8601 en UTC, terminadas en
Z, p. ej."2026-05-13T10:30:00Z". - Fechas (p. ej. el campo
datede una transacción) — el mismo formato ISO-8601, pero solo la parte de la fecha es significativa. - Montos — números JSON en unidades mayores con hasta 4 decimales (p. ej.
12.50). Nunca en unidades menores. Siempre acompañados decurrencyCode. - Códigos de moneda — ISO-4217, exactamente tres letras mayúsculas (p. ej.
"EUR","USD","GBP"). - Eliminaciones — todas las operaciones de eliminación son eliminaciones lógicas. Los elementos eliminados dejan de aparecer en las respuestas de listado y de consulta; se conservan los participantes de los recursos compartidos y los recibos históricos.
- Efectos secundarios — eliminar una cuenta, una categoría o un grupo deja intactas las transacciones dependientes; sus referencias se desvinculan. El endpoint Eliminar cuenta acepta una estrategia explícita.
Cuentas
Las cuentas son los contenedores que guardan saldos: una cuenta bancaria, una tarjeta de crédito, una cartera de efectivo, una cuenta de inversión. Cada transacción está vinculada a una (o a dos, en las transferencias).
/api/v1/accounts
accounts:read
Devuelve todas las cuentas que el usuario autenticado posee o a las que tiene acceso.
/api/v1/accounts/{id}
accounts:read
Obtén una cuenta por su id. 404 si no se encuentra.
/api/v1/accounts
accounts:write
Crea una cuenta nueva. Devuelve 201 con el objeto creado y una cabecera Location.
| Campo | Tipo | Descripción |
|---|---|---|
| name | string | obligatorioNombre visible. Máx. 100 caracteres. |
| currencyCode | string | obligatorioISO-4217. Exactamente 3 letras. |
| initialBalance | number | obligatorioSaldo inicial en currencyCode. |
| order | integer | obligatorioPosición de orden. El menor va primero. |
| icon | string | opcionalIdentificador de icono de /api/v1/icons. Máx. 50. |
| color | string | opcionalColor hexadecimal, p. ej. "#4A90E2". Máx. 20. |
| iconColor | string | opcionalSobrescribe el tinte del icono. |
POST /api/v1/accounts
{
"name": "Cash",
"currencyCode": "EUR",
"initialBalance": 50.00,
"order": 2,
"icon": "wallet",
"color": "#22C55E"
}
{
"id": "acc_01HK8V…",
"name": "Cash",
"currencyCode": "EUR",
"initialBalance": 50.00,
"order": 2,
"icon": "wallet",
"color": "#22C55E",
"iconColor": null,
"shareInviteToken": null,
"createdAt": "2026-05-13T10:30:00Z",
"updatedAt": "2026-05-13T10:30:00Z"
}
/api/v1/accounts/{id}
accounts:write
Reemplaza una cuenta existente. El cuerpo es idéntico al de Crear; deben enviarse todos los campos.
/api/v1/accounts/{id}
accounts:write
Elimina lógicamente una cuenta. Decide qué pasa con sus transacciones mediante el parámetro de consulta action.
| Campo | Tipo | Descripción |
|---|---|---|
| action | enum | opcionalDetach (por defecto): borra la referencia a la cuenta en cada transacción. Move: reasigna las transacciones a moveTargetAccountId. DeleteAll: elimina lógicamente todas las transacciones vinculadas que te pertenezcan. |
| moveTargetAccountId | string | opcionalObligatorio cuando action=Move. Id de la cuenta de destino. |
Categorías
Las categorías etiquetan para qué es una transacción (supermercado, renta, ingresos de freelance). Las categorías del sistema son de solo lectura y las comparten todos los usuarios; las categorías de usuario las gestionas tú. Los grupos de categorías agrupan categorías relacionadas.
/api/v1/categories/system-categories
categories:read
Devuelve el conjunto curado de categorías “conocidas” de Manilo: el conjunto inicial que trae la app de iOS. Están versionadas globalmente y se pueden cachear con seguridad por version.
Categorías de usuario
/api/v1/categories
categories:read
Lista todas las categorías definidas por el usuario.
/api/v1/categories/{id}
categories:read
Obtén una categoría de usuario por su id.
/api/v1/categories
categories:write
Crea una categoría de usuario.
| Campo | Tipo | Descripción |
|---|---|---|
| name | string | obligatorioNombre visible. Máx. 100. |
| type | string | obligatorio"income" o "expense". |
| order | integer | obligatorioPosición de orden dentro de su grupo. |
| isPinned | boolean | obligatorioFijar al inicio del selector. |
| categoryGroupId | string | opcionalId del grupo padre, o null si no tiene grupo. |
| icon | string | opcionalIdentificador de icono. |
| color | string | opcionalColor hexadecimal. |
/api/v1/categories/{id}
categories:write
Reemplaza una categoría de usuario. El cuerpo es idéntico al de Crear.
/api/v1/categories/{id}
categories:write
Elimina lógicamente una categoría de usuario. Las transacciones no se eliminan; su categoryId se borra.
Grupos de categorías
/api/v1/categories/groups
categories:read
Lista tus grupos de categorías.
/api/v1/categories/groups/{id}
categories:read
Obtén un grupo de categorías.
/api/v1/categories/groups
categories:write
Crea un grupo de categorías.
| Campo | Tipo | Descripción |
|---|---|---|
| name | string | obligatorioNombre visible. Máx. 100. |
| order | integer | obligatorioPosición de orden. |
| icon | string | opcionalIdentificador de icono. |
| color | string | opcionalColor hexadecimal. |
/api/v1/categories/groups/{id}
categories:write
Reemplaza un grupo de categorías.
/api/v1/categories/groups/{id}
categories:write
Elimina lógicamente un grupo. Las categorías hijas sobreviven: su categoryGroupId se borra.
Presupuestos
Un presupuesto limita el gasto de una categoría (o, cuando categoryId es null, del registro completo) durante una ventana recurrente. Comparte un presupuesto con un grupo estableciendo groupId.
/api/v1/budgets
budgets:read
Lista todos los presupuestos.
/api/v1/budgets/{id}
budgets:read
Obtén un presupuesto.
/api/v1/budgets
budgets:write
Crea un presupuesto.
| Campo | Tipo | Descripción |
|---|---|---|
| amount | number | obligatorioLímite por periodo. Debe ser mayor que 0. |
| currencyCode | string | obligatorioISO-4217. |
| period | integer | obligatorio0 Semanal · 1 Mensual · 2 Trimestral · 3 Anual. |
| startDate | string (ISO-8601) | obligatorioInicio del primer periodo. |
| isActive | boolean | obligatorioSi este presupuesto se aplica actualmente. |
| endDate | string (ISO-8601) | opcionalDeja de dar seguimiento después de esta fecha. |
| name | string | opcionalEtiqueta. Máx. 200. |
| categoryId | string | opcionalCategoría a la que dar seguimiento. Omítelo para presupuestar todo el gasto. |
| groupId | string | opcionalGrupo con el que compartir. Omítelo para que sea personal. |
/api/v1/budgets/{id}
budgets:write
Reemplaza un presupuesto.
/api/v1/budgets/{id}
budgets:write
Elimina lógicamente un presupuesto.
Grupos
Los grupos son registros compartidos: un hogar, un viaje, un departamento compartido. Todos los miembros ven las mismas transacciones; la propiedad sigue siendo personal. La membresía y las invitaciones se gestionan en la app de iOS; lo que la API expone aquí es el recurso en sí.
/api/v1/groups
groups:read
Lista los grupos que posees o de los que eres miembro.
/api/v1/groups/{id}
groups:read
Obtén un grupo.
/api/v1/groups
groups:write
Crea un grupo. Te conviertes en su propietario; invita a los miembros desde la app de iOS.
| Campo | Tipo | Descripción |
|---|---|---|
| name | string | obligatorioNombre visible. Máx. 100. |
| description | string | opcionalTexto libre. Máx. 500. |
| icon | string | opcionalIdentificador de icono. |
| color | string | opcionalColor hexadecimal. |
/api/v1/groups/{id}
groups:write
Reemplaza los metadatos de un grupo.
/api/v1/groups/{id}
groups:write
Elimina lógicamente un grupo. Los miembros pierden la visibilidad; las transacciones subyacentes vuelven a ser personales.
Transacciones
Las transacciones son los verbos del registro. Existen en cuatro formas: income, expense, transfer (de cuenta a cuenta) y adjustment (reajuste puntual). El endpoint base crea ingresos y gastos; las transferencias tienen su propio endpoint; existen variantes masivas para importadores de gran volumen.
/api/v1/transactions
transactions:read
Lista transacciones con paginación por cursor y filtros.
| Campo | Tipo | Descripción |
|---|---|---|
| limit | integer | opcionalTamaño de página. 1..200. Por defecto 50. |
| cursor | string | opcionalToken de continuación opaco de la respuesta anterior. |
| type | string | opcional"income", "expense", "transfer" o "adjustment". |
| dateFrom | string (ISO-8601) | opcionalLímite inferior inclusivo. |
| dateTo | string (ISO-8601) | opcionalLímite superior inclusivo. |
| categoryId | string | opcionalFiltrar por una categoría. |
| accountId | string | opcionalFiltrar por una cuenta. |
| groupId | string | opcionalFiltrar por un grupo compartido. |
{
"items": [ /* TransactionDto[] */ ],
"totalCount": 317,
"nextCursor": "eyJrIjoiMjAyNi0wNS0xM1QxMDoz…"
}
/api/v1/transactions/{id}
transactions:read
Obtén una transacción.
/api/v1/transactions
transactions:write
Crea una sola transacción de ingreso o gasto. Para transferencias, usa /transfer.
| Campo | Tipo | Descripción |
|---|---|---|
| type | string | obligatorio"income" o "expense". |
| amount | number | obligatorioMonto positivo en currencyCode. |
| currencyCode | string | obligatorioISO-4217. |
| date | string (ISO-8601) | obligatorioCuándo ocurrió la transacción (UTC). |
| accountId | string | opcionalCuenta de origen o de destino. |
| categoryId | string | opcionalEtiqueta de categoría. |
| payee | string | opcionalComercio o contraparte. Máx. 200. |
| note | string | opcionalNota de texto libre. Máx. 2000. |
| groupId | string | opcionalCompartir con un grupo. |
| exchangeRate | number | opcionalTipo de cambio cuando currencyCode ≠ la moneda principal del usuario. |
| convertedAmount | number | opcionalMonto en la moneda principal del usuario. |
POST /api/v1/transactions
{
"type": "expense",
"amount": 8.50,
"currencyCode": "EUR",
"date": "2026-05-12T09:14:00Z",
"accountId": "acc_01HK8V…",
"categoryId": "cat_food_drinks",
"payee": "Pret",
"note": "Flat white"
}
/api/v1/transactions/{id}
transactions:write
Reemplaza una transacción. El cuerpo es idéntico al de Crear.
/api/v1/transactions/{id}
transactions:write
Elimina lógicamente una transacción.
/api/v1/transactions/transfer
transactions:write
Crea una transferencia entre dos cuentas. Sin categoría. Para transferencias entre monedas distintas, proporciona exchangeRate y convertedAmount en la moneda de destino.
| Campo | Tipo | Descripción |
|---|---|---|
| fromAccountId | string | obligatorioCuenta de origen. |
| toAccountId | string | obligatorioCuenta de destino. Debe ser distinta de la de origen. |
| amount | number | obligatorioMonto enviado, en currencyCode. |
| currencyCode | string | obligatorioMoneda de origen, ISO-4217. |
| date | string (ISO-8601) | obligatorioFecha de la transferencia. |
| exchangeRate | number | opcionalObligatorio cuando las monedas de origen y destino difieren. |
| convertedAmount | number | opcionalMonto acreditado en el destino, en su moneda. |
| note | string | opcionalMáx. 2000. |
Operaciones masivas
Diseñadas para importadores. Cada lote está limitado a 100 elementos y se ejecuta en modo best-effort: una sola fila incorrecta no revierte las demás. Los elementos exitosos y los errores por fila se reportan por separado para que puedas reintentar los fallidos.
/api/v1/transactions/bulk
transactions:write
Crea hasta 100 transacciones en una sola llamada.
{
"items": [ /* successful TransactionDto[] */ ],
"errors": [
{ "index": 3, "error": "Invalid currency code" }
]
}
/api/v1/transactions/bulk
transactions:write
Actualiza hasta 100 transacciones en una sola llamada. Cada elemento debe incluir su id junto con el cuerpo completo de la transacción.
/api/v1/transactions/bulk-delete
transactions:write
Elimina lógicamente hasta 100 transacciones en una sola llamada. Usa POST en lugar de DELETE para que todos los clientes HTTP acepten el cuerpo de la petición.
| Campo | Tipo | Descripción |
|---|---|---|
| ids | string[] | obligatorioDe 1 a 100 ids de transacción. |
{
"deleted": 97,
"notFound": [ "tx_old1", "tx_old2", "tx_old3" ]
}
El objeto transacción
| Campo | Tipo | Descripción |
|---|---|---|
| id | string | Identificador opaco. |
| type | string | income · expense · transfer · adjustment. |
| amount | number | En currencyCode. |
| currencyCode | string | ISO-4217. |
| exchangeRate | number | null | Se establece en los registros entre monedas distintas. |
| convertedAmount | number | null | El mismo valor en la moneda principal del usuario. |
| date | string | ISO-8601 UTC. |
| accountId | string | null | En las transferencias, esta es la cuenta de origen. |
| toAccountId | string | null | Cuenta de destino, solo en transferencias. |
| categoryId | string | null | Null en las transferencias. |
| groupId | string | null | Se establece si se comparte con un grupo. |
| payee | string | null | Comercio o contraparte. |
| note | string | null | Texto libre. |
| receiptImagePath | string | null | Ruta al recibo adjunto. Se obtiene por el endpoint de recibos (independiente de v1). |
| recurringTransactionId | string | null | Se establece si esta fila se generó a partir de una regla recurrente. |
| source | string | Etiqueta de origen — api, mobile, web, import, etc. |
| createdAt | string | ISO-8601 UTC. |
| updatedAt | string | ISO-8601 UTC. |
Iconos
Manilo incluye un conjunto curado de iconos y una paleta de colores que se usan en todas partes: cuentas, categorías y grupos. Obtén el catálogo una vez, guárdalo en caché y reutiliza los identificadores al crear recursos.
/api/v1/icons
Devuelve la biblioteca completa de iconos, agrupada por categorías, junto con la paleta de colores admitida. Está versionada: se puede cachear con seguridad por el campo version.
{
"version": "2026.05.10",
"library": "font-awesome-6",
"categories": [
{
"id": "finance",
"name": "Finance",
"icons": [ "wallet", "credit-card", "piggy-bank" ]
}
],
"colors": [
{ "name": "Blue", "hex": "#4A90E2" },
{ "name": "Green", "hex": "#22C55E" }
]
}
Versionado
- Sin cambios que rompan compatibilidad dentro de v1. Solo añadiremos endpoints nuevos, campos opcionales nuevos y valores de enumeración nuevos. El tipo de un campo, su nulabilidad o su obligatoriedad no cambiarán.
- Los valores de enumeración nuevos no rompen la compatibilidad. Trata los valores desconocidos de
type,sourceoactioncomo “no renderizar” en lugar de fallar: los añadiremos conforme crezca el producto. - Los cambios que rompan la compatibilidad (si alguna vez son necesarios) se publicarán bajo
/api/v2/con al menos 6 meses de disponibilidad en paralelo y una cabecera de obsolescencia en las respuestas de v1.
Soporte
¿Encontraste un error, quieres un endpoint o topaste con algo sin documentar? Abre el centro de ayuda o escribe a support@manilo.app — incluye el id de la petición (que se devuelve en la cabecera de respuesta X-Request-Id) al reportar un problema.
Para divulgaciones sensibles de seguridad (una filtración de token, un bypass de permisos, una lectura no autorizada) escribe a security@manilo.app.