FXCode
ENTRAR →
Doc. 10 — Funciones

Importar tareas

Migra tus tareas desde Notion (o cualquier otra herramienta) con sus comentarios, adjuntos, personas y fechas originales, sin duplicar al reimportar.

Trae tus tareas tal como estaban. La importación crea o actualiza las tareas de un proyecto a partir de un archivo JSON: con su descripción en Markdown, estado, prioridad, entorno y versión, quién la tiene asignada y quién la pidió, sus enlaces, sus comentarios (con su autor y su fecha) y sus archivos. Las fechas de creación, última edición y cierre se conservan.

Es idempotente: cada tarea se identifica por su source (p. ej. notion) y su externalId. Importar el mismo archivo otra vez actualiza esas tareas; no las duplica. Así puedes migrar, revisar y volver a importar con el mapeo corregido.

En dos pasos

# 1. Ensayo: no escribe nada. Dice qué se crearía o actualizaría y qué no mapeó
fxc tasks import notion.json --project-id <id> --dry-run

# 2. Importación de verdad (repetirla actualiza, no duplica)
fxc tasks import notion.json --project-id <id>

La CLI valida el archivo, sube las imágenes locales incrustadas en el Markdown, importa las tareas y después sube sus adjuntos. Los archivos que ya están en la tarea (mismo nombre y tamaño) se saltan al reimportar.

Desde un asistente, la herramienta MCP import_tasks hace lo mismo ({ file, projectId, dryRun }). Por HTTP es POST /projects/:id/tasks/import (añade ?dryRun=true para el ensayo); hace falta ser del equipo del proyecto. El API no recibe archivos: las rutas locales solo las entiende la CLI.

El archivo

{
  "version": 1,
  "source": "notion",
  "statusMap": { "Not started": "No Iniciada", "In progress": "Working", "Completed": "Terminada" },
  "priorityMap": { "High": "Alta", "Medium": "Media", "Low": "Baja" },
  "createMissingPeople": true,
  "tasks": [
    {
      "externalId": "1a2b3c",
      "externalUrl": "https://www.notion.so/acme/Login-roto-1a2b3c",
      "title": "El login falla con Safari",
      "description": "Pasos:\n\n| Paso | Resultado |\n|---|---|\n| Abrir /login | OK |\n| Enviar | Error 500 |\n\n![captura](./notion-import/captura.png)",
      "status": "In progress",
      "priority": "High",
      "environment": "producción",
      "version": "1.4.0",
      "tags": ["auth", "safari"],
      "assignee": "Leonardo",
      "requester": "Alexander Navarro",
      "createdAt": "2025-02-10T09:30:00Z",
      "updatedAt": "2025-02-12T16:00:00Z",
      "links": [{ "url": "https://github.com/acme/web/issues/42", "label": "Issue" }],
      "comments": [
        {
          "author": "Leonardo",
          "createdAt": "2025-02-11T10:00:00Z",
          "content": "Lo reproduzco. Adjunto el informe.",
          "attachments": ["./notion-import/informe.docx"]
        }
      ],
      "attachments": ["./notion-import/datos.xlsx"]
    }
  ]
}

Tienes un ejemplo completo de cinco tareas en el repositorio: docs/examples/notion-import.json.

Nivel del archivo

Campo Tipo Obligatorio Qué hace
version 1 no Versión del formato.
source texto (≤ 50) no (import) Origen por defecto de las tareas: notion, jira, trello… Forma parte de la clave de idempotencia.
statusMap objeto no Nombre del estado en el origen → nombre del estado en FXCode.
priorityMap objeto no Igual para las prioridades.
createMissingPeople booleano no (false) Crea como persona sin cuenta a quien no se encuentre. Sin él, solo se informa.
createMissingReleases booleano no (false) Crea las entregas nombradas en release que no existan en el proyecto. Sin él, solo se informa.
tasks lista sí De 1 a 500 tareas.

Cada tarea

