Développeurs

Référence de l’API v1

REST · JSON · OAuth 2.0 Bearer https://api.manilo.app Stable — aucun changement incompatible au sein de v1

Vue d’ensemble

L’API Manilo est une API REST JSON qui permet de gérer par programme le registre que vous utilisez dans l’application iOS — comptes, catégories, budgets, groupes partagés et transactions. Servez-vous-en pour construire des imports, des exports, des passerelles de synchronisation, des tableaux de bord, ou pour alimenter vos propres automatisations.

  • URL de base : https://api.manilo.app — l’ancienne URL de base api.ledgy.app reste fonctionnelle.
  • Préfixe de version : tous les endpoints de ce document sont exposés sous /api/v1/.
  • Transport : HTTPS uniquement. Les requêtes HTTP ne sont pas acceptées.
  • Encodage : Corps de requête et de réponse en JSON. UTF-8. Noms de propriétés en camelCase.
  • Authentification : Authorization: Bearer … sur chaque requête.
  • Abonnement Cloud : requis sur chaque endpoint v1. Voir Contrôle d’abonnement.
Vous cherchez plutôt un assistant IA ? Si vous voulez que Claude, ChatGPT ou Cursor dialoguent avec Manilo en votre nom, utilisez l’endpoint Model Context Protocol à l’adresse https://api.manilo.app/mcp — voir Intégrations. L’API REST documentée ici s’adresse au code que vous écrivez vous-même.

Démarrage rapide

Trois étapes jusqu’à votre première requête authentifiée.

1. Générer un Personal Access Token

  1. Connectez-vous à votre tableau de bord Manilo et ouvrez Réglages → Accès API.
  2. Cliquez sur + Nouveau jeton.
  3. Donnez au jeton un nom explicite (par ex. « Zapier — export hebdomadaire »), choisissez les scopes dont vous avez besoin (voir Scopes) et, si vous le souhaitez, définissez une date d’expiration.
  4. Copiez le jeton. Il n’est affiché qu’une seule fois. Les jetons commencent par le préfixe lgpat_ suivi de 64 caractères hexadécimaux.

La même page liste vos jetons actifs avec leur date de dernière utilisation et leur nombre de permissions, affiche un avertissement « Expire dans 30 jours » et vous permet de révoquer un jeton instantanément via l’icône corbeille. Chaque compte peut détenir jusqu’à 25 jetons actifs simultanément.

2. Envoyer une requête

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

3. Examiner la réponse

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
}
Traitez votre jeton comme un mot de passe. Quiconque le détient peut lire ou modifier votre registre dans la limite des scopes que vous avez accordés. Révoquez immédiatement tout jeton compromis depuis Réglages → Accès API.

Authentification

Chaque requête vers /api/v1/ doit porter un en-tête Authorization. Deux types de jetons sont acceptés :

  • Personal Access Token (PAT) — jeton bearer de longue durée que vous créez depuis la page Réglages → Accès API du tableau de bord. Format : lgpat_ + 64 caractères hexadécimaux. Limité par scopes, révocable, avec expiration facultative. Recommandé pour toutes les intégrations tierces.
  • JWT de session — jeton de courte durée délivré aux applications propriétaires (iOS, tableau de bord). Il n’a aucune restriction de scope. Vous pouvez l’utiliser pour un test ponctuel si vous parvenez à en extraire un depuis une session connectée, mais les PAT restent la voie prise en charge.

Format de l’en-tête

HTTP
Authorization: Bearer lgpat_a1b2c3d4e5f6…

Limites des jetons

  • Jusqu’à 25 PAT actifs par compte Manilo.
  • Une date d’expiration facultative peut être définie à la création. Les jetons expirés renvoient 401 Unauthorized.
  • Les jetons révoqués cessent de fonctionner immédiatement — Manilo ne conserve qu’un hachage SHA-256 du jeton, jamais sa valeur : un jeton divulgué ne peut donc pas être récupéré, seulement révoqué et remplacé.

Échecs d’authentification courants

401
Jeton absent, mal formé, expiré ou révoqué.
403
Le jeton est valide, mais l’endpoint demandé exige un scope que votre PAT ne possède pas, ou votre abonnement Cloud n’est pas actif.

Scopes

