bluesmarties876/skills

Cliente completo para la API REST de Motion (usemotion.com) — los 27 endpoints documentados en tareas, proyectos, tareas recurrentes, comentarios, campos personalizados, usuarios, espacios de trabajo, estados y horarios, además de un escape hatch `raw` (con paginación por cursor) para cualquier cosa no documentada. Maneja autenticación con X-API-Key, paginación por cursor, retroceso por límite de tasa y protege cada eliminación con un flag de confirmación explícito.

¿Qué es skills?

Este skill proporciona un cliente completo para la API REST de Motion, permitiendo a agentes de IA interactuar programáticamente con los 27 endpoints documentados de Motion. Cubre tareas, proyectos, tareas recurrentes, comentarios, campos personalizados, usuarios, espacios de trabajo, estados y horarios, e incluye un escape hatch `raw` para consultar endpoints no documentados con soporte de paginación. Es útil para automatizar flujos de trabajo que involucren la creación, lectura, actualización o eliminación de datos en Motion, como listar tareas, mover proyectos, gestionar tareas recurrentes, o agregar comentarios. El cliente maneja automáticamente la autenticación mediante la variable de entorno `MOTION_API_KEY`, implementa retroceso ante límites de tasa (rate limiting) siguiendo la cabecera `Retry-After`, y requiere un flag `--yes` explícito para toda operación de eliminación. Soporta plataformas de agentes como Claude Code, Cursor y Codex, y está diseñado para funcionar en entornos donde se inyectan credenciales de forma segura a través de `RunWithCredentials`.

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

Installed? Explore more Productividad y colaboración skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

API de Motion

Cliente completo para la API REST de Motion (usemotion.com) — los 27 endpoints documentados en tareas, proyectos, tareas recurrentes, comentarios, campos personalizados, usuarios, espacios de trabajo, estados y horarios, además de un escape hatch raw para cualquier cosa aún no documentada.

Cuándo usar este skill

Úsalo siempre que el trabajo involucre Motion: leer o crear tareas, listar proyectos, publicar comentarios en tareas, gestionar tareas recurrentes, o leer estados de espacios de trabajo, usuarios y horarios.

Credenciales

Una credencial, MOTION_API_KEY. La plataforma la inyecta como variable de entorno cuando un script se ejecuta con RunWithCredentials — nunca pases una clave en la línea de comandos, y nunca la imprimas o la hagas eco.

Obtén una clave en Motion en Configuración → API. Motion la muestra solo una vez, así que cópiala inmediatamente. Si las llamadas empiezan a devolver 401, la clave fue revocada o regenerada y necesita ser reemplazada.

Nota: con la entrega de credenciales en modo proxy, la variable de entorno contiene un marcador de posición no secreto y el valor real se inyecta en las solicitudes a api.usemotion.com. Una credencial recién guardada puede tardar unos minutos en propagarse; un 401 inmediato que luego tiene éxito es propagación, no una clave incorrecta.

Convenciones que sigue la API

Estos son los hechos que marcan la diferencia entre una llamada que funciona y una que no.

AspectoValor
URL basehttps://api.usemotion.com
Cabecera de autenticaciónX-API-Key: <clave>
Verificación de estadoGET /v1/users/me — devuelve el propietario de la clave
Prefijos de ruta/v1/ para todo excepto campos personalizados, que son /beta/
Límites de tasa12 solicitudes/min (individual), hasta 120 (equipos), más en enterprise
DescripcionesGithub Flavored Markdown
Valores de prioridadASAP, HIGH, MEDIUM, LOW (tareas recurrentes: solo HIGH/MEDIUM)
Tipos de plazoHARD, SOFT, NONE (tareas recurrentes: solo HARD/SOFT)

Paginación. Los endpoints de lista toman un cursor y devuelven un envoltorio:

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

La clave del envoltorio es meta, y el campo de la siguiente página es nextCursor. Pasa --all en cualquier comando de lista y el script sigue el cursor por ti, devolviendo un único array plano.

--all se detiene en 100 páginas y luego falla, por lo que una consulta descontrolada no puede bloquear una ejecución — reduce la consulta si lo ves. Y si una página no es ni un objeto JSON ni un array (truncada, o una página de error de gateway), la caminata se niega en lugar de devolver lo que tiene: una lista parcial que parece completa es peor que un error.