Campo Tipo Obligatorio Notas
externalId texto (≤ 200) sí Id de la tarea en el origen. Único por source dentro del archivo.
source texto no Sobrescribe el source del archivo para esta tarea.
externalUrl URL http(s) no Enlace a la tarea original.
title texto (1–500) sí
description Markdown (≤ 50.000) no Tablas, citas, listas e imágenes.
status texto no Se traduce con statusMap; si no está, se busca tal cual.
priority texto no Se traduce con priorityMap.
environment texto (≤ 100) no P. ej. producción, staging.
version texto (≤ 100) no P. ej. 1.4.0.
tags lista de textos no Hasta 30. Al reimportar reemplaza las etiquetas.
kind TASK | IDEA no Por defecto, tarea.
assignee email o nombre no Ver personas.
requester email o nombre no Quién la pidió.
createdAt, updatedAt, completedAt fecha ISO 8601 no 2025-02-10 o 2025-02-10T09:30:00Z.
dueDate fecha ISO 8601 no Fecha de entrega; se queda el día (00:00 UTC).
release texto (≤ 100) no Nombre de la entrega del proyecto (sin mayúsculas ni acentos). Sin release, una tarea con version se enlaza a la entrega que se llame igual, si existe.
links lista { url, label? } no Hasta 50. Se añaden los que faltan (por URL) y se actualiza su etiqueta.
comments lista no Hasta 200. Ver comentarios.
attachments lista de rutas no Hasta 20 archivos locales (≤ 10 MB cada uno), relativos al JSON.

Lo que el archivo no trae no se toca al actualizar: una tarea reimportada sin description conserva la que tenía. Los campos que no son del formato se ignoran y aparecen en el informe.

Comentarios

Campo Tipo Obligatorio Notas
content Markdown (≤ 20.000) sí Puede incrustar imágenes locales.
author email o nombre no Se conserva aunque no tenga cuenta.
createdAt fecha ISO 8601 no La fecha original.
externalId texto no Clave del comentario. Sin ella se deriva de autor + fecha + contenido.
attachments lista de rutas no Hasta 10 archivos, adjuntos a ese comentario.

Los comentarios también son idempotentes: reimportar el mismo comentario lo actualiza. Si cambias su texto en el origen y no tiene externalId, entra como uno nuevo.

Estados y prioridades

statusMap traduce los nombres del origen a los tuyos, sin importar mayúsculas ni tildes. Un estado que no mapea deja la tarea sin estado (o con el que tenía, si ya existía) y sale en el informe. Si la tarea llega en un estado terminado y no trae completedAt, se fecha con su updatedAt.

Las prioridades funcionan igual. Si tu catálogo de prioridades está vacío, el API crea Alta, Media y Baja al arrancar; también puedes sembrarlas tú (es idempotente: con alguna prioridad ya creada no hace nada):

fxc settings priorities seed
fxc settings priorities list

Personas

assignee, requester y el author de cada comentario aceptan un email o un nombre exacto (sin importar mayúsculas ni tildes; vale el nombre o nombre y apellido):

  • Por email se busca en todo el directorio de personas.
  • Por nombre, entre el equipo del proyecto y las personas que ya cuelgan de sus tareas (así una persona creada en una importación anterior se reconoce al reimportar). Si dos personas comparten nombre, no se adivina: sale como ambiguous.
  • Si no se encuentra y createMissingPeople es true, se crea como persona sin cuenta. En la tarea se ve «Asignado a Leonardo» o «Pedido por Alexander Navarro» igual que con una cuenta.
  • Sin ese flag, el campo se deja vacío y la persona sale en el informe como missing.

Si la persona tiene cuenta, la tarea queda asignada también a su usuario (aparece en «mis tareas»).

Imágenes y adjuntos

  • Imágenes incrustadas: la CLI busca ![texto](ruta) con rutas locales en las descripciones y los comentarios, las sube como imágenes públicas (png, jpg, webp, gif o avif, hasta 5 MB) y reescribe la URL. Las que ya son https:// se dejan como están. Las rutas con espacios valen escritas tal cual entre <…> o codificadas como las exporta Notion (image%203.png).

  • Adjuntos: los de attachments se suben a la tarea (o al comentario) después de importarla. Son privados: se descargan desde la tarea con un enlace temporal. Cualquier tipo de archivo (docx, xlsx, pdf, zip…), hasta 10 MB.

  • Dry-run: no sube nada. El informe dice cuántas imágenes y adjuntos se subirían (wouldUpload).

Reimportar sin deshacer tu limpieza

Reimportar el mismo archivo actualiza las tareas que ya existen con todo lo que trae el archivo: título, descripción, estado, prioridad, etiquetas, asignado… Si después de importar editaste tareas a mano, limita qué se actualiza con --fields (o fields en el archivo). Las tareas nuevas se crean completas igualmente.