Les PAT suivent un modèle de refus par défaut. Un jeton ne peut appeler que les endpoints dont il détient le scope requis ; tout le reste renvoie 403 Forbidden. N’accordez que le jeu de scopes le plus restreint dont votre intégration a réellement besoin.

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

Chaque endpoint listé ci-dessous affiche le scope qu’il exige sous forme de petite pastille violette. Les scopes :write n’impliquent pas :read — demandez les deux si vous avez besoin des deux.

Contrôle d’abonnement

Tous les endpoints v1 — y compris ceux en lecture seule — exigent que l’utilisateur appelant dispose d’un abonnement Manilo Cloud actif. Si l’abonnement a été résilié, a expiré ou n’a jamais été souscrit, l’API répond :

403 Forbidden
HTTP/1.1 403 Forbidden
X-Subscription-Required: true
Content-Type: application/json

{ "error": "Active cloud subscription required" }

L’en-tête X-Subscription-Required permet aux clients de distinguer un blocage lié à l’abonnement d’un refus de permission générique. Rétablissez l’accès en réactivant Cloud dans l’application iOS ou sur dashboard.manilo.app/upgrade.

Erreurs

Les erreurs utilisent les codes de statut HTTP standard. Le corps de la réponse est un objet JSON à un seul champ :

JSON
{ "error": "Human-readable message" }

Codes de statut que vous devez gérer :

200
OK — ressource renvoyée, ou liste renvoyée.
201
Created — nouvelle ressource créée. L’en-tête Location pointe vers l’URL canonique.
204
No Content — suppression réussie ; aucun corps de réponse.
400
Échec de la validation — champ requis manquant, valeur hors limites, JSON mal formé.
401
Échec de l’authentification — voir Authentification.
403
Permission refusée — scope insuffisant ou abonnement inactif.
404
Ressource introuvable, ou masquée par les règles d’autorisation.
409
Conflit — par ex. violation d’une contrainte d’unicité.
5xx
Défaillance côté serveur. Vous pouvez réessayer sans risque les lectures idempotentes avec un backoff exponentiel.

Pagination et filtres

Par défaut, les endpoints de liste renvoient tous les éléments correspondants. Les transactions, seule ressource susceptible de devenir volumineuse, prennent en charge une pagination par curseur.

Curseur des transactions

cURLListe paginée
curl "https://api.manilo.app/api/v1/transactions?limit=50&type=expense&dateFrom=2026-01-01" \
  -H "Authorization: Bearer lgpat_…"

La réponse contient un nextCursor. Renvoyez-le dans le paramètre de requête cursor pour obtenir la page suivante ; lorsque nextCursor vaut null, vous avez atteint la fin.

  • limit — taille de page, bornée à 1..200. Par défaut 50.
  • cursor — jeton opaque. Traitez-le comme une boîte noire.
  • type, dateFrom, dateTo, categoryId, accountId, groupId — filtres facultatifs ; voir l’endpoint Lister les transactions.

Types et formats

  • ID — chaînes opaques. Ne les analysez pas ; traitez-les comme des identifiants UTF-8 sensibles à la casse.
  • Horodatages — ISO-8601 en UTC, avec un Z final, par ex. "2026-05-13T10:30:00Z".
  • Dates (par ex. le champ date d’une transaction) — même forme ISO-8601, mais seule la partie date est significative.
  • Montants — nombres JSON exprimés en unités principales, avec jusqu’à 4 décimales (par ex. 12.50). Jamais en sous-unités. Toujours accompagnés de currencyCode.
  • Codes devise — ISO-4217, exactement trois lettres majuscules (par ex. "EUR", "USD", "GBP").
  • Suppressions — toutes les opérations de suppression sont des suppressions logiques. Les éléments supprimés cessent d’apparaître dans les réponses de liste et de lecture ; les partenaires de partage et les reçus historiques sont conservés.
  • Effets de bord — supprimer un compte, une catégorie ou un groupe laisse intactes les transactions qui en dépendent ; leurs références sont simplement détachées. L’endpoint Supprimer un compte accepte une stratégie explicite.

Comptes

Les comptes sont les enveloppes qui portent les soldes — un compte bancaire, une carte de crédit, un portefeuille d’espèces, un compte-titres. Chaque transaction est rattachée à l’un d’eux (ou à deux, dans le cas des virements).

GET /api/v1/accounts accounts:read

Renvoie tous les comptes que l’utilisateur authentifié possède ou auxquels il a accès.

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

