Desarrolladores

Referencia de la API v1

REST · JSON · OAuth 2.0 Bearer https://api.manilo.app Estable — sin cambios que rompan compatibilidad dentro de v1

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 anterior api.ledgy.app sigue 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.
¿Buscas un asistente de IA? Si quieres que Claude, ChatGPT o Cursor hablen con Manilo en tu nombre, usa el endpoint de Model Context Protocol en 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

  1. Inicia sesión en tu panel de Manilo y abre Configuración → Acceso a la API.
  2. Haz clic en + Nuevo token.
  3. 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.
  4. 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

cURLListar cuentas
# Replace lgpat_… with your token
curl "https://api.manilo.app/api/v1/accounts" \
  -H "Authorization: Bearer lgpat_a1b2c3d4e5…"

3. Revisa la respuesta

200 OKapplication/json
{
  "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
}
Trata tu token como una contraseña. Cualquiera que lo tenga puede leer o modificar tu registro dentro de los scopes que le otorgaste. Revoca de inmediato los tokens comprometidos desde Configuración → Acceso a la API.

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

HTTP
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:

403 Forbidden
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:

JSON
{ "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 Location apunta 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

cURLListado paginado
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 a 1..200. Por defecto 50.
  • 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 date de 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 de currencyCode.
  • 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).

GET /api/v1/accounts accounts:read

Devuelve todas las cuentas que el usuario autenticado posee o a las que tiene acceso.

GET /api/v1/accounts/{id} accounts:read

Obtén una cuenta por su id. 404 si no se encuentra.

POST /api/v1/accounts accounts:write

Crea una cuenta nueva. Devuelve 201 con el objeto creado y una cabecera Location.

Cuerpo de la petición
CampoTipoDescripción
namestringobligatorioNombre visible. Máx. 100 caracteres.
currencyCodestringobligatorioISO-4217. Exactamente 3 letras.
initialBalancenumberobligatorioSaldo inicial en currencyCode.
orderintegerobligatorioPosición de orden. El menor va primero.
iconstringopcionalIdentificador de icono de /api/v1/icons. Máx. 50.
colorstringopcionalColor hexadecimal, p. ej. "#4A90E2". Máx. 20.
iconColorstringopcionalSobrescribe el tinte del icono.
Petición
POST /api/v1/accounts
{
  "name": "Cash",
  "currencyCode": "EUR",
  "initialBalance": 50.00,
  "order": 2,
  "icon": "wallet",
  "color": "#22C55E"
}
201 Created
{
  "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"
}
PUT /api/v1/accounts/{id} accounts:write

Reemplaza una cuenta existente. El cuerpo es idéntico al de Crear; deben enviarse todos los campos.

DELETE /api/v1/accounts/{id} accounts:write

Elimina lógicamente una cuenta. Decide qué pasa con sus transacciones mediante el parámetro de consulta action.