# Añadir comentarios y fechas originales sin tocar títulos, estados ni etiquetas
fxc tasks import notion.json --project-id <id> --fields comments,dates --dry-run
fxc tasks import notion.json --project-id <id> --fields comments,dates

Campos: title, description, status, priority, environment, version, tags, kind, externalUrl, assignee, requester, dates (createdAt, updatedAt y completedAt), dueDate, release, links, comments y attachments. Con fields, la CLI solo sube las imágenes y los adjuntos que de verdad se van a escribir.

Limpiar después de importar

Una importación rara vez llega limpia: duplicadas, tareas viejas, etiquetas que ya no significan nada. Ordénalo desde la CLI (o seleccionando varias tareas en la web):

# Archivar lo que ya no aplica: sale de las listas, del avance del proyecto y del MCP (no se borra nada)
fxc tasks archive <id> <id> …
fxc tasks list --project-id <id> --archived          # revisar lo archivado (unarchive lo recupera)

# Los mismos cambios a muchas tareas a la vez
fxc tasks update --ids a,b,c --status "No Iniciada" --priority Media --add-tag migrada --remove-tag notion-viejo
fxc tasks update --ids a,b --assigned-person-id <personId>   # una persona sin cuenta

# Fusionar duplicadas en una: comentarios, adjuntos, enlaces, notas y solicitudes pasan a la destino
fxc tasks merge <destinoId> <duplicadaId> <duplicadaId>

# Ordenar el trabajo: relaciones, subtareas y la cola
fxc tasks link-task <id> --blocked-by <otraId>
fxc tasks update <id> --parent-id <madreId>
fxc tasks queue <id> --first

Reimportar respeta tu limpieza. Una tarea archivada sigue archivada al reimportar, y una fusionada también: la de origen conserva su source + externalId, así que la importación la actualiza (archivada) en vez de volver a crearla. Combínalo con --fields (por ejemplo --fields comments,dates) para que reimportar no pise los estados, las etiquetas o los títulos que corregiste a mano. Más en Kanban, cola y relaciones.

El informe

El ensayo y la importación devuelven lo mismo (con --json, tal cual):

{
  "dryRun": true,
  "summary": { "total": 5, "create": 4, "update": 1 },
  "tasks": [{ "externalId": "1a2b3c", "source": "notion", "action": "update", "title": "…", "taskId": "cm…" }],
  "unmapped": {
    "statuses": [{ "name": "Blocked", "count": 2 }],
    "priorities": [],
    "people": [{ "ref": "Leonardo", "count": 3, "resolution": "will-create" }],
    "fields": [{ "field": "tasks[].Sprint", "count": 5 }],
    "releases": [{ "name": "Fase 9", "count": 4, "resolution": "missing" }]
  },
  "warnings": []
}

resolution es missing, ambiguous, will-create (ensayo con createMissingPeople) o created. En releases es missing (la tarea queda sin entrega), will-create (ensayo con createMissingReleases) o created. La CLI añade cuántas imágenes y adjuntos subiría o subió y cuáles no existen en disco.

La importación no envía correos ni una notificación por tarea: al terminar, las listas abiertas se refrescan una sola vez.

Límites

Límite Valor
Tareas por archivo 500
Título 500 caracteres
Descripción 50.000 caracteres (igual que al crear una tarea a mano)
Etiquetas por tarea 30
Enlaces por tarea 50
Comentarios por tarea 200 (20.000 caracteres cada uno)
Adjuntos 20 por tarea, 10 por comentario, 10 MB cada uno
Entradas de statusMap / priorityMap 100

¿Más de 500 tareas? Divide el archivo: como la clave es source + externalId, puedes importarlo por partes en cualquier orden.

Desde Notion

Exporta la base de datos como Markdown & CSV y convierte cada fila en una tarea del formato de arriba (un script corto o tu asistente lo hace): el id de la página va en externalId, su URL en externalUrl, la columna de estado en status y la de fecha de creación en createdAt. Las imágenes de la exportación quedan en carpetas junto al Markdown: referéncialas con rutas relativas al JSON y la CLI las sube. Una columna de fecha de entrega va en dueDate y una de sprint o hito en release (crea antes las entregas o pasa createMissingReleases: true).