Récupère un compte par son id. Renvoie 404 s’il est introuvable.

POST /api/v1/accounts accounts:write

Crée un compte. Renvoie 201 avec l’objet créé ainsi qu’un en-tête Location.

Corps de la requête
ChampTypeDescription
namestringrequisNom affiché. 100 caractères max.
currencyCodestringrequisISO-4217. Exactement 3 lettres.
initialBalancenumberrequisSolde d’ouverture, exprimé en currencyCode.
orderintegerrequisPosition de tri. Les valeurs les plus basses viennent en premier.
iconstringfacultatifIdentifiant d’icône issu de /api/v1/icons. 50 max.
colorstringfacultatifCouleur hexadécimale, par ex. "#4A90E2". 20 max.
iconColorstringfacultatifRemplace la teinte de l’icône.
Requête
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

Remplace un compte existant. Le corps est identique à Créer ; tous les champs doivent être fournis.

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

Supprime logiquement un compte. Décidez du sort de ses transactions via le paramètre de requête action.

Paramètres de requête
ChampTypeDescription
actionenumfacultatifDetach (par défaut) : efface la référence au compte sur chaque transaction. Move : réaffecte les transactions à moveTargetAccountId. DeleteAll : supprime logiquement toutes les transactions liées dont vous êtes propriétaire.
moveTargetAccountIdstringfacultatifObligatoire lorsque action=Move. Id du compte de destination.

Catégories

Les catégories indiquent à quoi correspond une transaction (courses, loyer, revenus en freelance). Les catégories système sont en lecture seule et communes à tous les utilisateurs ; les catégories utilisateur, elles, vous appartiennent. Les groupes de catégories rassemblent les catégories apparentées.

GET /api/v1/categories/system-categories categories:read

Renvoie le jeu de catégories « bien connues » sélectionné par Manilo — l’ensemble de départ livré avec l’application iOS. Il est versionné globalement et peut être mis en cache sans risque en s’appuyant sur version.

Catégories utilisateur

GET /api/v1/categories categories:read

Liste toutes les catégories définies par l’utilisateur.

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

Récupère une catégorie utilisateur par son id.

POST /api/v1/categories categories:write

Crée une catégorie utilisateur.

Corps de la requête
ChampTypeDescription
namestringrequisNom affiché. 100 max.
typestringrequis"income" ou "expense".
orderintegerrequisPosition de tri au sein de son groupe.
isPinnedbooleanrequisÉpingle la catégorie en haut du sélecteur.
categoryGroupIdstringfacultatifId du groupe parent, ou null si la catégorie n’appartient à aucun groupe.
iconstringfacultatifIdentifiant d’icône.
colorstringfacultatifCouleur hexadécimale.
PUT /api/v1/categories/{id} categories:write

Remplace une catégorie utilisateur. Le corps est identique à Créer.

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

Supprime logiquement une catégorie utilisateur. Les transactions ne sont pas supprimées ; leur champ categoryId est simplement vidé.

Groupes de catégories

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

Liste vos groupes de catégories.

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

Récupère un groupe de catégories.

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

Crée un groupe de catégories.

Corps de la requête
ChampTypeDescription
namestringrequisNom affiché. 100 max.
orderintegerrequisPosition de tri.
iconstringfacultatifIdentifiant d’icône.
colorstringfacultatifCouleur hexadécimale.
PUT /api/v1/categories/groups/{id} categories:write

Remplace un groupe de catégories.

DELETE /api/v1/categories/groups/{id} categories:write

Supprime logiquement un groupe. Les catégories enfants subsistent — leur champ categoryGroupId est vidé.

Budgets

Un budget plafonne les dépenses d’une catégorie (ou, lorsque categoryId vaut null, de l’ensemble du registre) sur une période récurrente. Partagez un budget avec un groupe en renseignant groupId.

GET /api/v1/budgets budgets:read

Liste tous les budgets.

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

Récupère un budget.

POST /api/v1/budgets budgets:write

Crée un budget.