Dos endpoints de lista devuelven un array simple sin paginación — GET /v1/statuses y GET /beta/workspaces/{id}/custom-fields. Todo lo demás está envuelto.

Errores. Las respuestas no 2xx llevan {"message", "error", "statusCode"}. Los scripts lo imprimen como JSON en stderr y salen con código no cero. 429 se reintenta con retroceso respetando Retry-After; 400, 401, 403 y 404 se muestran inmediatamente y nunca se reintentan. Los fallos de validación llevan un array errors[] por campo; el cliente añade esos mensajes para que se vea la razón real.

User-Agent es obligatorio. El borde de Motion bloquea la firma predeterminada Python-urllib/x.y con un Cloudflare 403 (browser_signature_banned) antes de que la solicitud llegue a la API. El cliente siempre envía motion-api-skill/1.0; si escribes tu propia solicitud contra esta API, establece un User-Agent explícitamente o recibirás un 403 confuso en lugar de una respuesta real.

Los parámetros de consulta booleanos deben estar en minúsculas. Motion analiza las cadenas de consulta como texto, por lo que un filtro booleano debe ser el literal true, no True de Python. Equivocarse en esto no es un no-op silencioso: la API responde con un 400 confuso que habla del valor opuesto (consulta la sección de diferencias en vivo a continuación). El cliente siempre envía literales en minúsculas.

Referencia de comandos

Cada comando imprime JSON en stdout. python3 motion.py <grupo> --help lista las banderas.

Tareas

ComandoEndpoint
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

Filtros de tasks list: --assignee-id, --project-id, --workspace-id, --name (subcadena sin distinción de mayúsculas/minúsculas), --label (singular, un nombre de etiqueta), --status (repetible), --include-all-statuses. --status y --include-all-statuses son mutuamente excluyentes — enviar ambos es rechazado por la API, y la CLI se niega temprano con ese mensaje en lugar de gastar una solicitud en un 400 garantizado.

--include-all-statuses es la forma de ver una tarea que ha pasado su espacio de trabajo predeterminado (por ejemplo, todo lo que está en Backlog o In Review); sin él, GET /v1/tasks devuelve solo el estado predeterminado y la lista parece vacía. Combinarlo con --all devuelve cada tarea asignada a alguien, en todos los estados.

tasks create / tasks update toman los mismos campos: --name, --workspace-id, --due-date (ISO 8601; requerido para tareas programadas), --duration, --status, --project-id, --description, --priority, --label (repetible), --assignee-id.

--duration acepta un número entero de minutos, o las cadenas NONE / REMINDER (sin distinción de mayúsculas/minúsculas en la entrada, normalizado a mayúsculas).

La auto-programación es triestado, y la distinción importa:

  • omitir ambas banderas → el campo se omite de la solicitud por completo (sin cambios)
  • --auto-scheduled '{"deadlineType":"HARD"}' → lo activa; los campos anidados son startDate, deadlineType (HARD/SOFT/NONE) y schedule (predeterminado Work Hours)
  • --auto-scheduled-off → envía un null explícito, lo desactiva

El estado de la tarea debe tener la auto-programación activada, o la API se niega.

Una respuesta de tarea se ve así — recortada a los campos de los que depende el flujo de trabajo del calendario a continuación:

{
  "id": "tsk_123",
  "name": "Borrador del plan Q4",
  "description": "Esbozar los tres escenarios.",
  "status": { "name": "En progreso", "isResolvedStatus": false },
  "priority": "HIGH",
  "labels": ["q4"],
  "assignees": [{ "id": "usr_1", "name": "Sam Patel" }],
  "project": { "id": "prj_1", "name": "Planificación" },
  "dueDate": "2026-11-01T17:00:00.000Z",
  "duration": 45,
  "scheduledStart": "2026-10-28T09:00:00.000Z",
  "startOn": "2026-10-28",
  "completedTime": null
}

Léelo como: status es un objeto, no una cadena — el filtro que quieres es status.isResolvedStatus, que se vuelve true una vez que una tarea está completada o cancelada. assignees y labels son arrays; project es un objeto o null. scheduledStart y dueDate están en UTC; startOn es una fecha simple.

