Motion API (Client complet)

Client complet pour l'API REST de Motion (usemotion.com) — tous les 27 endpoints documentés pour les tâches, projets, tâches récurrentes, commentaires, champs personnalisés, utilisateurs, espaces de travail, statuts et plannings, plus une 'escape hatch' brute pour tout ce qui n'est pas encore documenté. Gère l'authentification par clé API, la pagination par curseur, le backoff en cas de limite de débit, et protège chaque suppression par un indicateur explicite de confirmation.

Qu'est-ce que Motion API (Client complet) ?

Ce skill fournit un client complet pour l'API REST de Motion (usemotion.com), couvrant l'ensemble des 27 endpoints documentés. Il permet de gérer les tâches, projets, tâches récurrentes, commentaires, champs personnalisés, utilisateurs, espaces de travail, statuts et plannings directement depuis votre agent IA. Le client gère automatiquement l'authentification via une clé API (X-API-Key), la pagination par curseur (y compris le mode '--all' pour récupérer toutes les pages), le backoff en cas de limite de débit (respectant l'en-tête Retry-After), et protège toute opération de suppression par un indicateur explicite '--yes' pour éviter les suppressions accidentelles. Il inclut également une commande 'raw' qui permet d'envoyer des requêtes arbitraires (GET, POST, PATCH, DELETE) vers n'importe quel endpoint, y compris ceux qui ne sont pas encore documentés. Le skill prend en charge les champs personnalisés (y compris les types text, url, date, person, multiPerson, phone, select, multiSelect, number, email, checkbox, relatedTo) via les endpoints /beta/, ainsi que des fonctionnalités avancées comme l'auto-planification des tâches (tri-state : défaut, activé, désactivé), les filtres multi-critères, et la gestion des fréquences de tâches récurrentes (quotidien, hebdomadaire, bi-hebdomadaire, mensuel, trimestriel) au format DSL de Motion. Les erreurs sont gérées de manière robuste : les codes 4xx (hors 429) sont remontés immédiatement, tandis que les 429 sont retentés automatiquement avec backoff. Le client fonctionne sur toutes les plateformes prenant en charge l'exécution de scripts Python (comme Claude Code, Cursor, Codex, etc.) via les credentials injectés comme variables d'environnement.

Compatible avec~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/bluesmarties876/skills/tree/HEAD/api/motion-api

Installed? Explore more Productivité et collaboration skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

API Motion

Client complet pour l'API REST de Motion (usemotion.com) — tous les 27 endpoints documentés pour les tâches, projets, tâches récurrentes, commentaires, champs personnalisés, utilisateurs, espaces de travail, statuts et plannings, plus une escape hatch raw pour tout ce qui n'est pas encore documenté.

Quand utiliser ce skill

Utilisez-le chaque fois que le travail implique Motion : lire ou créer des tâches, lister des projets, publier des commentaires sur des tâches, gérer des tâches récurrentes, ou lire les statuts, utilisateurs et plannings d'un espace de travail.

Identifiants

Un identifiant, MOTION_API_KEY. La plateforme l'injecte comme variable d'environnement lorsque un script s'exécute via RunWithCredentials — ne transmettez jamais la clé en ligne de commande, et ne l'imprimez ou ne l'affichez jamais.

Obtenez une clé dans Motion sous Paramètres → API. Motion ne l'affiche qu'une seule fois, copiez-la donc immédiatement. Si les appels commencent à retourner 401, la clé a été révoquée ou régénérée et doit être remplacée.

Remarque : avec la remise d'identifiant en mode proxy, la variable d'environnement contient un espace réservé non secret et la valeur réelle est injectée dans les requêtes à api.usemotion.com. Un identifiant nouvellement enregistré peut prendre quelques minutes à se propager ; un 401 immédiat qui réussit ensuite est une propagation, pas une clé invalide.

Conventions suivies par l'API

