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ès | Rôle |
|---|---|
GET /.well-known/oauth-authorization-server | Métadonnées RFC 8414 |
POST /oauth/register | Enregistrement dynamique de client (RFC 7591) |
GET /oauth/authorize | Autorisation, PKCE obligatoire |
POST /oauth/token | authorization_code et refresh_token |
POST /oauth/revoke | Ré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
S256est obligatoire.plainest refusé et n’est pas annoncé. - Les URI de redirection sont comparées à l’identique avec celles que
vous avez enregistrées.
httpsn’importe où,httpuniquement 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ée | Autorise |
|---|---|
capture | POST /api/v1/capture |
items:read | GET /api/v1/boxes, GET /api/v1/items |
items:write | POST /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 }
| Champ | Signification | |
|---|---|---|
text | obligatoire | Ce que l’utilisateur a dit, dans ses propres mots. |
parentId | optionnel | Identifiant de boîte où tout classer. Omettez-le pour laisser tidee choisir. |
mode | optionnel | plan (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 :
| Statut | code | Signification |
|---|---|---|
| 400 | error.api.v1.text.required | Entrée manquante ou vide. |
| 401 | error.api.token.invalid | Jeton révoqué, expiré ou incorrect. |
| 402 | error.free.tier.capture.limit | Plafond de la formule gratuite atteint. L’élément n’a PAS été créé ; l’utilisateur doit s’abonner. |
| 403 | error.api.token.scope.missing | Identifiant valide, portée incorrecte. |
| 429 | rate_limited | 60 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.