No hay un endpoint de calendario. La posición de una tarea en el calendario es su scheduledStart (UTC ISO 8601), con duration en minutos y startOn como la fecha simple. Para responder "qué viene después", obtén las tareas y ordena por scheduledStart — no busques un endpoint de horario que devuelva eventos, porque schedules list devuelve solo las ventanas de horas laborables (ver más abajo), no los elementos reservados. Filtra las tareas con status.isResolvedStatus para excluir cualquier cosa ya completada o cancelada.

Proyectos

ComandoEndpoint
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 también toma --due-date, --description (se acepta HTML), --label (repetible), --priority (por defecto es MEDIUM en el servidor), y el par de plantillas --project-definition-id + --stages. Las etapas deben coincidir exactamente con el orden y recuento de la definición, o la API devuelve 400. Para arrays largos de etapas usa --stages @stages.json. La CLI no aplica el par; la API rechaza una lista de etapas sin un id de definición.

Tareas recurrentes

ComandoEndpoint
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 requiere --frequency, --name, --workspace-id y --assignee-id. Opcional: --deadline-type (solo HARD/SOFT — NONE se rechaza aquí), --duration (minutos como entero, o REMINDER; NONE no se acepta en este endpoint), --starting-on, --ideal-time (HH:mm), --schedule (predeterminado Work Hours), --description, --priority (solo HIGH/MEDIUM).

--starting-on se envía a la API exactamente como se da — sin recortes.

--frequency es una cadena en el DSL propio de Motion, no un objeto. Esto confunde a la gente. El vocabulario está documentado en https://docs.usemotion.com/cookbooks/frequency/:

  • Códigos de día: MO, TU, WE, TH, FR, SA, SU
  • Diario: daily_every_day, daily_every_week_day, daily_specific_days_[MO, TU, FR]
  • Semanal: weekly_any_day, weekly_any_week_day, weekly_specific_days_[MO, FR]
  • Quincenal: biweekly_first_week_any_day, biweekly_first_week_specific_days_[MO, TU], biweekly_second_week_any_week_day
  • Mensual por día de semana: monthly_first_MO, monthly_second_TU, monthly_last_FR
  • Mensual por fecha: monthly_1, monthly_15, monthly_31 (un día fuera de rango retrocede al último día del mes)
  • Mensual por semana: monthly_any_day_first_week, monthly_any_week_day_last_week, monthly_last_day_of_month
  • Trimestral: quarterly_first_day, quarterly_last_week_day, quarterly_any_day_first_week

Un array de días siempre debe ir emparejado con un tipo de frecuencia — [MO, FR] solo no es válido.

Una llamada completa, ya que la cadena de frecuencia es la parte difícil:

python3 motion.py recurring-tasks create \
    --name "Revisión semanal" --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

Observa los conjuntos más restringidos aquí: las tareas recurrentes solo toman prioridad HIGH/MEDIUM y solo tipos de plazo HARD/SOFT, mientras que el resto de la API también permite ASAP/LOW y NONE. La CLI aplica ambos.

Comentarios

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

--content es Github Flavored Markdown al entrar y vuelve como HTML. Es opcional — sin --content el cuerpo de la solicitud lleva solo el id de la tarea. Si Motion acepta un comentario vacío no está verificado; pasa --content cuando quieras decir algo.

Campos personalizados — nota el prefijo /beta/

ComandoEndpoint
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}

Los 12 tipos de campos personalizados, exactamente como se escriben: text, url, date, person, multiPerson, phone, select, multiSelect, number, email, checkbox, relatedTo.

Para establecer un valor necesita --custom-field-instance-id (el campo en el espacio de trabajo), --type, y --value. --value es JSON, así que cítalo correctamente: --value '"hola"' para una cadena, --value '42' para un número, --value '["A","B"]' para una selección múltiple, --value 'true' para una casilla de verificación.

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

custom-fields create toma --metadata como JSON — format (plain/formatted/percent, solo números), toggle para casillas de verificación, y options para campos de selección:

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

Usuarios, espacios de trabajo, estados, horarios

ComandoEndpoint
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 es repetible, y --all sigue el cursor.

users list debe tener un alcance. Pasa --workspace-id o --team-id; el script rechaza una llamada sin alcance al principio con ese mensaje en lugar de gastar una solicitud en el 400 que la API devuelve. Para listar a todos, itera sobre tus espacios de trabajo.