Corps de la requête
ChampTypeDescription
amountnumberrequisPlafond par période. Doit être supérieur à 0.
currencyCodestringrequisISO-4217.
periodintegerrequis0 hebdomadaire · 1 mensuel · 2 trimestriel · 3 annuel.
startDatestring (ISO-8601)requisDébut de la première période.
isActivebooleanrequisIndique si ce budget est actuellement appliqué.
endDatestring (ISO-8601)facultatifArrêter le suivi après cette date.
namestringfacultatifLibellé. 200 max.
categoryIdstringfacultatifCatégorie à suivre. Omettre pour budgéter toutes les dépenses.
groupIdstringfacultatifGroupe avec lequel partager. Omettre pour un budget personnel.
PUT /api/v1/budgets/{id} budgets:write

Remplace un budget.

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

Supprime logiquement un budget.

Groupes

Les groupes sont des registres partagés — un foyer, un voyage, une colocation. Chaque membre voit les mêmes transactions ; la propriété, elle, reste personnelle. Les adhésions et les invitations se gèrent dans l’application iOS ; l’API ne couvre ici que la ressource elle-même.

GET /api/v1/groups groups:read

Liste les groupes dont vous êtes propriétaire ou membre.

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

Récupère un groupe.

POST /api/v1/groups groups:write

Crée un groupe. Vous en devenez le propriétaire ; invitez les membres depuis l’application iOS.

Corps de la requête
ChampTypeDescription
namestringrequisNom affiché. 100 max.
descriptionstringfacultatifTexte libre. 500 max.
iconstringfacultatifIdentifiant d’icône.
colorstringfacultatifCouleur hexadécimale.
PUT /api/v1/groups/{id} groups:write

Remplace les métadonnées d’un groupe.

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

Supprime logiquement un groupe. Les membres perdent la visibilité ; les transactions sous-jacentes redeviennent personnelles.

Transactions

Les transactions sont les verbes du registre. Elles se déclinent en quatre formes : income, expense, transfer (de compte à compte) et adjustment (rééquilibrage ponctuel). L’endpoint de base crée les revenus et les dépenses ; les virements ont leur propre endpoint ; des variantes en lot existent pour les imports à fort volume.

GET /api/v1/transactions transactions:read

Liste les transactions avec pagination par curseur et filtres.

Paramètres de requête
ChampTypeDescription
limitintegerfacultatifTaille de page. 1..200. Par défaut 50.
cursorstringfacultatifJeton de continuation opaque issu de la réponse précédente.
typestringfacultatif"income", "expense", "transfer" ou "adjustment".
dateFromstring (ISO-8601)facultatifBorne inférieure incluse.
dateTostring (ISO-8601)facultatifBorne supérieure incluse.
categoryIdstringfacultatifFiltrer sur une seule catégorie.
accountIdstringfacultatifFiltrer sur un seul compte.
groupIdstringfacultatifFiltrer sur un groupe partagé.
200 OK
{
  "items": [ /* TransactionDto[] */ ],
  "totalCount": 317,
  "nextCursor": "eyJrIjoiMjAyNi0wNS0xM1QxMDoz…"
}
GET /api/v1/transactions/{id} transactions:read

Récupère une transaction.

POST /api/v1/transactions transactions:write

Crée une seule transaction de revenu ou de dépense. Pour les virements, utilisez /transfer.

Corps de la requête
ChampTypeDescription
typestringrequis"income" ou "expense".
amountnumberrequisMontant positif exprimé en currencyCode.
currencyCodestringrequisISO-4217.
datestring (ISO-8601)requisDate à laquelle la transaction a eu lieu (UTC).
accountIdstringfacultatifCompte source ou de destination.
categoryIdstringfacultatifÉtiquette de catégorie.
payeestringfacultatifCommerçant ou contrepartie. 200 max.
notestringfacultatifNote libre. 2000 max.
groupIdstringfacultatifPartager avec un groupe.
exchangeRatenumberfacultatifTaux de change lorsque currencyCode ≠ devise principale de l’utilisateur.
convertedAmountnumberfacultatifMontant dans la devise principale de l’utilisateur.
RequêteCafé à €8.50 hier sur 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

Remplace une transaction. Le corps est identique à Créer.

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

Supprime logiquement une transaction.

POST /api/v1/transactions/transfer transactions:write

Crée un virement entre deux comptes. Sans catégorie. Pour les virements multidevises, fournissez exchangeRate et convertedAmount dans la devise de destination.

