Référence de l'API

Deux surfaces, avec des promesses différentes.

/api/v1/… est publique. Des tiers conservent ces chemins dans des fichiers de configuration que nous ne pouvons pas modifier : elle est donc versionnée et sa forme ne changera pas sous leurs pieds. Tout ce qui suit documente cette surface.

/api/… est interne. Elle existe pour les clients web et mobile de tidee, change en même temps qu’eux, et n’est pas documentée ici. Les jetons d’accès personnels y sont refusés par conception : ces routes n’exigent aucune portée, donc un jeton créé pour capture seul pourrait sinon atteindre chacune d’elles. /e2e/… sert aux tests et dépend de TIDEE_LIFE_API_AUTH_UUID.

URL de base : https://app.tidee.com


S’authentifier

Deux types d’identifiant atteignent l’API publique, tous deux via Authorization: Bearer ….

Jetons d’accès personnels

Pour vos propres scripts et pour les clients MCP que vous configurez à la main. Créez-en un sur le web, sous Paramètres de l’application → Intégrations. Il est affiché une seule fois et conservé uniquement sous forme de hachage — si vous le perdez, révoquez-le et créez-en un autre.

Authorization: Bearer tdl_…

OAuth 2.1

Pour les applications que d’autres personnes connecteront. L’utilisateur approuve l’application sur un écran de consentement et ne manipule jamais de secret ; il peut la déconnecter à tout moment, ce qui l’arrête immédiatement.

La découverte est automatique : un appel non authentifié à /mcp renvoie un 401 dont l’en-tête WWW-Authenticate pointe vers /.well-known/oauth-protected-resource, qui nomme le serveur d’autorisation. À partir de là :

Point d’accèsRôle
GET /.well-known/oauth-authorization-serverMétadonnées RFC 8414
POST /oauth/registerEnregistrement dynamique de client (RFC 7591)
GET /oauth/authorizeAutorisation, PKCE obligatoire
POST /oauth/tokenauthorization_code et refresh_token
POST /oauth/revokeRévocation RFC 7009

L’enregistrement est ouvert — aucun accord préalable avec nous n’est nécessaire. Ce que cela implique en pratique :

  • PKCE avec S256 est obligatoire. plain est refusé et n’est pas annoncé.
  • Les URI de redirection sont comparées à l’identique avec celles que vous avez enregistrées. https n’importe où, http uniquement vers la boucle locale, ou un schéma privé contenant un point.
  • Les codes d’autorisation sont à usage unique et de courte durée. En présenter un deux fois révoque l’octroi, au motif que deux parties le détiennent.
  • Les jetons de rafraîchissement tournent, et présenter un jeton déjà remplacé révoque l’octroi, pour la même raison.
  • Les jetons d’accès durent une heure ; les jetons de rafraîchissement 60 jours à compter de la dernière utilisation.
  • Les clients sont publics — il n’y a pas de secret client, parce qu’une application de bureau ou en ligne de commande ne peut pas en garder un.

Portées

PortéeAutorise
capturePOST /api/v1/capture
items:readGET /api/v1/boxes, GET /api/v1/items
items:writePOST /api/v1/items, PATCH /api/v1/items/:id

Chaque point d’accès exige la portée dont il a besoin. Demandez le minimum qui vous suffit : une personne qui lit un écran de consentement approuve plus volontiers une liste courte, et un identifiant divulgué fait moins de dégâts.


Points d’accès

POST /api/v1/capture

Du texte libre en entrée, des éléments en sortie. Celui vers lequel se tourner quand vous avez une phrase plutôt qu’un titre décidé : tidee détermine les éléments distincts, la boîte de chacun, et les dates éventuelles.

{ "text": "book the MOT and call Sam about Friday", "parentId": null }
ChampSignification
textobligatoireCe que l’utilisateur a dit, dans ses propres mots.
parentIdoptionnelIdentifiant de boîte où tout classer. Omettez-le pour laisser tidee choisir.
modeoptionnelplan (par défaut) ou literal.
{
  "created": [{ "itemShareId": "…", "title": "book the MOT" }],
  "createdCount": 2,
  "usedFallback": false
}

usedFallback: true signifie que le texte n’a pas pu être interprété et a été conservé tel quel comme un seul élément. Rien n’est jamais perdu — cela vaut la peine d’être relayé, car le résultat ne ressemblera pas à ce qui a été demandé.

Il reformule en classant. « get some milk tomorrow » devient un élément nommé « milk » avec une date. C’est ce que l’on veut quand quelqu’un réfléchit à voix haute, et c’est faux quand la personne a choisi ses mots — mettez le titre entre guillemets, ou utilisez POST /api/v1/items, qui le conserve exactement.

POST /api/v1/items

Un élément, titre utilisé exactement tel qu’il est fourni.

{
  "title": "Get some milk",
  "parentId": null,
  "targetDate": "2026-09-10T09:00:00"
}

targetDate est le moment où l’utilisateur doit AGIR, en heure locale sans fuseau ; le serveur la résout d’après le fuseau de son profil. Ce n’est pas la date d’un événement dont la tâche parle simplement — « réserver une table pour le 14 » doit être fait avant le 14, donc omettez-la. Par défaut 09:00 quand aucune heure n’est donnée.

GET /api/v1/boxes

{ "boxes": [{ "id": "…", "name": "Work" }], "workspaceRootId": "…" }

GET /api/v1/items

Paramètres : q (recherche dans les titres), parentId (une boîte), limit (50 par défaut, plafonné à 200).

{
  "items": [
    {
      "id": "…",
      "title": "book the MOT",
      "itemType": "todoText",
      "completed": false,
      "parentId": "…",
      "targetDate": null,
      "createdDate": "2026-09-01T09:00:00.000Z"
    }
  ]
}

PATCH /api/v1/items/:itemId

{ "completed": true }

Accepte completed, title, ou les deux. Un identifiant appartenant à quelqu’un d’autre ne se résout tout simplement pas.


Erreurs

Une seule forme, parce que l’appelant est un programme :

{ "error": { "code": "error.free.tier.capture.limit", "message": "…" } }

Branchez sur code, pas sur le texte. Le code ne change jamais de langue : c’est un identifiant, et le traduire casserait toute intégration qui l’utilise. Le message est destiné à une personne et arrive dans la langue du compte.

Ceux qui valent la peine d’être traités :

StatutcodeSignification
400error.api.v1.text.requiredEntrée manquante ou vide.
401error.api.token.invalidJeton révoqué, expiré ou incorrect.
402error.free.tier.capture.limitPlafond de la formule gratuite atteint. L’élément n’a PAS été créé ; l’utilisateur doit s’abonner.
403error.api.token.scope.missingIdentifiant valide, portée incorrecte.
429rate_limited60 requêtes par minute, par jeton.

Limites

  • 60 requêtes par minute et par jeton sur l’API publique, 120 sur /mcp. Comptées par jeton plutôt que par compte, pour qu’une intégration ne puisse pas épuiser le budget d’une autre.
  • Les comptes gratuits ont un plafond à vie de 50 éléments. Les captures faites via cette API y comptent exactement comme celles faites dans l’application — il n’y a pas de quota distinct, et un 402 signifie que l’écriture n’a pas eu lieu.

MCP

https://app.tidee.com/mcp parle MCP sur HTTP en flux continu, avec les mêmes cinq opérations exposées comme outils. Le service est sans état : pas d’identifiant de session, une requête une réponse. Authentifiez-vous avec l’un ou l’autre des identifiants ci-dessus.

Il existe aussi un serveur local pour les clients qui préfèrent stdio — voir packages/tidee-life-mcp/README.md.