schedules list devuelve ventanas de horas laborables, no eventos. Cada horario tiene su propia timezone y un array por día de bloques {start, end} — y un día puede contener múltiples bloques (por ejemplo, un horario de recogida escolar con uno matutino y otro vespertino). No leas esto como "el usuario tiene un bloque de trabajo al día", y no lo trates como el calendario; para lo que está realmente reservado, lee los valores scheduledStart de las tareas.

Escape hatch raw

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":"hola"}'
python3 motion.py raw DELETE /v1/tasks/t1 --yes

Úsalo para cualquier cosa que los comandos nombrados no cubran. --query KEY=VALUE es repetible y repetir la misma clave añade múltiples parámetros de consulta con el mismo nombre (tal como la API espera para filtros repetibles). --body debe ser JSON crudo entre comillas. Toda operación de escritura o eliminación requiere --yes.

Se aplican las mismas reglas de paginación: el parámetro --all recorre las páginas con el cursor; la caminata se niega si encuentra una página que no es JSON, y se detiene en 100 páginas.

Comportamiento de la línea de comandos

Todas las eliminaciones requieren --yes. Si falta, el script imprime "No confirmaste y las cosas peligrosas no se ejecutan. Agrega --yes para confirmar." y sale con código 1 — el mensaje exacto está cableado así que un agente que lo lea puede saber qué hacer.

Para ignorar errores de validación de la API (no recomendado para scripting), pasa --force — esto envía la solicitud incluso si faltan argumentos requeridos en el lado de la CLI. La API aún los rechazará si son realmente obligatorios; útil cuando el comando de lista tiene argumentos que solo se aplican a una sub-ruta.

--help imprime ayuda de uso y sale inmediatamente. El texto proviene del módulo argparse de Python y no está traducido; el agente debe leer las banderas, no la prosa inglesa.

Diferencias en vivo (gotchas de la API Motion)

  • Los filtros booleanos deben estar en minúsculas. ?includeAllStatuses=True devuelve un 400 diciendo "Se esperaba false" — aunque literalmente enviaste True. Solo true / false funcionan. El cliente siempre envía minúsculas.
  • Calendario vs. horario. No existe un endpoint de calendario. schedules list devuelve ventanas, no eventos. Para ver qué está programado en un día, lee las tareas.
  • status es un objeto. No se puede filtrar por status: "Done"; en su lugar, usa status.isResolvedStatus.
  • completedTime puede ser null. No está ausente del JSON; es explícitamente null para tareas no completadas.
  • El filtro name en tasks list es una subcadena sin distinción de mayúsculas/minúsculas. No una coincidencia exacta, no una expresión regular.
  • Los límites de tasa son estrictos. 429 se reintenta automáticamente con retroceso. En pruebas locales, espera al menos 5 segundos entre llamadas para mantenerte por debajo del límite de 12/min.
  • El espacio de trabajo y el proyecto pueden divergir. Los ids de tarea pertenecen a un espacio de trabajo y una tarea puede referirse a un proyecto en otro espacio de trabajo (o ninguno).
  • Repetir un filtro es correcto. --status A --status B se envía como status=A&status=B y la API lo entiende como "A o B". La repetición simple es la forma prevista de OR en Motion.

Sin solicitudes entrantes (estilo teléfono)

Este skill no maneja webhooks ni eventos de Motion "push". Un agente solo puede leer Motion cuando ejecuta un script. No hay forma de recibir una notificación cuando una tarea cambia.

Registro de cambios

Se han eliminado algunos errores de comandos anteriores: los filtros repetibles se envían correctamente, los valores booleanos están siempre en minúsculas, y el escape hatch raw maneja --body correctamente. tasks delete ahora requiere el flag --yes en lugar de preguntar interactivamente. El script nunca imprime la clave API.

Pruebas

Se incluye un conjunto de pruebas en test_motion.py (unittest). Ejecuta python3 -m pytest test_motion.py -v desde este directorio. Las pruebas se burlan de todas las llamadas HTTP, no golpean el servidor real y no necesitan una clave API. Cubren entradas inválidas, casos límite de paginación y cada comando al menos una vez.

Skills relacionados