Parámetros de consulta
CampoTipoDescripción
actionenumopcionalDetach (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.
moveTargetAccountIdstringopcionalObligatorio 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.

GET /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

GET /api/v1/categories categories:read

Lista todas las categorías definidas por el usuario.

GET /api/v1/categories/{id} categories:read

Obtén una categoría de usuario por su id.

POST /api/v1/categories categories:write

Crea una categoría de usuario.

Cuerpo de la petición
CampoTipoDescripción
namestringobligatorioNombre visible. Máx. 100.
typestringobligatorio"income" o "expense".
orderintegerobligatorioPosición de orden dentro de su grupo.
isPinnedbooleanobligatorioFijar al inicio del selector.
categoryGroupIdstringopcionalId del grupo padre, o null si no tiene grupo.
iconstringopcionalIdentificador de icono.
colorstringopcionalColor hexadecimal.
PUT /api/v1/categories/{id} categories:write

Reemplaza una categoría de usuario. El cuerpo es idéntico al de Crear.

DELETE /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

GET /api/v1/categories/groups categories:read

Lista tus grupos de categorías.

GET /api/v1/categories/groups/{id} categories:read

Obtén un grupo de categorías.

POST /api/v1/categories/groups categories:write

Crea un grupo de categorías.

Cuerpo de la petición
CampoTipoDescripción
namestringobligatorioNombre visible. Máx. 100.
orderintegerobligatorioPosición de orden.
iconstringopcionalIdentificador de icono.
colorstringopcionalColor hexadecimal.
PUT /api/v1/categories/groups/{id} categories:write

Reemplaza un grupo de categorías.

DELETE /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.

GET /api/v1/budgets budgets:read

Lista todos los presupuestos.

GET /api/v1/budgets/{id} budgets:read

Obtén un presupuesto.

POST /api/v1/budgets budgets:write

Crea un presupuesto.

Cuerpo de la petición
CampoTipoDescripción
amountnumberobligatorioLímite por periodo. Debe ser mayor que 0.
currencyCodestringobligatorioISO-4217.
periodintegerobligatorio0 Semanal · 1 Mensual · 2 Trimestral · 3 Anual.
startDatestring (ISO-8601)obligatorioInicio del primer periodo.
isActivebooleanobligatorioSi este presupuesto se aplica actualmente.
endDatestring (ISO-8601)opcionalDeja de dar seguimiento después de esta fecha.
namestringopcionalEtiqueta. Máx. 200.
categoryIdstringopcionalCategoría a la que dar seguimiento. Omítelo para presupuestar todo el gasto.
groupIdstringopcionalGrupo con el que compartir. Omítelo para que sea personal.
PUT /api/v1/budgets/{id} budgets:write

Reemplaza un presupuesto.

DELETE /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í.

GET /api/v1/groups groups:read

Lista los grupos que posees o de los que eres miembro.

GET /api/v1/groups/{id} groups:read

Obtén un grupo.

POST /api/v1/groups groups:write

Crea un grupo. Te conviertes en su propietario; invita a los miembros desde la app de iOS.

Cuerpo de la petición
CampoTipoDescripción
namestringobligatorioNombre visible. Máx. 100.
descriptionstringopcionalTexto libre. Máx. 500.
iconstringopcionalIdentificador de icono.
colorstringopcionalColor hexadecimal.
PUT /api/v1/groups/{id} groups:write

Reemplaza los metadatos de un grupo.

DELETE /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.

GET /api/v1/transactions transactions:read

Lista transacciones con paginación por cursor y filtros.

Parámetros de consulta
CampoTipoDescripción
limitintegeropcionalTamaño de página. 1..200. Por defecto 50.
cursorstringopcionalToken de continuación opaco de la respuesta anterior.
typestringopcional"income", "expense", "transfer" o "adjustment".
dateFromstring (ISO-8601)opcionalLímite inferior inclusivo.
dateTostring (ISO-8601)opcionalLímite superior inclusivo.
categoryIdstringopcionalFiltrar por una categoría.
accountIdstringopcionalFiltrar por una cuenta.
groupIdstringopcionalFiltrar por un grupo compartido.
200 OK
{
  "items": [ /* TransactionDto[] */ ],
  "totalCount": 317,
  "nextCursor": "eyJrIjoiMjAyNi0wNS0xM1QxMDoz…"
}
GET /api/v1/transactions/{id} transactions:read

Obtén una transacción.

POST /api/v1/transactions transactions:write

Crea una sola transacción de ingreso o gasto. Para transferencias, usa /transfer.

Cuerpo de la petición
CampoTipoDescripción
typestringobligatorio"income" o "expense".
amountnumberobligatorioMonto positivo en currencyCode.
currencyCodestringobligatorioISO-4217.
datestring (ISO-8601)obligatorioCuándo ocurrió la transacción (UTC).
accountIdstringopcionalCuenta de origen o de destino.
categoryIdstringopcionalEtiqueta de categoría.
payeestringopcionalComercio o contraparte. Máx. 200.
notestringopcionalNota de texto libre. Máx. 2000.
groupIdstringopcionalCompartir con un grupo.
exchangeRatenumberopcionalTipo de cambio cuando currencyCode ≠ la moneda principal del usuario.
convertedAmountnumberopcionalMonto en la moneda principal del usuario.
PeticiónCafé de €8.50 de ayer en Wise
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"
}
PUT /api/v1/transactions/{id} transactions:write

Reemplaza una transacción. El cuerpo es idéntico al de Crear.

DELETE /api/v1/transactions/{id} transactions:write

Elimina lógicamente una transacción.

POST /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.

Cuerpo de la petición
CampoTipoDescripción
fromAccountIdstringobligatorioCuenta de origen.
toAccountIdstringobligatorioCuenta de destino. Debe ser distinta de la de origen.
amountnumberobligatorioMonto enviado, en currencyCode.
currencyCodestringobligatorioMoneda de origen, ISO-4217.
datestring (ISO-8601)obligatorioFecha de la transferencia.
exchangeRatenumberopcionalObligatorio cuando las monedas de origen y destino difieren.
convertedAmountnumberopcionalMonto acreditado en el destino, en su moneda.
notestringopcionalMá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.

POST /api/v1/transactions/bulk transactions:write

Crea hasta 100 transacciones en una sola llamada.

200 OK
{
  "items": [ /* successful TransactionDto[] */ ],
  "errors": [
    { "index": 3, "error": "Invalid currency code" }
  ]
}
PUT /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.

POST /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.

Cuerpo de la petición
CampoTipoDescripción
idsstring[]obligatorioDe 1 a 100 ids de transacción.
200 OK
{
  "deleted": 97,
  "notFound": [ "tx_old1", "tx_old2", "tx_old3" ]
}

El objeto transacción

CampoTipoDescripción
idstringIdentificador opaco.
typestringincome · expense · transfer · adjustment.
amountnumberEn currencyCode.
currencyCodestringISO-4217.
exchangeRatenumber | nullSe establece en los registros entre monedas distintas.
convertedAmountnumber | nullEl mismo valor en la moneda principal del usuario.
datestringISO-8601 UTC.
accountIdstring | nullEn las transferencias, esta es la cuenta de origen.
toAccountIdstring | nullCuenta de destino, solo en transferencias.
categoryIdstring | nullNull en las transferencias.
groupIdstring | nullSe establece si se comparte con un grupo.
payeestring | nullComercio o contraparte.
notestring | nullTexto libre.
receiptImagePathstring | nullRuta al recibo adjunto. Se obtiene por el endpoint de recibos (independiente de v1).
recurringTransactionIdstring | nullSe establece si esta fila se generó a partir de una regla recurrente.
sourcestringEtiqueta de origen — api, mobile, web, import, etc.
createdAtstringISO-8601 UTC.
updatedAtstringISO-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.

GET /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.

200 OK
{
  "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, source o action como “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.