Corps de la requête
ChampTypeDescription
fromAccountIdstringrequisCompte source.
toAccountIdstringrequisCompte de destination. Doit différer du compte source.
amountnumberrequisMontant envoyé, exprimé en currencyCode.
currencyCodestringrequisDevise source, ISO-4217.
datestring (ISO-8601)requisDate du virement.
exchangeRatenumberfacultatifObligatoire lorsque les devises source et de destination diffèrent.
convertedAmountnumberfacultatifMontant crédité sur le compte de destination, dans sa devise.
notestringfacultatif2000 max.

Opérations en lot

Conçues pour les imports. Chaque lot est plafonné à 100 éléments et s’exécute en mode best-effort : une seule ligne invalide n’annule pas les autres. Les éléments traités et les erreurs ligne par ligne sont rapportés séparément, ce qui vous permet de rejouer uniquement les échecs.

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

Crée jusqu’à 100 transactions en un seul appel.

200 OK
{
  "items": [ /* successful TransactionDto[] */ ],
  "errors": [
    { "index": 3, "error": "Invalid currency code" }
  ]
}
PUT /api/v1/transactions/bulk transactions:write

Met à jour jusqu’à 100 transactions en un seul appel. Chaque élément doit inclure son id en plus du corps complet de la transaction.

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

Supprime logiquement jusqu’à 100 transactions en un seul appel. Utilise POST plutôt que DELETE afin que le corps de requête soit accepté par tous les clients HTTP.

Corps de la requête
ChampTypeDescription
idsstring[]requisDe 1 à 100 identifiants de transaction.
200 OK
{
  "deleted": 97,
  "notFound": [ "tx_old1", "tx_old2", "tx_old3" ]
}

L’objet transaction

ChampTypeDescription
idstringIdentifiant opaque.
typestringincome · expense · transfer · adjustment.
amountnumberExprimé en currencyCode.
currencyCodestringISO-4217.
exchangeRatenumber | nullRenseigné sur les écritures multidevises.
convertedAmountnumber | nullLa même valeur dans la devise principale de l’utilisateur.
datestringISO-8601 UTC.
accountIdstring | nullPour les virements, il s’agit du compte source.
toAccountIdstring | nullCompte de destination, sur les virements uniquement.
categoryIdstring | nullNull sur les virements.
groupIdstring | nullRenseigné si la transaction est partagée avec un groupe.
payeestring | nullCommerçant ou contrepartie.
notestring | nullTexte libre.
receiptImagePathstring | nullChemin du reçu joint. À récupérer via l’endpoint des reçus (distinct de v1).
recurringTransactionIdstring | nullRenseigné si cette ligne a été générée par une règle de récurrence.
sourcestringMarqueur d’origine — api, mobile, web, import, etc.
createdAtstringISO-8601 UTC.
updatedAtstringISO-8601 UTC.

Icônes

Manilo est livré avec un jeu d’icônes et une palette de couleurs sélectionnés, utilisés partout — comptes, catégories, groupes. Récupérez le catalogue une fois, mettez-le en cache, puis réutilisez les identifiants lors de la création de ressources.

GET /api/v1/icons

Renvoie la bibliothèque d’icônes complète, regroupée par catégories, ainsi que la palette de couleurs prise en charge. Versionnée — peut être mise en cache sans risque en s’appuyant sur le champ 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" }
  ]
}

Versionnage

  • Aucun changement incompatible au sein de v1. Nous n’ajouterons que de nouveaux endpoints, de nouveaux champs facultatifs et de nouvelles valeurs d’énumération. Le type d’un champ, sa nullabilité ou son caractère obligatoire ne changeront pas.
  • Les nouvelles valeurs d’énumération ne sont pas incompatibles. Traitez les valeurs inconnues de type, source ou action comme « à ne pas afficher » plutôt que de faire planter votre client — nous en ajouterons à mesure que le produit s’étoffe.
  • Les changements incompatibles datés (si jamais ils s’avéraient nécessaires) seront livrés sous /api/v2/, avec au moins 6 mois de disponibilité en parallèle et un en-tête de dépréciation sur les réponses v1.

Assistance

Vous avez trouvé un bug, vous souhaitez un nouvel endpoint ou vous êtes tombé sur quelque chose de non documenté ? Ouvrez le centre d’aide ou écrivez à support@manilo.app — merci d’indiquer l’identifiant de requête (renvoyé dans l’en-tête de réponse X-Request-Id) lorsque vous signalez un problème.

Pour les divulgations sensibles en matière de sécurité (fuite de jeton, contournement de permission, lecture non autorisée), écrivez à security@manilo.app.