Voici les faits qui font la différence entre un appel qui fonctionne et un qui ne fonctionne pas.

ÉlémentValeur
URL de basehttps://api.usemotion.com
En-tête d'authentificationX-API-Key: <clé>
Vérification de santéGET /v1/users/me — retourne le propriétaire de la clé
Préfixes de chemin/v1/ pour tout sauf les champs personnalisés, qui sont en /beta/
Limites de débit12 requêtes/min (individuel), jusqu'à 120 (équipes), supérieur pour entreprise
DescriptionsMarkdown GitHub Flavored
Valeurs de prioritéASAP, HIGH, MEDIUM, LOW (tâches récurrentes : HIGH/MEDIUM uniquement)
Types d'échéanceHARD, SOFT, NONE (tâches récurrentes : HARD/SOFT uniquement)

Pagination. Les endpoints de liste prennent un cursor et retournent une enveloppe :

{ "meta": { "nextCursor": "…", "pageSize": 25 }, "tasks": [ … ] }

La clé de l'enveloppe est meta, et le champ de la page suivante est nextCursor. Passez --all sur n'importe quelle commande de liste et le script suit le curseur pour vous, retournant un tableau plat.

--all s'arrête à 100 pages puis échoue, donc une requête incontrôlée ne peut pas bloquer une exécution — réduisez la requête si vous voyez cela. Et si une page n'est ni un objet JSON ni un tableau (tronquée, ou une page d'erreur de passerelle), la navigation refuse plutôt que de retourner ce qu'elle a : une liste partielle qui semble complète est pire qu'une erreur.

Deux endpoints de liste retournent un tableau nu sans pagination — GET /v1/statuses et GET /beta/workspaces/{id}/custom-fields. Tout le reste est envelopé.

Erreurs. Les réponses non 2xx portent {"message", "error", "statusCode"}. Les scripts impriment cela en JSON sur stderr et sortent avec un code non nul. 429 est retenté avec backoff en respectant Retry-After ; 400, 401, 403 et 404 sont remontés immédiatement et jamais retentés. Les échecs de validation portent un tableau errors[] par champ ; le client ajoute ces messages pour que la raison réelle soit visible.

Un User-Agent est obligatoire. Le proxy de Motion bloque la signature par défaut Python-urllib/x.y avec un Cloudflare 403 (browser_signature_banned) avant que la requête n'atteigne l'API. Le client envoie toujours motion-api-skill/1.0 ; si vous écrivez votre propre requête vers cette API, définissez un User-Agent explicitement ou vous obtiendrez un 403 déroutant au lieu d'une réponse réelle.

Les paramètres de requête booléens doivent être en minuscules. Motion analyse les chaînes de requête comme du texte, donc un filtre booléen doit être le littéral true, pas le True de Python. Se tromper n'est pas une opération silencieuse : l'API répond avec un 400 confus qui parle de la valeur opposée (voir la section des différences en direct ci-dessous). Le client envoie toujours des littéraux en minuscules.

Référence des commandes

Chaque commande imprime du JSON sur stdout. python3 motion.py <groupe> --help liste les indicateurs.

Tâches

CommandeEndpoint
tasks listGET /v1/tasks
tasks get <task_id>GET /v1/tasks/{id}
tasks createPOST /v1/tasks
tasks update <task_id>PATCH /v1/tasks/{id}
tasks move <task_id> --workspace-id …PATCH /v1/tasks/{id}/move
tasks delete <task_id> --yesDELETE /v1/tasks/{id}
tasks unassign <task_id> --yesDELETE /v1/tasks/{id}/assignee

Filtres de tasks list : --assignee-id, --project-id, --workspace-id, --name (sous-chaîne insensible à la casse), --label (singulier, un nom d'étiquette), --status (répétable), --include-all-statuses. --status et --include-all-statuses s'excluent mutuellement — les envoyer tous deux est rejeté par l'API, et le CLI refuse tôt avec ce message plutôt que de dépenser une requête pour un 400 garanti.

--include-all-statuses est le moyen de voir une tâche qui a dépassé le statut par défaut de son espace de travail (par exemple, tout ce qui se trouve dans Backlog ou In Review) ; sans cela, GET /v1/tasks ne retourne que le statut par défaut et la liste semble vide. Le combiner avec --all retourne chaque tâche assignée à quelqu'un, dans tous les statuts.

tasks create / tasks update prennent les mêmes champs : --name, --workspace-id, --due-date (ISO 8601 ; requis pour les tâches planifiées), --duration, --status, --project-id, --description, --priority, --label (répétable), --assignee-id.

--duration accepte un nombre entier de minutes, ou les chaînes NONE / REMINDER (insensible à la casse en entrée, normalisé en majuscules).

L'auto-planification est un état ternaire, et la distinction est importante :

  • omettre les deux indicateurs → le champ est laissé de côté de la requête entièrement (inchangé)
  • --auto-scheduled '{"deadlineType":"HARD"}' → l'active ; les champs imbriqués sont startDate, deadlineType (HARD/SOFT/NONE) et schedule (par défaut Work Hours)
  • --auto-scheduled-off → envoie un null explicite, la désactivant

Le statut de la tâche doit avoir l'auto-planification activée, sinon l'API refuse.

Une réponse de tâche ressemble à ceci — réduite aux champs dont le workflow de calendrier ci-dessous dépend :

{
  "id": "tsk_123",
  "name": "Rédiger le plan T4",
  "description": "Esquisser les trois scénarios.",
  "status": { "name": "En cours", "isResolvedStatus": false },
  "priority": "HIGH",
  "labels": ["t4"],
  "assignees": [{ "id": "usr_1", "name": "Sam Patel" }],
  "project": { "id": "prj_1", "name": "Planification" },
  "dueDate": "2026-11-01T17:00:00.000Z",
  "duration": 45,
  "scheduledStart": "2026-10-28T09:00:00.000Z",
  "startOn": "2026-10-28",
  "completedTime": null
}

Interprétez-le comme : status est un objet, pas une chaîne — le filtre que vous voulez est status.isResolvedStatus, qui devient true une fois qu'une tâche est terminée ou annulée. assignees et labels sont des tableaux ; project est un objet ou null. scheduledStart et dueDate sont en UTC ; startOn est une date nue.

Il n'y a pas d'endpoint de calendrier. La position d'une tâche sur le calendrier est son scheduledStart (UTC ISO 8601), avec duration en minutes et startOn comme date simple. Pour répondre à "quelle est la prochaine", récupérez les tâches et triez par scheduledStart — ne cherchez pas un endpoint de planning qui retournerait des événements, car schedules list ne retourne que les fenêtres d'heures de travail (voir ci-dessous), pas les éléments réservés. Filtrez les tâches status.isResolvedStatus pour exclure tout ce qui est déjà terminé ou annulé.

Projets

CommandeEndpoint
projects list [--workspace-id …]GET /v1/projects
projects get <project_id>GET /v1/projects/{id}
projects create --name … --workspace-id …POST /v1/projects

projects create prend également --due-date, --description (HTML accepté), --label (répétable), --priority (par défaut MEDIUM côté serveur), et la paire de modèles --project-definition-id + --stages. Les étapes doivent correspondre exactement à l'ordre et au nombre de la définition, sinon l'API retourne 400. Pour les longs tableaux d'étapes, utilisez --stages @stages.json. Le CLI n'applique pas la paire ; l'API rejette une liste d'étapes sans un identifiant de définition.

Tâches récurrentes

CommandeEndpoint
recurring-tasks list --workspace-id …GET /v1/recurring-tasks
recurring-tasks createPOST /v1/recurring-tasks
recurring-tasks delete <id> --yesDELETE /v1/recurring-tasks/{id}

recurring-tasks create nécessite --frequency, --name, --workspace-id et --assignee-id. Optionnel : --deadline-type (HARD/SOFT uniquement — NONE est rejeté ici), --duration (minutes en entier, ou REMINDER ; NONE n'est pas accepté sur cet endpoint), --starting-on, --ideal-time (HH:mm), --schedule (par défaut Work Hours), --description, --priority (HIGH/MEDIUM uniquement).

--starting-on est envoyé à l'API exactement comme donné — pas de découpage.

--frequency est une chaîne dans le DSL propre à Motion, pas un objet. Cela surprend les gens. Le vocabulaire est documenté à https://docs.usemotion.com/cookbooks/frequency/ :

  • Codes jour : MO, TU, WE, TH, FR, SA, SU
  • Quotidien : daily_every_day, daily_every_week_day, daily_specific_days_[MO, TU, FR]
  • Hebdomadaire : weekly_any_day, weekly_any_week_day, weekly_specific_days_[MO, FR]
  • Bi-hebdomadaire : biweekly_first_week_any_day, biweekly_first_week_specific_days_[MO, TU], biweekly_second_week_any_week_day
  • Mensuel par jour de semaine : monthly_first_MO, monthly_second_TU, monthly_last_FR
  • Mensuel par date : monthly_1, monthly_15, monthly_31 (un jour hors limites revient au dernier jour du mois)
  • Mensuel par semaine : monthly_any_day_first_week, monthly_any_week_day_last_week, monthly_last_day_of_month
  • Trimestriel : quarterly_first_day, quarterly_last_week_day, quarterly_any_day_first_week

Un tableau de jours doit toujours être associé à un type de fréquence — [MO, FR] seul n'est pas valide.

Un appel complet, puisque la chaîne de fréquence est la partie difficile :

python3 motion.py recurring-tasks create \
    --name "Revue hebdomadaire" --workspace-id ws_1 --assignee-id usr_1 \
    --frequency "weekly_specific_days_[MO,FR]" \
    --deadline-type SOFT --duration 30 --ideal-time 16:00 \
    --schedule "Work Hours" --priority MEDIUM

Notez les ensembles plus restreints ici : les tâches récurrentes ne prennent que la priorité HIGH/MEDIUM et seulement les types d'échéance HARD/SOFT, alors que le reste de l'API autorise également ASAP/LOW et NONE. Le CLI applique les deux.

Commentaires

CommandeEndpoint
comments list --task-id …GET /v1/comments
comments create --task-id … [--content …]POST /v1/comments

--content est du Markdown GitHub Flavored à l'envoi et revient en HTML. Il est optionnel — sans --content, le corps de la requête ne porte que l'identifiant de la tâche. Nous n'avons pas vérifié si Motion accepte un commentaire vide ; passez --content quand vous voulez dire quelque chose.

Champs personnalisés — notez le préfixe /beta/

CommandeEndpoint
custom-fields list --workspace-id …GET /beta/workspaces/{workspaceId}/custom-fields
custom-fields create --workspace-id … --type … --name …POST /beta/workspaces/{workspaceId}/custom-fields
custom-fields delete <id> --workspace-id … --yesDELETE /beta/workspaces/{workspaceId}/custom-fields/{id}
custom-field-values add-task <task_id> …POST /beta/custom-field-values/task/{taskId}
custom-field-values add-project <project_id> …POST /beta/custom-field-values/project/{projectId}
custom-field-values delete-from-task <task_id> <value_id> --yesDELETE /beta/custom-field-values/task/{taskId}/custom-fields/{valueId}
custom-field-values delete-from-project <project_id> <value_id> --yesDELETE /beta/custom-field-values/project/{projectId}/custom-fields/{valueId}

Les 12 types de champs personnalisés, exactement comme orthographiés : text, url, date, person, multiPerson, phone, select, multiSelect, number, email, checkbox, relatedTo.

Définir une valeur nécessite --custom-field-instance-id (le champ sur l'espace de travail), --type, et --value. --value est du JSON, donc citez-le correctement : --value '"bonjour"' pour une chaîne, --value '42' pour un nombre, --value '["A","B"]' pour une multi-sélection, --value 'true' pour une case à cocher.

python3 motion.py custom-field-values add-task tsk_123 \
    --custom-field-instance-id cfi_1 --type select --value '"Alpha"'

custom-fields create prend --metadata en JSON — format (plain/formatted/percent, nombres uniquement), toggle pour les cases à cocher, et options pour les champs de sélection :

--metadata '{"options":[{"id":"a","value":"Alpha","color":"#ff0000"}]}'

Utilisateurs, espaces de travail, statuts, plannings

CommandeEndpoint
meGET /v1/users/me
users list [--workspace-id …] [--team-id …]GET /v1/users
workspaces list [--ids …]GET /v1/workspaces
statuses list [--workspace-id …]GET /v1/statuses
schedules listGET /v1/schedules

workspaces list --ids est répétable, et --all suit le curseur.

users list doit être cadré. Passez --workspace-id ou --team-id ; le script refuse un appel non cadré tôt avec ce message plutôt que de dépenser une requête sur le 400 que l'API retourne. Pour lister tout le monde, bouclez sur vos espaces de travail.

schedules list retourne les fenêtres d'heures de travail, pas les événements. Chaque planning a son propre timezone et un tableau par jour de blocs {start, end} — et un jour peut contenir plusieurs blocs (par exemple, un planning scolaire avec un bloc le matin et un le soir). Ne lisez pas cela comme "l'utilisateur a un bloc de travail par jour", et ne le traitez pas comme le calendrier ; pour ce qui est effectivement réservé, lisez les valeurs scheduledStart des tâches.

Escape hatch brute

python3 motion.py raw GET  /v1/tasks --query status=Todo --query status=Doing
python3 motion.py raw GET  /v1/tasks --query assigneeId=u1 --query includeAllStatuses=true --all
python3 motion.py raw POST /v1/comments --body '{"taskId":"t1","content":"salut"}'
python3 motion.py raw DELETE /v1/tasks/t1 --yes

Utilisez cela pour tout ce que les commandes nommées ne couvrent pas. --query KEY=VALUE est répétable et répéter le même KEY les envoie tous (l'API les traite comme un tableau implicite).

--body accepte soit un texte JSON inline, soit un chemin de fichier préfixé par @ (--body @payload.json). Toujours spécifier --endpoint. --all est disponible ici aussi : il récupère toutes les pages et rassemble les résultats en un seul tableau, exactement comme les commandes de liste dédiées.

Chaque requête brute porte le comportement de base : X-API-Key, User-Agent, backoff sur 429, et --yes obligatoire pour les suppressions.

Différences en direct

Deux pièges qui ont causé des bugs réels pendant le développement de ce client et qui pourraient surprendre les futurs utilisateurs.

1. Les booléens en minuscules (encore). Motion analyse les paramètres de requête. Les True et False de Python sont des noms en anglais ; la chaîne littérale true ne l'est pas. Envoyer includeAllStatuses=True dans une requête crée un paramètre inattendu que Motion n'analyse pas comme un booléen : la réponse est un 400 qui dit que allStatuses=false a été reçu. Le client envoie toujours des littéraux en minuscules, donc cela ne se produit que si vous écrivez votre propre appel avec requests.

2. L'utilisateur virtuel dans les assignations. Motion crée un utilisateur motion pour le bot ou le système et l'utilise comme valeur par défaut pour certaines tâches sans personne assignée. Les listes d'utilisateurs incluent "motion" avec un email comme motion@…. L'assigner à une tâche (où c'est autorisé) ne produit pas d'erreur mais n'est pas vraiment utile — filtrez-le si vous cherchez un humain réel.

Skills associés