Daylist

Desarrolladores

Conecta Daylist con tus scripts, atajos y servicios. Todo lo que hace la app pasa por esta API.

Introducción

La API de Daylist te deja leer y cambiar tus tareas, proyectos, secciones, etiquetas y comentarios desde tus propios programas, scripts o automatizaciones. Es una API REST con JSON y sigue de cerca el formato de la API v1 de Todoist: los mismos nombres de campo, las mismas prioridades y el mismo objeto due.

URL basehttps://daylist.gcoder.dev/api/v1
FormatoJSON en UTF-8, campos en snake_case
AutenticaciónToken personal Authorization: Bearer dl_pat_… o sesión de navegador
Versiónv1

Tu primera llamada

Crea un token en Configuración → Integraciones y guárdalo en la variable DAYLIST_TOKEN. Esta llamada crea una tarea para mañana a las 9:

curl
curl -X POST "https://daylist.gcoder.dev/api/v1/tasks" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "Comprar pan", "due_string": "mañana a las 9", "priority": 4}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Comprar pan",
    due_string: "mañana a las 9",
    priority: 4
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/tasks",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "content": "Comprar pan",
        "due_string": "mañana a las 9",
        "priority": 4
    },
)
r.raise_for_status()
datos = r.json()

En qué se parece y en qué no a Todoist

  • Igual: nombres de campo (content, project_id, child_order…), prioridad 1–4 con 4 como la más alta, objeto due con date, string, lang e is_recurring, y rutas como /tasks/quick, /tasks/{id}/close y /tasks/{id}/reopen.
  • Distinto: los listados devuelven un arreglo JSON directo, sin results ni next_cursor; para actualizar se usa PATCH (Todoist usa POST); los ids son UUID; el responsable se llama assignee_id; deadline trae solo date.
  • No existe en Daylist: la Sync API (/sync), /tasks/filter, /user (usa /me), /tasks/completed/* (usa /reports/completed), filtros guardados, recordatorios, adjuntos, plantillas ni OAuth para apps de terceros.

Autenticación

Cada petición a /api/v1 necesita identificarse de una de estas dos formas.

Token personal

Es la forma pensada para scripts y servidores. Créalo en Configuración → Integraciones → Tokens personales, dale un nombre y copia la clave: empieza por dl_pat_ y solo se muestra una vez. Envíala en cada petición:

Authorization: Bearer dl_pat_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  • El token actúa en tu nombre, con tus mismos permisos. Trátalo como una contraseña: guárdalo en una variable de entorno o en un gestor de secretos, nunca en el código ni en el navegador.
  • Puedes borrarlo cuando quieras desde la misma pantalla; deja de funcionar al instante.
  • Si mandas Authorization, la cookie de sesión se ignora. Una cabecera mal formada o un token inválido devuelven 401.
  • Algunas rutas de administración (tokens, sesiones, cuentas conectadas, gestionar equipos) exigen una sesión de navegador y responden 403 con un token. Aparecen marcadas como Solo sesión.

Sesión de navegador

La app web usa una cookie de sesión que se obtiene al entrar con Google o GitHub. Sirve para llamadas hechas desde daylist.gcoder.dev: la API solo acepta CORS desde la propia app, y las escrituras con cookie exigen que la cabecera Origin sea la de Daylist (si no, 403 «Origen no permitido»). Para cualquier integración externa usa un token personal.

Espacio de trabajo: X-Workspace-Id

Todos los datos viven en un espacio: tu espacio personal o un equipo. Las rutas de datos (tareas, proyectos, secciones, etiquetas, comentarios, vistas, actividad, exportación) actúan sobre un solo espacio a la vez:

  • Con token personal, la API usa tu espacio personal salvo que envíes X-Workspace-Id: <id del espacio>. Si no eres miembro de ese espacio, responde 404 «Espacio no encontrado». Consulta los ids con GET /workspaces.
  • Con sesión, la API usa el espacio activo de la sesión (el que elegiste en la app). Si envías X-Workspace-Id y no coincide con el activo, responde 409 «El espacio activo cambió; actualiza la vista».
  • La cabecera se ignora en /me, /workspaces y /workspace-invites.
curl
curl "https://daylist.gcoder.dev/api/v1/projects" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "X-Workspace-Id: 9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "X-Workspace-Id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18",
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/projects",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}", "X-Workspace-Id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18"},
)
r.raise_for_status()
datos = r.json()

Quién soy

GET/api/v1/me

Devuelve tu perfil y el espacio sobre el que actúa la petición. Útil para comprobar que el token funciona.

curl
curl "https://daylist.gcoder.dev/api/v1/me" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/me", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/me",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "user": {
    "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "name": "Gerar Arévalo",
    "email": "tu@correo.com",
    "image": "https://avatars.githubusercontent.com/u/1?v=4"
  },
  "workspace": {
    "id": "f0a3c7e2-1b9d-4e6a-8c5f-3d2b7e9a1c64",
    "name": "Personal",
    "personal": true,
    "role": "owner"
  }
}

Errores

  • 401 Falta la sesión o el token no es válido.

Gestionar tokens

Estas rutas las usa la pantalla de Integraciones. Necesitan sesión: un token no puede crear ni borrar tokens.

Listar tokens

GET/api/v1/tokensSolo sesión

Nunca devuelve la clave completa; start son sus primeros caracteres.

Respuesta 200

[
  {
    "id": "tok_8Hf2kQ",
    "name": "Atajos de Mac",
    "createdAt": "2026-10-13T19:02:11.000Z",
    "expiresAt": null,
    "start": "dl_pat_Xb"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.

Crear un token

POST/api/v1/tokensSolo sesión

Cuerpo (JSON)

CampoTipoDescripción
namestringobligatorioNombre para reconocerlo, de 1 a 32 caracteres.

Respuesta 201

{
  "id": "tok_8Hf2kQ",
  "name": "Atajos de Mac",
  "key": "dl_pat_Xb…",
  "createdAt": "2026-10-13T19:02:11.000Z",
  "expiresAt": null,
  "start": "dl_pat_Xb"
}

key es la única vez que verás la clave completa.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Borrar un token

DELETE/api/v1/tokens/{id}Solo sesión

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 404 Token personal no encontrado.

Tu cuenta

Rutas de la pantalla Configuración → Cuenta. Todas exigen sesión.

Cambiar tu nombre

PATCH/api/v1/meSolo sesión

Cuerpo (JSON)

CampoTipoDescripción
namestringobligatorioDe 1 a 255 caracteres.

Respuesta 200

{
  "user": {
    "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "name": "Gerar A.",
    "email": "tu@correo.com",
    "image": null
  },
  "workspace": {
    "id": "f0a3c7e2-1b9d-4e6a-8c5f-3d2b7e9a1c64",
    "name": "Personal",
    "personal": true,
    "role": "owner"
  }
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Borrar tu cuenta

DELETE/api/v1/meSolo sesión

Borra tu perfil, tu espacio personal, tus tokens y te saca de tus equipos. No se puede deshacer.

Cuerpo (JSON)

CampoTipoDescripción
emailstringobligatorioTu correo, como confirmación.

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 409 Transfiere la propiedad de tus equipos antes de eliminar tu cuenta.
  • 422 El correo no coincide.

Cuentas conectadas

GET/api/v1/me/accountsSolo sesión

Los proveedores con los que puedes entrar.

Respuesta 200

[
  {
    "id": "acc_4Nf7",
    "providerId": "github"
  },
  {
    "id": "acc_9Kd2",
    "providerId": "google"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.

Desconectar una cuenta

DELETE/api/v1/me/accounts/{id}Solo sesión

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 404 Cuenta conectada no encontrada.
  • 409 Conserva al menos una forma de entrar.

Sesiones abiertas

GET/api/v1/me/sessionsSolo sesión

Respuesta 200

[
  {
    "id": "ses_2Lq8",
    "createdAt": "2026-10-14T16:02:11.000Z",
    "expiresAt": "2026-11-13T16:02:11.000Z",
    "ipAddress": "190.86.0.12",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)…",
    "current": true
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.

Cerrar una sesión

DELETE/api/v1/me/sessions/{id}Solo sesión

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 404 Sesión no encontrada.

Las rutas /api/auth/* (inicio de sesión con Google y GitHub) y /api/v1/me/desktop-link son internas de la web y la app de Mac. No forman parte de la API pública y pueden cambiar sin aviso.

Formato y errores

  • Peticiones: cuerpo JSON con Content-Type: application/json. Un cuerpo que no es JSON devuelve 400.
  • Campos desconocidos: se rechazan con 422. Envía solo los campos documentados.
  • Actualizaciones parciales: PATCH cambia solo los campos que envías. Para vaciar un campo que admite null, envía null.
  • Ids: cadenas opacas (UUID en tareas, proyectos, secciones, etiquetas y comentarios). No las conviertas a número.
  • Fechas y horas: los momentos (added_at, updated_at, completed_at…) van en ISO 8601 y UTC. Las fechas de tareas (due.date) son de calendario, YYYY-MM-DD o YYYY-MM-DDTHH:mm:ss sin zona: «hoy» se calcula en la zona del servidor, America/El_Salvador.
  • Borrados: DELETE responde 204 sin cuerpo. Los objetos se marcan como borrados (is_deleted) y dejan de aparecer en los listados.
  • Listados: devuelven un arreglo con todos los resultados, ya ordenados como en la app. Solo la actividad y los reportes se paginan, con el cursor before.

Errores

Cualquier error devuelve un objeto con un mensaje en español, pensado para mostrarse a una persona:

{
  "error": "No entiendo la fecha «el día del juicio»"
}
CódigoCuándo
400El cuerpo no es JSON válido.
401Falta la sesión, la sesión caducó o el token no es válido.
403No tienes permiso: la ruta requiere sesión, tu rol en el equipo no alcanza, o la escritura con cookie llega desde otro origen.
404El objeto no existe, está borrado o no lo puedes ver (por ejemplo, un proyecto privado del que no eres participante). También para rutas inexistentes.
409Conflicto con el estado actual: nombre repetido, la Bandeja de entrada no se borra, el espacio activo cambió, ya eres miembro…
410Enlace de invitación caducado o revocado.
413Cuerpo mayor de 1 KiB en las rutas de invitación.
422Validación: campo con tipo o valor incorrecto, campo desconocido, fecha que no se entiende. El mensaje indica el campo, p. ej. priority: Number must be less than or equal to 4.
429Demasiadas peticiones. Respeta la cabecera Retry-After (segundos). Ver Límites de uso.
500Error interno. Reintenta más tarde.

Tareas

Una tarea pertenece a un proyecto (la Bandeja de entrada si no indicas otro), opcionalmente a una sección y a una tarea padre. Así es el objeto que devuelve la API:

{
  "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
  "assignee_id": null,
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "section_id": null,
  "parent_id": null,
  "content": "Comprar pan",
  "description": "",
  "note_count": 0,
  "priority": 4,
  "due": {
    "date": "2026-10-15T09:00:00",
    "timezone": null,
    "string": "mañana a las 9",
    "lang": "es",
    "is_recurring": false,
    "recurrence": null
  },
  "deadline": null,
  "duration": null,
  "labels": [
    "casa"
  ],
  "child_order": 3,
  "day_order": 0,
  "is_collapsed": false,
  "checked": false,
  "completed_at": null,
  "is_deleted": false,
  "added_at": "2026-10-14T21:03:12.481Z",
  "updated_at": "2026-10-14T21:03:12.481Z",
  "assignee": null
}
CampoDescripción
priority1 normal (P4) · 2 (P3) · 3 (P2) · 4 urgente (P1).
dueFecha de la tarea o null. date es YYYY-MM-DD o YYYY-MM-DDTHH:mm:ss (hora flotante, timezone siempre null); string es el texto original; recurrence describe la repetición.
deadline{"date": "YYYY-MM-DD"} o null.
duration{"amount": 30, "unit": "minute"} o null.
note_countNúmero de comentarios.
assignee / assignee_idResponsable de la tarea: id y un resumen con id, name e image.
checked / completed_atSi está completada y cuándo.

Listar tareas

GET/api/v1/tasks

Devuelve las tareas activas del espacio, ordenadas por child_order. Los filtros se combinan.

Parámetros de consulta

CampoTipoDescripción
project_idstringopcionalSolo las de ese proyecto.
section_idstringopcionalSolo las de esa sección.
parent_idstringopcionalSolo las subtareas directas de esa tarea.
labelstringopcionalSolo las que tienen esa etiqueta (nombre exacto).
assignee_idstringopcionalSolo las asignadas a ese usuario; me significa tú.
checked"true"opcionaltrue devuelve solo las completadas en lugar de las pendientes.
curl
curl "https://daylist.gcoder.dev/api/v1/tasks?project_id=8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41&label=casa" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks?project_id=8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41&label=casa", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/tasks",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    params={
        "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
        "label": "casa"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "assignee_id": null,
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "section_id": null,
    "parent_id": null,
    "content": "Comprar pan",
    "description": "",
    "note_count": 0,
    "priority": 4,
    "due": {
      "date": "2026-10-15T09:00:00",
      "timezone": null,
      "string": "mañana a las 9",
      "lang": "es",
      "is_recurring": false,
      "recurrence": null
    },
    "deadline": null,
    "duration": null,
    "labels": [
      "casa"
    ],
    "child_order": 3,
    "day_order": 0,
    "is_collapsed": false,
    "checked": false,
    "completed_at": null,
    "is_deleted": false,
    "added_at": "2026-10-14T21:03:12.481Z",
    "updated_at": "2026-10-14T21:03:12.481Z",
    "assignee": null
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.

Obtener una tarea

GET/api/v1/tasks/{id}

curl
curl "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
  "assignee_id": null,
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "section_id": null,
  "parent_id": null,
  "content": "Comprar pan",
  "description": "",
  "note_count": 0,
  "priority": 4,
  "due": {
    "date": "2026-10-15T09:00:00",
    "timezone": null,
    "string": "mañana a las 9",
    "lang": "es",
    "is_recurring": false,
    "recurrence": null
  },
  "deadline": null,
  "duration": null,
  "labels": [
    "casa"
  ],
  "child_order": 3,
  "day_order": 0,
  "is_collapsed": false,
  "checked": false,
  "completed_at": null,
  "is_deleted": false,
  "added_at": "2026-10-14T21:03:12.481Z",
  "updated_at": "2026-10-14T21:03:12.481Z",
  "assignee": null
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea no existe, está borrada o no la puedes ver.

Crear una tarea

POST/api/v1/tasks

Cuerpo (JSON)

CampoTipoDescripción
contentstringobligatorioTítulo de la tarea. No puede quedar vacío.
descriptionstringopcionalNotas de la tarea, en Markdown.
project_idstring | nullopcionalProyecto. Si falta o es null, la tarea va a la Bandeja de entrada.
section_idstring | nullopcionalSección; debe pertenecer al proyecto.
parent_idstring | nullopcionalTarea padre. La subtarea hereda el proyecto y la sección del padre.
priorityentero 1–4opcional4 es la más urgente (P1 en la app) y 1 la normal (P4). Igual que en Todoist.
due_stringstring | nullopcionalFecha en lenguaje natural, en español o inglés: mañana a las 9, cada lunes. null o "" quita la fecha. Ver Fechas en lenguaje natural.
due_datestring | nullopcionalFecha exacta YYYY-MM-DD o con hora YYYY-MM-DDTHH:mm[:ss] (hora local, sin zona).
dueobjeto | nullopcionalObjeto due completo, tal como lo devuelve la API. Útil para copiar la fecha de otra tarea.
deadline_datestring | nullopcionalFecha límite YYYY-MM-DD, independiente de due.
durationobjeto | nullopcional{"amount": 30, "unit": "minute"}; unit es minute o day.
labelsstring[]opcionalNombres de etiqueta. Las que no existan se crean.
assignee_idstring | nullopcionalResponsable (id de usuario). Debe tener acceso al proyecto; en el espacio personal solo puedes ser tú.
child_orderenteroopcionalPosición entre sus hermanas. Por defecto, al final.
day_orderenteroopcionalPosición dentro del día en Hoy y Próximo.
is_collapsedbooleanopcionalSi sus subtareas se muestran plegadas.
insert_before / insert_afterstringopcionalId de otra tarea junto a la que se coloca. Usa solo uno de los dos.
order_context"project" | "day"opcionalCon insert_before/insert_after: ordenar en el proyecto (por defecto) o en el día.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/tasks" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "Comprar pan", "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41", "due_string": "mañana a las 9", "priority": 4, "labels": ["casa"]}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Comprar pan",
    project_id: "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    due_string: "mañana a las 9",
    priority: 4,
    labels: [
      "casa"
    ]
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/tasks",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "content": "Comprar pan",
        "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
        "due_string": "mañana a las 9",
        "priority": 4,
        "labels": [
            "casa"
        ]
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 201

{
  "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
  "assignee_id": null,
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "section_id": null,
  "parent_id": null,
  "content": "Comprar pan",
  "description": "",
  "note_count": 0,
  "priority": 4,
  "due": {
    "date": "2026-10-15T09:00:00",
    "timezone": null,
    "string": "mañana a las 9",
    "lang": "es",
    "is_recurring": false,
    "recurrence": null
  },
  "deadline": null,
  "duration": null,
  "labels": [
    "casa"
  ],
  "child_order": 3,
  "day_order": 0,
  "is_collapsed": false,
  "checked": false,
  "completed_at": null,
  "is_deleted": false,
  "added_at": "2026-10-14T21:03:12.481Z",
  "updated_at": "2026-10-14T21:03:12.481Z",
  "assignee": null
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea padre no existe.
  • 409 No se pudo resolver alguna etiqueta (choque al crearla).
  • 422 Falta content, la fecha no se entiende («No entiendo la fecha…»), el proyecto o la sección no existen, la sección no es de ese proyecto, el responsable no tiene acceso, o el anidado crearía un ciclo.

Añadido rápido

POST/api/v1/tasks/quick

Interpreta una frase completa igual que la caja de añadido rápido de la app y crea la tarea. Reconoce:

  • la fecha en lenguaje natural (ver Fechas);
  • #Proyecto y /Sección;
  • @etiqueta (también %etiqueta);
  • p1–p4 para la prioridad;
  • {viernes} entre llaves para la fecha límite;
  • for 30m o for 1h 30m para la duración (requiere una fecha con hora).

Si el proyecto o la sección no existen, la tarea se crea igual y la respuesta lo avisa en warnings.

Cuerpo (JSON)

CampoTipoDescripción
textstringobligatorioLa frase completa.
project_idstring | nullopcionalProyecto por defecto si la frase no trae #Proyecto.
section_idstring | nullopcionalSección por defecto.
parent_idstring | nullopcionalCrear como subtarea de esta tarea.
insert_before / insert_after / order_contextopcionalIgual que al crear una tarea.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/tasks/quick" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"text": "Llamar al dentista mañana a las 10 p2 #Casa @llamadas"}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks/quick", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "Llamar al dentista mañana a las 10 p2 #Casa @llamadas"
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/tasks/quick",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "text": "Llamar al dentista mañana a las 10 p2 #Casa @llamadas"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 201

{
  "task": {
    "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "assignee_id": null,
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "section_id": null,
    "parent_id": null,
    "content": "Llamar al dentista",
    "description": "",
    "note_count": 0,
    "priority": 3,
    "due": {
      "date": "2026-10-15T10:00:00",
      "timezone": null,
      "string": "mañana a las 10",
      "lang": "es",
      "is_recurring": false,
      "recurrence": null
    },
    "deadline": null,
    "duration": null,
    "labels": [
      "llamadas"
    ],
    "child_order": 3,
    "day_order": 0,
    "is_collapsed": false,
    "checked": false,
    "completed_at": null,
    "is_deleted": false,
    "added_at": "2026-10-14T21:03:12.481Z",
    "updated_at": "2026-10-14T21:03:12.481Z",
    "assignee": null
  },
  "warnings": []
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Actualizar una tarea

PATCH/api/v1/tasks/{id}

Acepta los mismos campos que crear, todos opcionales. Cambiar project_id, section_id o parent_id mueve la tarea con sus subtareas. En Todoist esta ruta es POST; en Daylist es PATCH.

curl
curl -X PATCH "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"due_string": "viernes 7pm", "priority": 3}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    due_string: "viernes 7pm",
    priority: 3
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.patch(
    "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "due_string": "viernes 7pm",
        "priority": 3
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
  "assignee_id": null,
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "section_id": null,
  "parent_id": null,
  "content": "Comprar pan",
  "description": "",
  "note_count": 0,
  "priority": 3,
  "due": {
    "date": "2026-10-16T19:00:00",
    "timezone": null,
    "string": "viernes 7pm",
    "lang": "es",
    "is_recurring": false,
    "recurrence": null
  },
  "deadline": null,
  "duration": null,
  "labels": [
    "casa"
  ],
  "child_order": 3,
  "day_order": 0,
  "is_collapsed": false,
  "checked": false,
  "completed_at": null,
  "is_deleted": false,
  "added_at": "2026-10-14T21:03:12.481Z",
  "updated_at": "2026-10-14T21:03:12.481Z",
  "assignee": null
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea no existe, está borrada o no la puedes ver.
  • 422 Validación, content vacío, una tarea no puede ser subtarea de sí misma, o la fecha no se entiende.

Completar una tarea

POST/api/v1/tasks/{id}/close

Completa la tarea y sus subtareas pendientes. Si es recurrente, no se cierra: pasa a su siguiente fecha y la respuesta trae rescheduled: true junto con lo necesario para deshacerlo.

Parámetros de consulta

CampoTipoDescripción
forever"true"opcionalEn una recurrente, cerrarla para siempre en lugar de reprogramarla.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/close" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/close", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/close",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "task": {
    "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "assignee_id": null,
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "section_id": null,
    "parent_id": null,
    "content": "Comprar pan",
    "description": "",
    "note_count": 0,
    "priority": 4,
    "due": {
      "date": "2026-10-19T09:00:00",
      "timezone": null,
      "string": "cada lunes a las 9",
      "lang": "es",
      "is_recurring": true,
      "recurrence": {
        "freq": "weekly",
        "interval": 1,
        "byWeekday": [
          1
        ],
        "time": "09:00"
      }
    },
    "deadline": null,
    "duration": null,
    "labels": [
      "casa"
    ],
    "child_order": 3,
    "day_order": 0,
    "is_collapsed": false,
    "checked": false,
    "completed_at": null,
    "is_deleted": false,
    "added_at": "2026-10-14T21:03:12.481Z",
    "updated_at": "2026-10-14T21:03:12.481Z",
    "assignee": null
  },
  "rescheduled": true,
  "completion_id": "3e7a1c9d-2b5f-4d8e-a6c0-9f1b4e7d2a63",
  "previous_due": {
    "date": "2026-10-12T09:00:00",
    "timezone": null,
    "string": "cada lunes a las 9",
    "lang": "es",
    "is_recurring": true,
    "recurrence": {
      "freq": "weekly",
      "interval": 1,
      "byWeekday": [
        1
      ],
      "time": "09:00"
    }
  }
}

En una tarea normal la respuesta es {"task": {…, "checked": true}, "rescheduled": false}.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea no existe, está borrada o no la puedes ver.

Deshacer la finalización de una recurrente

POST/api/v1/tasks/{id}/undo-close

Devuelve una recurrente a la fecha que tenía antes de completarla y borra esa entrada del historial.

Cuerpo (JSON)

CampoTipoDescripción
completion_idstringobligatorioEl completion_id que devolvió close.
previous_dueobjetoobligatorioEl previous_due que devolvió close.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/undo-close" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"completion_id": "3e7a1c9d-2b5f-4d8e-a6c0-9f1b4e7d2a63", "previous_due": {"date": "2026-10-12T09:00:00", "timezone": null, "string": "cada lunes a las 9", "lang": "es", "is_recurring": true, "recurrence": {"freq": "weekly", "interval": 1, "byWeekday": [1], "time": "09:00"}}}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/undo-close", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    completion_id: "3e7a1c9d-2b5f-4d8e-a6c0-9f1b4e7d2a63",
    previous_due: {
      date: "2026-10-12T09:00:00",
      timezone: null,
      string: "cada lunes a las 9",
      lang: "es",
      is_recurring: true,
      recurrence: {
        freq: "weekly",
        interval: 1,
        byWeekday: [
          1
        ],
        time: "09:00"
      }
    }
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/undo-close",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "completion_id": "3e7a1c9d-2b5f-4d8e-a6c0-9f1b4e7d2a63",
        "previous_due": {
            "date": "2026-10-12T09:00:00",
            "timezone": None,
            "string": "cada lunes a las 9",
            "lang": "es",
            "is_recurring": True,
            "recurrence": {
                "freq": "weekly",
                "interval": 1,
                "byWeekday": [
                    1
                ],
                "time": "09:00"
            }
        }
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
  "assignee_id": null,
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "section_id": null,
  "parent_id": null,
  "content": "Comprar pan",
  "description": "",
  "note_count": 0,
  "priority": 4,
  "due": {
    "date": "2026-10-15T09:00:00",
    "timezone": null,
    "string": "mañana a las 9",
    "lang": "es",
    "is_recurring": false,
    "recurrence": null
  },
  "deadline": null,
  "duration": null,
  "labels": [
    "casa"
  ],
  "child_order": 3,
  "day_order": 0,
  "is_collapsed": false,
  "checked": false,
  "completed_at": null,
  "is_deleted": false,
  "added_at": "2026-10-14T21:03:12.481Z",
  "updated_at": "2026-10-14T21:03:12.481Z",
  "assignee": null
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea o la finalización no existen.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Reabrir una tarea

POST/api/v1/tasks/{id}/reopen

Reabre la tarea, las subtareas que se cerraron con ella y cualquier tarea padre que esté cerrada.

curl
curl -X POST "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/reopen" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/reopen", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/reopen",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
  "assignee_id": null,
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "section_id": null,
  "parent_id": null,
  "content": "Comprar pan",
  "description": "",
  "note_count": 0,
  "priority": 4,
  "due": {
    "date": "2026-10-15T09:00:00",
    "timezone": null,
    "string": "mañana a las 9",
    "lang": "es",
    "is_recurring": false,
    "recurrence": null
  },
  "deadline": null,
  "duration": null,
  "labels": [
    "casa"
  ],
  "child_order": 3,
  "day_order": 0,
  "is_collapsed": false,
  "checked": false,
  "completed_at": null,
  "is_deleted": false,
  "added_at": "2026-10-14T21:03:12.481Z",
  "updated_at": "2026-10-14T21:03:12.481Z",
  "assignee": null
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea no existe, está borrada o no la puedes ver.

Borrar una tarea

DELETE/api/v1/tasks/{id}

Borra la tarea y todas sus subtareas.

curl
curl -X DELETE "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
// 204: sin cuerpo
Python
import os
import requests

r = requests.delete(
    "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
# 204: sin cuerpo

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea no existe, está borrada o no la puedes ver.

Cambios en lote

POST/api/v1/tasks/drag

Aplica una misma acción a varias tareas a la vez (hasta 100), en una sola transacción. Es lo que usa la app al arrastrar o al editar una selección. Devuelve las tareas afectadas.

Cuerpo (JSON)

CampoTipoDescripción
task_idsstring[]obligatorioDe 1 a 100 ids, sin repetir.
actionobjetoobligatorioUna de estas, según type:
{"type": "move", "project_id", "section_id"?, "parent_id"?} mover;
{"type": "schedule", "date": "YYYY-MM-DD" | null, "time"?: "HH:mm" | null, "duration"?: 1–1440} reprogramar;
{"type": "priority", "priority": 1–4} cambiar prioridad;
{"type": "label", "label": "nombre"} añadir una etiqueta;
{"type": "reorder", "target_id", "placement": "before" | "after" | "inside", "order": "project" | "day"} reordenar.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/tasks/drag" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"task_ids": ["5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95", "a91d4e7c-3b2f-4d8a-b6e1-7c5f0a2d9e38"], "action": {"type": "schedule", "date": "2026-10-16", "time": null}}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/tasks/drag", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    task_ids: [
      "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "a91d4e7c-3b2f-4d8a-b6e1-7c5f0a2d9e38"
    ],
    action: {
      type: "schedule",
      date: "2026-10-16",
      time: null
    }
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/tasks/drag",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "task_ids": [
            "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
            "a91d4e7c-3b2f-4d8a-b6e1-7c5f0a2d9e38"
        ],
        "action": {
            "type": "schedule",
            "date": "2026-10-16",
            "time": None
        }
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "assignee_id": null,
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "section_id": null,
    "parent_id": null,
    "content": "Comprar pan",
    "description": "",
    "note_count": 0,
    "priority": 4,
    "due": {
      "date": "2026-10-16",
      "timezone": null,
      "string": "2026-10-16",
      "lang": "es",
      "is_recurring": false,
      "recurrence": null
    },
    "deadline": null,
    "duration": null,
    "labels": [
      "casa"
    ],
    "child_order": 3,
    "day_order": 0,
    "is_collapsed": false,
    "checked": false,
    "completed_at": null,
    "is_deleted": false,
    "added_at": "2026-10-14T21:03:12.481Z",
    "updated_at": "2026-10-14T21:03:12.481Z",
    "assignee": null
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Alguna tarea o el destino no existen.
  • 422 Acción inválida, ids repetidos, fecha que no existe o un movimiento que crearía un ciclo.

Fechas en lenguaje natural

El campo due_string y el añadido rápido entienden español e inglés, sin importar mayúsculas ni acentos. «Hoy» se calcula en la zona America/El_Salvador. Las horas son flotantes: «a las 9» significa las 9 donde estés. Si la API no entiende el texto responde 422 «No entiendo la fecha…»; sin fecha o no date quitan la fecha.

EscribeResultado (si hoy es martes 13 de octubre, 10:15)
hoy, mañana, pasado mañana13, 14 y 15 de octubre
lunes, vie, próximo lunesel siguiente lunes (19), viernes (16)…
próxima semana, fin de semana, fin de meslunes 19, sábado 17, sábado 31
15 oct, 15 de octubre, 15/10, 2026-10-1515 de octubre (día/mes por defecto)
el 2727 de este mes, o del siguiente si ya pasó
en 3 días, en 2 semanas, en 2 horas16 de octubre, 27 de octubre, hoy a las 12:15
a las 9, 9pm, 21:00la próxima vez que sean esa hora
mañana a las 9, viernes 7pm, mañana en la mañanafecha con hora (mañana 9:00, tarde 12:00, noche 19:00)
algún díadentro de dos meses
cada día, todos los días laborablesrecurrente diaria (laborables: lunes a viernes)
cada lunes y viernes, cada 2 semanasrecurrente semanal
cada 27, cada último día, cada 15 de eneromensual y anual
cada lunes a las 9, cada mañana, cada horarecurrente con hora
cada! 3 mesescon ! la siguiente fecha cuenta desde que la completas, no desde la fecha prevista
tomorrow at 9pm, every monday, next weeklo mismo en inglés

Una recurrente guarda su regla en due.recurrence, por ejemplo {"freq": "weekly", "interval": 1, "byWeekday": [1, 5], "time": "09:00"}. freq es hourly, daily, weekly, monthly o yearly; byWeekday usa 1 = lunes … 7 = domingo; byMonthDay −1 es el último día. Para escribir fechas, usa siempre due_string o due_date; no construyas recurrence a mano.

Prioridades

En la apppriorityAñadido rápido
P1 (roja)4p1
P2 (naranja)3p2
P3 (azul)2p3
P4 (sin color)1p4

Proyectos

Cada espacio tiene una Bandeja de entrada (inbox_project: true) que no se borra, no se archiva ni admite subproyectos. can_manage_access indica si puedes cambiar la privacidad y los participantes.

Listar proyectos

GET/api/v1/projects

Incluye los archivados (is_archived). La Bandeja va primero.

curl
curl "https://daylist.gcoder.dev/api/v1/projects" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/projects",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "0b6e3f6a-2d1c-4f7e-9a41-5c8d2e7b9f10",
    "visibility": "team",
    "created_by": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "name": "Bandeja de entrada",
    "description": "",
    "color": "charcoal",
    "parent_id": null,
    "child_order": 0,
    "inbox_project": true,
    "is_favorite": false,
    "is_archived": false,
    "is_collapsed": false,
    "view_style": "list",
    "is_deleted": false,
    "created_at": "2026-10-13T18:22:05.114Z",
    "updated_at": "2026-10-14T16:40:51.902Z",
    "can_manage_access": false
  },
  {
    "id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "visibility": "team",
    "created_by": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "name": "Casa",
    "description": "",
    "color": "blue",
    "parent_id": null,
    "child_order": 2,
    "inbox_project": false,
    "is_favorite": true,
    "is_archived": false,
    "is_collapsed": false,
    "view_style": "list",
    "is_deleted": false,
    "created_at": "2026-10-13T18:22:05.114Z",
    "updated_at": "2026-10-14T16:40:51.902Z",
    "can_manage_access": true
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.

Obtener un proyecto

GET/api/v1/projects/{id}

curl
curl "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "visibility": "team",
  "created_by": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
  "name": "Casa",
  "description": "",
  "color": "blue",
  "parent_id": null,
  "child_order": 2,
  "inbox_project": false,
  "is_favorite": true,
  "is_archived": false,
  "is_collapsed": false,
  "view_style": "list",
  "is_deleted": false,
  "created_at": "2026-10-13T18:22:05.114Z",
  "updated_at": "2026-10-14T16:40:51.902Z",
  "can_manage_access": true
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.

Crear un proyecto

POST/api/v1/projects

Cuerpo (JSON)

CampoTipoDescripción
namestringobligatorioDe 1 a 120 caracteres.
descriptionstringopcionalDescripción del proyecto.
colorstringopcionalNombre de color al estilo Todoist (charcoal, blue, berry_red…). Por defecto charcoal.
parent_idstring | nullopcionalProyecto padre.
child_orderenteroopcionalPosición entre sus hermanos. Por defecto, al final.
is_favoritebooleanopcionalMostrar en Favoritos.
view_style"list" | "board" | "calendar"opcionalVista del proyecto. Por defecto list.
visibility"team" | "private"opcionalSolo en equipos: private lo restringe a sus participantes. Un privado debe ser raíz y sin subproyectos.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/projects" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Casa", "color": "blue", "is_favorite": true}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Casa",
    color: "blue",
    is_favorite: true
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/projects",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "name": "Casa",
        "color": "blue",
        "is_favorite": True
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 201

{
  "id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "visibility": "team",
  "created_by": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
  "name": "Casa",
  "description": "",
  "color": "blue",
  "parent_id": null,
  "child_order": 2,
  "inbox_project": false,
  "is_favorite": true,
  "is_archived": false,
  "is_collapsed": false,
  "view_style": "list",
  "is_deleted": false,
  "created_at": "2026-10-13T18:22:05.114Z",
  "updated_at": "2026-10-14T16:40:51.902Z",
  "can_manage_access": true
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto padre no existe.
  • 409 Un privado no admite subproyectos, o un privado fuera de un equipo.
  • 422 Nombre vacío, la Bandeja como padre, o un ciclo de proyectos.

Actualizar un proyecto

PATCH/api/v1/projects/{id}

Los mismos campos que al crear, más is_archived y is_collapsed. Archivar o desarchivar se aplica también a sus subproyectos.

Cuerpo (JSON)

CampoTipoDescripción
is_archivedbooleanopcionalArchivar o desarchivar (con sus subproyectos).
is_collapsedbooleanopcionalPlegar sus subproyectos en la barra lateral.
…opcionalCualquier campo de crear.
curl
curl -X PATCH "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Casa y jardín", "is_archived": false}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Casa y jardín",
    is_archived: false
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.patch(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "name": "Casa y jardín",
        "is_archived": False
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "visibility": "team",
  "created_by": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
  "name": "Casa y jardín",
  "description": "",
  "color": "blue",
  "parent_id": null,
  "child_order": 2,
  "inbox_project": false,
  "is_favorite": true,
  "is_archived": false,
  "is_collapsed": false,
  "view_style": "list",
  "is_deleted": false,
  "created_at": "2026-10-13T18:22:05.114Z",
  "updated_at": "2026-10-14T16:40:51.902Z",
  "can_manage_access": true
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 No puedes administrar el acceso de este proyecto (al cambiar visibility).
  • 404 El proyecto no existe o no lo puedes ver.
  • 409 La Bandeja no se mueve ni se archiva.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Borrar un proyecto

DELETE/api/v1/projects/{id}

Borra el proyecto, sus subproyectos, secciones y tareas.

curl
curl -X DELETE "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
// 204: sin cuerpo
Python
import os
import requests

r = requests.delete(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
# 204: sin cuerpo

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.
  • 409 La Bandeja de entrada no se puede borrar.

Reordenar un proyecto

POST/api/v1/projects/reorder

Cuerpo (JSON)

CampoTipoDescripción
project_idstringobligatorioProyecto que se mueve.
target_idstringobligatorioProyecto de referencia.
placement"before" | "after" | "inside"obligatorioAntes, después o dentro (como subproyecto) del de referencia.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/projects/reorder" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41", "target_id": "0b6e3f6a-2d1c-4f7e-9a41-5c8d2e7b9f10", "placement": "after"}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/reorder", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    project_id: "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    target_id: "0b6e3f6a-2d1c-4f7e-9a41-5c8d2e7b9f10",
    placement: "after"
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/projects/reorder",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
        "target_id": "0b6e3f6a-2d1c-4f7e-9a41-5c8d2e7b9f10",
        "placement": "after"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "visibility": "team",
  "created_by": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
  "name": "Casa",
  "description": "",
  "color": "blue",
  "parent_id": null,
  "child_order": 2,
  "inbox_project": false,
  "is_favorite": true,
  "is_archived": false,
  "is_collapsed": false,
  "view_style": "list",
  "is_deleted": false,
  "created_at": "2026-10-13T18:22:05.114Z",
  "updated_at": "2026-10-14T16:40:51.902Z",
  "can_manage_access": true
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.
  • 409 Movimiento no permitido (privados, Bandeja).
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Mover un proyecto a otro espacio

POST/api/v1/projects/{id}/move-workspace

Mueve el proyecto entero (subproyectos, secciones, tareas y comentarios) a otro espacio del que seas miembro.

Cuerpo (JSON)

CampoTipoDescripción
workspace_idstringobligatorioEspacio de destino.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/move-workspace" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"workspace_id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18"}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/move-workspace", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    workspace_id: "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18"
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/move-workspace",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "workspace_id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "workspace_id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 No puedes mover al destino.
  • 404 Proyecto o espacio no encontrado.
  • 409 La Bandeja no se mueve, o ya está en ese espacio.

Participantes y responsables

Participantes de un proyecto privado

GET/api/v1/projects/{id}/members

curl
curl "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/members" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/members", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/members",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "Rk2vB8nQ5wT1yH7cM4zX9gF3dS6pJ0aL",
    "user_id": "Hq8wN2rT5yV1bK7mC4xZ9gD3fS6pL0jA",
    "name": "Ana López",
    "image": null,
    "role": "member"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.
  • 409 El proyecto no es privado.

Fijar los participantes

PUT/api/v1/projects/{id}/members

Reemplaza la lista completa de participantes de un proyecto privado. Solo quien lo creó, el dueño o un administrador del equipo.

Cuerpo (JSON)

CampoTipoDescripción
user_idsstring[]obligatorioIds de usuario (máximo 500); todos deben ser miembros del equipo.
curl
curl -X PUT "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/members" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_ids": ["Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE", "Hq8wN2rT5yV1bK7mC4xZ9gD3fS6pL0jA"]}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/members", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    user_ids: [
      "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
      "Hq8wN2rT5yV1bK7mC4xZ9gD3fS6pL0jA"
    ]
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.put(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/members",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "user_ids": [
            "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
            "Hq8wN2rT5yV1bK7mC4xZ9gD3fS6pL0jA"
        ]
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "Rk2vB8nQ5wT1yH7cM4zX9gF3dS6pJ0aL",
    "user_id": "Hq8wN2rT5yV1bK7mC4xZ9gD3fS6pL0jA",
    "name": "Ana López",
    "image": null,
    "role": "member"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 No puedes administrar el acceso.
  • 404 El proyecto no existe o no lo puedes ver.
  • 409 El proyecto no es privado.
  • 422 Los participantes deben pertenecer al equipo.

Posibles responsables

GET/api/v1/projects/{id}/assignees

Usuarios a los que se puede asignar una tarea de este proyecto.

curl
curl "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/assignees" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/assignees", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/assignees",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "Rk2vB8nQ5wT1yH7cM4zX9gF3dS6pJ0aL",
    "user_id": "Hq8wN2rT5yV1bK7mC4xZ9gD3fS6pL0jA",
    "name": "Ana López",
    "image": null,
    "role": "member"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.

Secciones

Listar secciones

GET/api/v1/sections

Parámetros de consulta

CampoTipoDescripción
project_idstringopcionalSolo las de ese proyecto.
curl
curl "https://daylist.gcoder.dev/api/v1/sections?project_id=8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/sections?project_id=8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/sections",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    params={
        "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "name": "Compras",
    "description": null,
    "section_order": 1,
    "is_collapsed": false,
    "is_archived": false,
    "archived_at": null,
    "is_deleted": false,
    "added_at": "2026-10-14T15:10:44.020Z",
    "updated_at": "2026-10-14T15:10:44.020Z"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.

Crear una sección

POST/api/v1/sections

Cuerpo (JSON)

CampoTipoDescripción
project_idstringobligatorioProyecto.
namestringobligatorioDe 1 a 120 caracteres.
descriptionstring | nullopcionalDescripción.
section_orderenteroopcionalPosición. Por defecto, al final.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/sections" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41", "name": "Compras"}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/sections", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    project_id: "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    name: "Compras"
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/sections",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
        "name": "Compras"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 201

{
  "id": "c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "name": "Compras",
  "description": null,
  "section_order": 1,
  "is_collapsed": false,
  "is_archived": false,
  "archived_at": null,
  "is_deleted": false,
  "added_at": "2026-10-14T15:10:44.020Z",
  "updated_at": "2026-10-14T15:10:44.020Z"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Actualizar una sección

PATCH/api/v1/sections/{id}

Cambiar project_id mueve la sección con todas sus tareas a otro proyecto.

Cuerpo (JSON)

CampoTipoDescripción
namestringopcional
descriptionstring | nullopcional
project_idstringopcionalMover a otro proyecto.
section_orderenteroopcional
is_collapsedbooleanopcional
is_archivedbooleanopcionalArchivar o desarchivar.
curl
curl -X PATCH "https://daylist.gcoder.dev/api/v1/sections/c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Súper"}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/sections/c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Súper"
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.patch(
    "https://daylist.gcoder.dev/api/v1/sections/c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "name": "Súper"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "name": "Súper",
  "description": null,
  "section_order": 1,
  "is_collapsed": false,
  "is_archived": false,
  "archived_at": null,
  "is_deleted": false,
  "added_at": "2026-10-14T15:10:44.020Z",
  "updated_at": "2026-10-14T15:10:44.020Z"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Sección o proyecto de destino no encontrados.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Borrar una sección

DELETE/api/v1/sections/{id}

Borra la sección y todas sus tareas.

curl
curl -X DELETE "https://daylist.gcoder.dev/api/v1/sections/c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/sections/c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
// 204: sin cuerpo
Python
import os
import requests

r = requests.delete(
    "https://daylist.gcoder.dev/api/v1/sections/c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
# 204: sin cuerpo

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Sección no encontrada.

Reordenar una sección

POST/api/v1/sections/reorder

Devuelve las secciones del proyecto en su nuevo orden.

Cuerpo (JSON)

CampoTipoDescripción
section_idstringobligatorio
target_idstringobligatorioSección de referencia.
placement"before" | "after"obligatorio
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/sections/reorder" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"section_id": "c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03", "target_id": "1f6b3d8e-5a2c-4e9f-b7d0-3c8a1e6f2b45", "placement": "before"}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/sections/reorder", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    section_id: "c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
    target_id: "1f6b3d8e-5a2c-4e9f-b7d0-3c8a1e6f2b45",
    placement: "before"
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/sections/reorder",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "section_id": "c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
        "target_id": "1f6b3d8e-5a2c-4e9f-b7d0-3c8a1e6f2b45",
        "placement": "before"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "name": "Compras",
    "description": null,
    "section_order": 1,
    "is_collapsed": false,
    "is_archived": false,
    "archived_at": null,
    "is_deleted": false,
    "added_at": "2026-10-14T15:10:44.020Z",
    "updated_at": "2026-10-14T15:10:44.020Z"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Sección no encontrada.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Etiquetas

Las tareas guardan sus etiquetas por nombre en labels. Renombrar una etiqueta actualiza todas sus tareas; borrarla la quita de ellas.

Listar etiquetas

GET/api/v1/labels

curl
curl "https://daylist.gcoder.dev/api/v1/labels" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/labels", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/labels",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56",
    "name": "casa",
    "color": "charcoal",
    "item_order": 1,
    "is_favorite": false,
    "is_deleted": false
  },
  {
    "id": "b5d1f8a3-7c2e-4b9d-a1e6-0f4c8b2d7e19",
    "name": "llamadas",
    "color": "green",
    "item_order": 2,
    "is_favorite": false,
    "is_deleted": false
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.

Crear una etiqueta

POST/api/v1/labels

Cuerpo (JSON)

CampoTipoDescripción
namestringobligatorioDe 1 a 60 caracteres, sin @, # ni %. Única en el espacio, sin distinguir mayúsculas.
colorstringopcionalPor defecto charcoal.
item_orderenteroopcionalPosición.
is_favoritebooleanopcional
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/labels" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "casa", "color": "charcoal"}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/labels", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "casa",
    color: "charcoal"
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/labels",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "name": "casa",
        "color": "charcoal"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 201

{
  "id": "e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56",
  "name": "casa",
  "color": "charcoal",
  "item_order": 1,
  "is_favorite": false,
  "is_deleted": false
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 409 Ya existe la etiqueta.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Actualizar una etiqueta

PATCH/api/v1/labels/{id}

Cuerpo (JSON)

CampoTipoDescripción
namestringopcionalDe 1 a 60 caracteres, sin @, # ni %. Única en el espacio, sin distinguir mayúsculas.
colorstringopcionalPor defecto charcoal.
item_orderenteroopcionalPosición.
is_favoritebooleanopcional
curl
curl -X PATCH "https://daylist.gcoder.dev/api/v1/labels/e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "hogar"}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/labels/e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "hogar"
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.patch(
    "https://daylist.gcoder.dev/api/v1/labels/e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "name": "hogar"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56",
  "name": "hogar",
  "color": "charcoal",
  "item_order": 1,
  "is_favorite": false,
  "is_deleted": false
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Etiqueta no encontrada.
  • 409 Ya existe esa etiqueta.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Borrar una etiqueta

DELETE/api/v1/labels/{id}

curl
curl -X DELETE "https://daylist.gcoder.dev/api/v1/labels/e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/labels/e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
// 204: sin cuerpo
Python
import os
import requests

r = requests.delete(
    "https://daylist.gcoder.dev/api/v1/labels/e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
# 204: sin cuerpo

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Etiqueta no encontrada.

Comentarios

Hay comentarios de tarea y comentarios de proyecto. El contenido es texto con Markdown, de 1 a 20 000 caracteres.

De tarea

Listar comentarios de una tarea

GET/api/v1/comments

Parámetros de consulta

CampoTipoDescripción
task_idstringobligatorioLa tarea.
curl
curl "https://daylist.gcoder.dev/api/v1/comments?task_id=5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/comments?task_id=5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/comments",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    params={
        "task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27",
    "task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "content": "Que sea integral.",
    "posted_at": "2026-10-14T21:10:02.337Z",
    "updated_at": "2026-10-14T21:10:02.337Z"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea no existe, está borrada o no la puedes ver.
  • 422 task_id es obligatorio.

Comentar una tarea

POST/api/v1/comments

Cuerpo (JSON)

CampoTipoDescripción
task_idstringobligatorio
contentstringobligatorioTexto del comentario.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/comments" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95", "content": "Que sea integral."}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/comments", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    task_id: "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    content: "Que sea integral."
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/comments",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
        "content": "Que sea integral."
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 201

{
  "id": "7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27",
  "task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
  "content": "Que sea integral.",
  "posted_at": "2026-10-14T21:10:02.337Z",
  "updated_at": "2026-10-14T21:10:02.337Z"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 La tarea no existe, está borrada o no la puedes ver.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Editar un comentario

PATCH/api/v1/comments/{id}

Cuerpo (JSON)

CampoTipoDescripción
contentstringobligatorio
curl
curl -X PATCH "https://daylist.gcoder.dev/api/v1/comments/7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "Que sea integral, por favor."}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/comments/7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Que sea integral, por favor."
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.patch(
    "https://daylist.gcoder.dev/api/v1/comments/7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "content": "Que sea integral, por favor."
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27",
  "task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
  "content": "Que sea integral, por favor.",
  "posted_at": "2026-10-14T21:10:02.337Z",
  "updated_at": "2026-10-14T21:12:40.019Z"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Comentario no encontrado.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Borrar un comentario

DELETE/api/v1/comments/{id}

curl
curl -X DELETE "https://daylist.gcoder.dev/api/v1/comments/7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/comments/7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
// 204: sin cuerpo
Python
import os
import requests

r = requests.delete(
    "https://daylist.gcoder.dev/api/v1/comments/7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
# 204: sin cuerpo

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Comentario no encontrado.

De proyecto

Listar comentarios de un proyecto

GET/api/v1/projects/{id}/comments

curl
curl "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comments" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comments", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comments",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39",
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "content": "Lista para el fin de semana.",
    "posted_at": "2026-10-14T20:01:13.508Z",
    "updated_at": "2026-10-14T20:01:13.508Z"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.

Comentar un proyecto

POST/api/v1/projects/{id}/comments

Cuerpo (JSON)

CampoTipoDescripción
contentstringobligatorio
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comments" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "Lista para el fin de semana."}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comments", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Lista para el fin de semana."
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comments",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "content": "Lista para el fin de semana."
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 201

{
  "id": "2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39",
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "content": "Lista para el fin de semana.",
  "posted_at": "2026-10-14T20:01:13.508Z",
  "updated_at": "2026-10-14T20:01:13.508Z"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Editar un comentario de proyecto

PATCH/api/v1/project-comments/{id}

Cuerpo (JSON)

CampoTipoDescripción
contentstringobligatorio
curl
curl -X PATCH "https://daylist.gcoder.dev/api/v1/project-comments/2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "Lista para el sábado."}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/project-comments/2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    content: "Lista para el sábado."
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.patch(
    "https://daylist.gcoder.dev/api/v1/project-comments/2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "content": "Lista para el sábado."
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39",
  "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
  "content": "Lista para el sábado.",
  "posted_at": "2026-10-14T20:01:13.508Z",
  "updated_at": "2026-10-14T20:01:13.508Z"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Comentario no encontrado.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Borrar un comentario de proyecto

DELETE/api/v1/project-comments/{id}

curl
curl -X DELETE "https://daylist.gcoder.dev/api/v1/project-comments/2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/project-comments/2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
// 204: sin cuerpo
Python
import os
import requests

r = requests.delete(
    "https://daylist.gcoder.dev/api/v1/project-comments/2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
# 204: sin cuerpo

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Comentario no encontrado.

Historial de comentarios de un proyecto

GET/api/v1/projects/{id}/comment-activity

Cada alta, edición y borrado de comentarios del proyecto, del más antiguo al más reciente. action es added, edited o deleted.

curl
curl "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comment-activity" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comment-activity", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comment-activity",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "6c2e9a4f-1d7b-4e3a-b8c5-2f0d7a9e1b84",
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "comment_id": "2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39",
    "action": "added",
    "content": "Lista para el fin de semana.",
    "posted_at": "2026-10-14T20:01:13.508Z"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.

Vistas y lecturas

Rutas de solo lectura que devuelven lo mismo que las pantallas de la app, ya agrupado y ordenado (fecha, prioridad, hora y orden del día).

Bandeja de entrada

GET/api/v1/views/inbox

curl
curl "https://daylist.gcoder.dev/api/v1/views/inbox" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/views/inbox", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/views/inbox",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "project": {
    "id": "0b6e3f6a-2d1c-4f7e-9a41-5c8d2e7b9f10",
    "visibility": "team",
    "created_by": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "name": "Bandeja de entrada",
    "description": "",
    "color": "charcoal",
    "parent_id": null,
    "child_order": 0,
    "inbox_project": true,
    "is_favorite": false,
    "is_archived": false,
    "is_collapsed": false,
    "view_style": "list",
    "is_deleted": false,
    "created_at": "2026-10-13T18:22:05.114Z",
    "updated_at": "2026-10-14T16:40:51.902Z",
    "can_manage_access": false
  },
  "tasks": [
    {
      "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "assignee_id": null,
      "project_id": "0b6e3f6a-2d1c-4f7e-9a41-5c8d2e7b9f10",
      "section_id": null,
      "parent_id": null,
      "content": "Comprar pan",
      "description": "",
      "note_count": 0,
      "priority": 4,
      "due": {
        "date": "2026-10-15T09:00:00",
        "timezone": null,
        "string": "mañana a las 9",
        "lang": "es",
        "is_recurring": false,
        "recurrence": null
      },
      "deadline": null,
      "duration": null,
      "labels": [],
      "child_order": 3,
      "day_order": 0,
      "is_collapsed": false,
      "checked": false,
      "completed_at": null,
      "is_deleted": false,
      "added_at": "2026-10-14T21:03:12.481Z",
      "updated_at": "2026-10-14T21:03:12.481Z",
      "assignee": null
    }
  ]
}

Errores

  • 401 Falta la sesión o el token no es válido.

Hoy

GET/api/v1/views/today

Tareas pendientes vencidas (overdue) y de hoy (today).

curl
curl "https://daylist.gcoder.dev/api/v1/views/today" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/views/today", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/views/today",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "date": "2026-10-14",
  "overdue": [],
  "today": [
    {
      "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "assignee_id": null,
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "section_id": null,
      "parent_id": null,
      "content": "Comprar pan",
      "description": "",
      "note_count": 0,
      "priority": 4,
      "due": {
        "date": "2026-10-14",
        "timezone": null,
        "string": "hoy",
        "lang": "es",
        "is_recurring": false,
        "recurrence": null
      },
      "deadline": null,
      "duration": null,
      "labels": [
        "casa"
      ],
      "child_order": 3,
      "day_order": 0,
      "is_collapsed": false,
      "checked": false,
      "completed_at": null,
      "is_deleted": false,
      "added_at": "2026-10-14T21:03:12.481Z",
      "updated_at": "2026-10-14T21:03:12.481Z",
      "assignee": null
    }
  ]
}

Errores

  • 401 Falta la sesión o el token no es válido.

Próximo

GET/api/v1/views/upcoming

Vencidas y un bloque por día desde hoy, incluidos los días vacíos.

Parámetros de consulta

CampoTipoDescripción
daysenteroopcionalCuántos días, de 1 a 730. Por defecto 14.
curl
curl "https://daylist.gcoder.dev/api/v1/views/upcoming?days=7" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/views/upcoming?days=7", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/views/upcoming",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    params={
        "days": 7
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "start": "2026-10-14",
  "end": "2026-10-20",
  "overdue": [],
  "days": [
    {
      "date": "2026-10-14",
      "tasks": []
    },
    {
      "date": "2026-10-15",
      "tasks": [
        {
          "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
          "assignee_id": null,
          "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
          "section_id": null,
          "parent_id": null,
          "content": "Comprar pan",
          "description": "",
          "note_count": 0,
          "priority": 4,
          "due": {
            "date": "2026-10-15T09:00:00",
            "timezone": null,
            "string": "mañana a las 9",
            "lang": "es",
            "is_recurring": false,
            "recurrence": null
          },
          "deadline": null,
          "duration": null,
          "labels": [
            "casa"
          ],
          "child_order": 3,
          "day_order": 0,
          "is_collapsed": false,
          "checked": false,
          "completed_at": null,
          "is_deleted": false,
          "added_at": "2026-10-14T21:03:12.481Z",
          "updated_at": "2026-10-14T21:03:12.481Z",
          "assignee": null
        }
      ]
    },
    {
      "date": "2026-10-16",
      "tasks": []
    }
  ]
}

El ejemplo de respuesta está recortado: days trae un elemento por cada día del rango.

Errores

  • 401 Falta la sesión o el token no es válido.

Asignadas a mí

GET/api/v1/views/assigned

Tareas pendientes del espacio en las que eres responsable.

curl
curl "https://daylist.gcoder.dev/api/v1/views/assigned" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/views/assigned", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/views/assigned",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "assignee_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "section_id": null,
    "parent_id": null,
    "content": "Comprar pan",
    "description": "",
    "note_count": 0,
    "priority": 4,
    "due": {
      "date": "2026-10-15T09:00:00",
      "timezone": null,
      "string": "mañana a las 9",
      "lang": "es",
      "is_recurring": false,
      "recurrence": null
    },
    "deadline": null,
    "duration": null,
    "labels": [
      "casa"
    ],
    "child_order": 3,
    "day_order": 0,
    "is_collapsed": false,
    "checked": false,
    "completed_at": null,
    "is_deleted": false,
    "added_at": "2026-10-14T21:03:12.481Z",
    "updated_at": "2026-10-14T21:03:12.481Z",
    "assignee": {
      "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
      "name": "Gerar Arévalo",
      "image": null
    }
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.

Buscar

GET/api/v1/search

Busca texto literal, sin distinguir mayúsculas ni acentos, en el título y la descripción de tareas pendientes, en proyectos no archivados y en etiquetas.

Parámetros de consulta

CampoTipoDescripción
qstringobligatorioTexto a buscar. Vacío devuelve listas vacías.
curl
curl "https://daylist.gcoder.dev/api/v1/search?q=pan" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/search?q=pan", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/search",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    params={
        "q": "pan"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "tasks": [
    {
      "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "assignee_id": null,
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "section_id": null,
      "parent_id": null,
      "content": "Comprar pan",
      "description": "",
      "note_count": 0,
      "priority": 4,
      "due": {
        "date": "2026-10-15T09:00:00",
        "timezone": null,
        "string": "mañana a las 9",
        "lang": "es",
        "is_recurring": false,
        "recurrence": null
      },
      "deadline": null,
      "duration": null,
      "labels": [
        "casa"
      ],
      "child_order": 3,
      "day_order": 0,
      "is_collapsed": false,
      "checked": false,
      "completed_at": null,
      "is_deleted": false,
      "added_at": "2026-10-14T21:03:12.481Z",
      "updated_at": "2026-10-14T21:03:12.481Z",
      "assignee": null
    }
  ],
  "projects": [],
  "labels": []
}

Errores

  • 401 Falta la sesión o el token no es válido.

Tareas completadas

GET/api/v1/reports/completed

Historial de finalizaciones, de la más reciente a la más antigua (incluye cada repetición completada de una recurrente). Para pedir la página siguiente, pasa en before el completed_at y el id del último elemento unidos por |.

Parámetros de consulta

CampoTipoDescripción
fromYYYY-MM-DDopcionalDesde ese día (incluido), en la zona del servidor.
toYYYY-MM-DDopcionalHasta ese día (incluido).
project_idstringopcionalSolo de ese proyecto.
limitenteroopcionalDe 1 a 200. Por defecto 100.
beforestringopcionalCursor completed_at|id.
curl
curl "https://daylist.gcoder.dev/api/v1/reports/completed?from=2026-10-01&to=2026-10-14&limit=50" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/reports/completed?from=2026-10-01&to=2026-10-14&limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/reports/completed",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    params={
        "from": "2026-10-01",
        "to": "2026-10-14",
        "limit": 50
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "3e7a1c9d-2b5f-4d8e-a6c0-9f1b4e7d2a63",
    "task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "content": "Comprar pan",
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "section_id": null,
    "completed_at": "2026-10-14T22:15:31.204118Z",
    "recurring": false,
    "still_open": false
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 422 Fecha inválida, from posterior a to, limit fuera de rango o cursor inválido.

Tareas añadidas

GET/api/v1/reports/added

Tareas creadas, de la más reciente a la más antigua. Mismos parámetros que completadas; el cursor usa created_at|id.

curl
curl "https://daylist.gcoder.dev/api/v1/reports/added?from=2026-10-01" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/reports/added?from=2026-10-01", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/reports/added",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    params={
        "from": "2026-10-01"
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "content": "Comprar pan",
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "section_id": null,
    "created_at": "2026-10-14T21:03:12.481233Z",
    "still_open": true
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Progreso de hoy

GET/api/v1/stats/today

Tareas completadas hoy y esta semana frente a tus metas.

curl
curl "https://daylist.gcoder.dev/api/v1/stats/today" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/stats/today", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/stats/today",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "completed_today": 3,
  "goal": 5,
  "completed_week": 12,
  "weekly_goal": 25,
  "rest_day": false,
  "vacation": false
}

Errores

  • 401 Falta la sesión o el token no es válido.

Preferencias

Leer preferencias

GET/api/v1/preferences

Tus ajustes de la app (son tuyos, no del espacio).

curl
curl "https://daylist.gcoder.dev/api/v1/preferences" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/preferences", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/preferences",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "name": "Gerar Arévalo",
  "theme": "light",
  "syncTheme": true,
  "autoDark": false,
  "darkTheme": "auto",
  "startView": "/today",
  "weekStart": 1,
  "timeFormat": "24h",
  "dateFormat": "DMY",
  "sound": false,
  "sidebar": {
    "inbox": true,
    "today": true,
    "upcoming": true,
    "labels": true,
    "reports": true,
    "counters": true
  },
  "quickAddOrder": [
    "project",
    "date",
    "priority",
    "labels",
    "deadline",
    "more"
  ],
  "quickAddHidden": [
    "priority",
    "labels",
    "deadline"
  ],
  "showActionLabels": true,
  "showDescription": false,
  "dailyGoal": 5,
  "weeklyGoal": 25,
  "celebrate": true,
  "daysOff": [
    0,
    6
  ],
  "vacation": false
}

Errores

  • 401 Falta la sesión o el token no es válido.

Cambiar preferencias

PATCH/api/v1/preferences

Envía solo lo que cambias. Estos campos van en camelCase, a diferencia del resto de la API. Cambiar name requiere sesión.

Cuerpo (JSON)

CampoTipoDescripción
dailyGoal / weeklyGoalentero 1–10000opcionalMetas de tareas completadas.
weekStart0 | 1opcionalLa semana empieza en domingo (0) o lunes (1).
daysOffentero[]opcionalDías de descanso, 0 = domingo … 6 = sábado.
vacationbooleanopcionalModo vacaciones.
startViewstringopcional/inbox, /today, /upcoming, /labels o /reports.
timeFormat / dateFormatstringopcional12h/24h; DMY/MDY/YMD.
…opcionalCualquier otro campo de la respuesta de leer preferencias.
curl
curl -X PATCH "https://daylist.gcoder.dev/api/v1/preferences" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"dailyGoal": 8, "daysOff": [0, 6]}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/preferences", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    dailyGoal: 8,
    daysOff: [
      0,
      6
    ]
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.patch(
    "https://daylist.gcoder.dev/api/v1/preferences",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "dailyGoal": 8,
        "daysOff": [
            0,
            6
        ]
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "name": "Gerar Arévalo",
  "theme": "light",
  "syncTheme": true,
  "autoDark": false,
  "darkTheme": "auto",
  "startView": "/today",
  "weekStart": 1,
  "timeFormat": "24h",
  "dateFormat": "DMY",
  "sound": false,
  "sidebar": {
    "inbox": true,
    "today": true,
    "upcoming": true,
    "labels": true,
    "reports": true,
    "counters": true
  },
  "quickAddOrder": [
    "project",
    "date",
    "priority",
    "labels",
    "deadline",
    "more"
  ],
  "quickAddHidden": [
    "priority",
    "labels",
    "deadline"
  ],
  "showActionLabels": true,
  "showDescription": false,
  "dailyGoal": 8,
  "weeklyGoal": 25,
  "celebrate": true,
  "daysOff": [
    0,
    6
  ],
  "vacation": false
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 Cambiar name con un token.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Equipos y espacios

Un espacio es tu espacio personal o un equipo compartido. Los roles en un equipo son owner (dueño, uno solo), admin y member. Los proyectos de un equipo son visibles para todos sus miembros salvo los privados.

Las rutas /workspaces/{id}/… actúan sobre el espacio activo de la sesión: si {id} no es el activo, responden 404. Actívalo antes con POST /workspaces/{id}/activate. Salvo listar espacios, todas exigen sesión.

Listar mis espacios

GET/api/v1/workspaces

Funciona también con token: así obtienes el id para X-Workspace-Id.

curl
curl "https://daylist.gcoder.dev/api/v1/workspaces" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/workspaces", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/workspaces",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "f0a3c7e2-1b9d-4e6a-8c5f-3d2b7e9a1c64",
    "name": "Personal",
    "personal": true,
    "role": "owner"
  },
  {
    "id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18",
    "name": "Familia",
    "personal": false,
    "role": "owner"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.

Crear un equipo

POST/api/v1/workspacesSolo sesión

Cuerpo (JSON)

CampoTipoDescripción
namestringobligatorioDe 1 a 120 caracteres.

Respuesta 201

{
  "id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18",
  "name": "Familia",
  "personal": false,
  "role": "owner"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Activar un espacio

POST/api/v1/workspaces/{id}/activateSolo sesión

Cambia el espacio activo de la sesión.

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 404 Espacio no encontrado.

Renombrar un equipo

PATCH/api/v1/workspaces/{id}Solo sesión

Cuerpo (JSON)

CampoTipoDescripción
namestringobligatorio

Respuesta 200

{
  "id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18",
  "name": "Familia Arévalo",
  "personal": false,
  "role": "owner"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 Requiere sesión o rol de dueño/administrador.
  • 404 Espacio no encontrado.
  • 409 Esta operación requiere un equipo.

Borrar un equipo

DELETE/api/v1/workspaces/{id}Solo sesión

Solo el dueño. Borra el equipo con todo su contenido.

Cuerpo (JSON)

CampoTipoDescripción
namestringobligatorioEl nombre exacto del equipo, como confirmación.

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 No eres el dueño.
  • 404 Espacio no encontrado.
  • 422 Escribe el nombre del equipo para confirmar.

Miembros

Listar miembros

GET/api/v1/workspaces/{id}/membersSolo sesión

Respuesta 200

[
  {
    "id": "Pm5tC1xV8zN3bQ7wR2kL9dF4",
    "user_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "name": "Gerar Arévalo",
    "image": null,
    "role": "owner"
  },
  {
    "id": "Rk2vB8nQ5wT1yH7cM4zX9gF3dS6pJ0aL",
    "user_id": "Hq8wN2rT5yV1bK7mC4xZ9gD3fS6pL0jA",
    "name": "Ana López",
    "image": null,
    "role": "member"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 404 Espacio no encontrado.
  • 409 Esta operación requiere un equipo.

Cambiar el rol de un miembro

PATCH/api/v1/workspaces/{id}/members/{userId}Solo sesión

Cuerpo (JSON)

CampoTipoDescripción
role"admin" | "member"obligatorio

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 Sin permiso, o el miembro es el dueño (transfiere la propiedad primero).
  • 404 Miembro no encontrado.

Quitar a un miembro

DELETE/api/v1/workspaces/{id}/members/{userId}Solo sesión

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 Un miembro no puede expulsar a otros, y nadie puede expulsar al dueño.
  • 404 Miembro no encontrado.

Salir de un equipo

POST/api/v1/workspaces/{id}/leaveSolo sesión

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 409 El dueño debe transferir la propiedad antes de salir.

Transferir la propiedad

POST/api/v1/workspaces/{id}/transferSolo sesión

Cuerpo (JSON)

CampoTipoDescripción
user_idstringobligatorioMiembro que pasa a ser dueño.

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 No eres el dueño.
  • 422 Elige otro miembro.

Enlaces de invitación

Un enlace sirve para varias personas durante 7 días. Solo el dueño y los administradores los gestionan; el espacio personal no admite invitaciones (409).

Listar enlaces activos

GET/api/v1/workspaces/{id}/invitesSolo sesión

Respuesta 200

[
  {
    "id": "4a9d2e7c-8b1f-4c6a-9e3d-5f0b2a7c1e96",
    "expires_at": "2026-10-21T18:30:00.000Z",
    "uses": 2,
    "created_at": "2026-10-14T18:30:00.000Z"
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 No puedes administrar enlaces.
  • 404 Espacio no encontrado.

Crear un enlace

POST/api/v1/workspaces/{id}/invitesSolo sesión

Respuesta 201

{
  "id": "4a9d2e7c-8b1f-4c6a-9e3d-5f0b2a7c1e96",
  "expires_at": "2026-10-21T18:30:00.000Z",
  "uses": 0,
  "created_at": "2026-10-14T18:30:00.000Z",
  "url": "https://daylist.gcoder.dev/unirse/q3ZtW8…"
}

url solo se devuelve aquí; la API guarda únicamente su huella.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 No puedes administrar enlaces.
  • 409 El espacio personal no admite invitaciones.

Revocar un enlace

DELETE/api/v1/workspaces/{id}/invites/{linkId}Solo sesión

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 No puedes administrar enlaces.
  • 404 Enlace no encontrado.

Aceptar una invitación

POST/api/v1/workspace-invites/acceptSolo sesión

Cuerpo (JSON)

CampoTipoDescripción
tokenstringobligatorioLos 43 caracteres finales del enlace /unirse/….

Respuesta 201

{
  "workspace_id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 403 La ruta necesita una sesión de navegador; un token personal no basta.
  • 409 Ya eres miembro.
  • 410 Enlace no disponible.
  • 413 Cuerpo mayor de 1 KiB.
  • 429 Demasiados intentos.

Vista previa de una invitación

POST/api/workspace-invites/preview

Pública, sin autenticación, y fuera de /api/v1. Devuelve el nombre del equipo y cuántos miembros tiene, para mostrarlo antes de unirse.

Cuerpo (JSON)

CampoTipoDescripción
tokenstringobligatorioLos 43 caracteres del enlace.

Respuesta 200

{
  "name": "Familia",
  "member_count": 3
}

Errores

  • 410 Enlace no disponible.
  • 413 Cuerpo mayor de 1 KiB.
  • 422 Token con formato inválido.
  • 429 Demasiados intentos.

Actividad

Registro de lo que pasa en el espacio: quién creó, editó, movió, asignó, completó o borró tareas, cambios en proyectos, comentarios y miembros. Solo incluye lo que puedes ver.

typeCuándo
task.created, task.edited, task.moved, task.assignedAlta y cambios de una tarea. task.edited trae en payload.fields los campos nuevos.
task.completed, task.reopened, task.deletedCompletar, reabrir y borrar.
project.created, project.moved, project.archivedProyectos.
comment.added, comment.edited, comment.deletedComentarios de tarea y de proyecto.
member.joined, member.left, member.removed, member.role_changedMiembros del equipo.

Listar actividad

GET/api/v1/activity

De lo más reciente a lo más antiguo. Si next_cursor no es null, pásalo en before para la página siguiente. entity es el estado actual de la tarea o el proyecto, si sigue existiendo.

Parámetros de consulta

CampoTipoDescripción
project_idstringopcionalSolo la de ese proyecto.
limitenteroopcionalDe 1 a 100. Por defecto 50.
beforestringopcionalEl next_cursor de la página anterior.
curl
curl "https://daylist.gcoder.dev/api/v1/activity?limit=20" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/activity?limit=20", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/activity",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    params={
        "limit": 20
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "items": [
    {
      "id": "6c2e9a4f-1d7b-4e3a-b8c5-2f0d7a9e1b84",
      "workspace_id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18",
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "actor": {
        "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
        "name": "Gerar Arévalo",
        "image": "https://avatars.githubusercontent.com/u/1?v=4"
      },
      "type": "task.completed",
      "entity_type": "task",
      "entity_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "payload": {
        "recurring": false
      },
      "created_at": "2026-10-14T22:15:31.204118Z",
      "entity": {
        "content": "Comprar pan",
        "checked": true
      }
    }
  ],
  "next_cursor": "2026-10-14T22:15:31.204118Z|6c2e9a4f-1d7b-4e3a-b8c5-2f0d7a9e1b84"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.
  • 422 limit fuera de rango o cursor inválido.

Actividad de un proyecto

GET/api/v1/projects/{id}/activity

Igual que la anterior con project_id fijo. Acepta limit y before.

curl
curl "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/activity" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/activity", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/activity",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "items": [
    {
      "id": "6c2e9a4f-1d7b-4e3a-b8c5-2f0d7a9e1b84",
      "workspace_id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18",
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "actor": {
        "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
        "name": "Gerar Arévalo",
        "image": "https://avatars.githubusercontent.com/u/1?v=4"
      },
      "type": "task.completed",
      "entity_type": "task",
      "entity_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "payload": {
        "recurring": false
      },
      "created_at": "2026-10-14T22:15:31.204118Z",
      "entity": {
        "content": "Comprar pan",
        "checked": true
      }
    }
  ],
  "next_cursor": null
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 El proyecto no existe o no lo puedes ver.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Exportar

Exportar el espacio

GET/api/v1/export

Una instantánea coherente de todo el espacio: preferencias, proyectos, secciones, tareas (incluidas las completadas), etiquetas, comentarios de tarea y de proyecto, y la actividad. Es lo que descarga Configuración → Copias de seguridad. Para otro equipo, usa X-Workspace-Id.

curl
curl "https://daylist.gcoder.dev/api/v1/export" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/export", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/export",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "schema_version": 1,
  "exported_at": "2026-10-14T23:00:00.000Z",
  "preferences": {
    "name": "Gerar Arévalo",
    "theme": "light",
    "syncTheme": true,
    "autoDark": false,
    "darkTheme": "auto",
    "startView": "/today",
    "weekStart": 1,
    "timeFormat": "24h",
    "dateFormat": "DMY",
    "sound": false,
    "sidebar": {
      "inbox": true,
      "today": true,
      "upcoming": true,
      "labels": true,
      "reports": true,
      "counters": true
    },
    "quickAddOrder": [
      "project",
      "date",
      "priority",
      "labels",
      "deadline",
      "more"
    ],
    "quickAddHidden": [
      "priority",
      "labels",
      "deadline"
    ],
    "showActionLabels": true,
    "showDescription": false,
    "dailyGoal": 5,
    "weeklyGoal": 25,
    "celebrate": true,
    "daysOff": [
      0,
      6
    ],
    "vacation": false
  },
  "projects": [
    {
      "id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "visibility": "team",
      "created_by": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
      "name": "Casa",
      "description": "",
      "color": "blue",
      "parent_id": null,
      "child_order": 2,
      "inbox_project": false,
      "is_favorite": true,
      "is_archived": false,
      "is_collapsed": false,
      "view_style": "list",
      "is_deleted": false,
      "created_at": "2026-10-13T18:22:05.114Z",
      "updated_at": "2026-10-14T16:40:51.902Z",
      "can_manage_access": true
    }
  ],
  "sections": [
    {
      "id": "c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03",
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "name": "Compras",
      "description": null,
      "section_order": 1,
      "is_collapsed": false,
      "is_archived": false,
      "archived_at": null,
      "is_deleted": false,
      "added_at": "2026-10-14T15:10:44.020Z",
      "updated_at": "2026-10-14T15:10:44.020Z"
    }
  ],
  "tasks": [
    {
      "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "assignee_id": null,
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "section_id": null,
      "parent_id": null,
      "content": "Comprar pan",
      "description": "",
      "note_count": 0,
      "priority": 4,
      "due": {
        "date": "2026-10-15T09:00:00",
        "timezone": null,
        "string": "mañana a las 9",
        "lang": "es",
        "is_recurring": false,
        "recurrence": null
      },
      "deadline": null,
      "duration": null,
      "labels": [
        "casa"
      ],
      "child_order": 3,
      "day_order": 0,
      "is_collapsed": false,
      "checked": false,
      "completed_at": null,
      "is_deleted": false,
      "added_at": "2026-10-14T21:03:12.481Z",
      "updated_at": "2026-10-14T21:03:12.481Z",
      "assignee": null
    }
  ],
  "labels": [
    {
      "id": "e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56",
      "name": "casa",
      "color": "charcoal",
      "item_order": 1,
      "is_favorite": false,
      "is_deleted": false
    }
  ],
  "comments": [
    {
      "id": "7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27",
      "task_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "content": "Que sea integral.",
      "posted_at": "2026-10-14T21:10:02.337Z",
      "updated_at": "2026-10-14T21:10:02.337Z"
    }
  ],
  "project_comments": [
    {
      "id": "2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39",
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "content": "Lista para el fin de semana.",
      "posted_at": "2026-10-14T20:01:13.508Z",
      "updated_at": "2026-10-14T20:01:13.508Z"
    }
  ],
  "activity": [
    {
      "id": "6c2e9a4f-1d7b-4e3a-b8c5-2f0d7a9e1b84",
      "workspace_id": "9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18",
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "actor": {
        "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
        "name": "Gerar Arévalo",
        "image": "https://avatars.githubusercontent.com/u/1?v=4"
      },
      "type": "task.completed",
      "entity_type": "task",
      "entity_id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "payload": {
        "recurring": false
      },
      "created_at": "2026-10-14T22:15:31.204118Z",
      "entity": {
        "content": "Comprar pan",
        "checked": true
      }
    }
  ]
}

Errores

  • 401 Falta la sesión o el token no es válido.

Webhooks

Un webhook avisa a tu servidor, con un POST, cada vez que algo cambia en Daylist. Siguen el modelo de los webhooks de Todoist: mismos nombres de evento, mismo cuerpo y la misma firma, así que una integración escrita para Todoist funciona casi sin cambios. La diferencia: en Todoist se registran por aplicación OAuth; en Daylist cada usuario registra los suyos en Configuración → Integraciones o por la API.

  • Cada webhook pertenece al espacio en que se creó (el activo o el de X-Workspace-Id) y solo recibe eventos de ese espacio, incluidos los cambios que hacen otros miembros del equipo.
  • Solo recibes eventos de objetos que puedes ver: un proyecto privado del que no eres participante no se filtra. Si pierdes el acceso, las entregas pendientes se cancelan antes de conectar.
  • Máximo 10 webhooks por usuario.
  • La URL debe ser https pública, sin usuario y contraseña ni fragmento (#…). Se rechazan las que resuelven a direcciones privadas, de loopback o link-local; se comprueba al guardar y otra vez al enviar. Solo fuera de producción se admite http o https a localhost y 127.0.0.1.

Eventos

EventoObjeto en event_data
item:added, item:updated, item:deleted, item:completed, item:uncompletedTarea (con assignee)
note:added, note:updated, note:deletedComentario de tarea (con task_id) o de proyecto (con project_id)
project:added, project:updated, project:deleted, project:archived, project:unarchivedProyecto (can_manage_access calculado para ti)
section:added, section:updated, section:deleted, section:archived, section:unarchivedSección
label:added, label:updated, label:deletedEtiqueta

Son 21 eventos. Además existe ping, que solo se envía con Enviar una prueba y no se puede elegir como suscripción. No hay reminder:fired porque Daylist no tiene recordatorios. Mover un proyecto a otro espacio envía project:updated a los webhooks del espacio de destino.

Cuerpo de la petición

Siempre con estas siete claves. Ejemplo de item:updated tras cambiar la prioridad y añadir una etiqueta:

{
  "event_name": "item:updated",
  "user_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
  "event_data": {
    "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
    "assignee_id": null,
    "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
    "section_id": null,
    "parent_id": null,
    "content": "Comprar pan",
    "description": "",
    "note_count": 0,
    "priority": 4,
    "due": {
      "date": "2026-10-15T09:00:00",
      "timezone": null,
      "string": "mañana a las 9",
      "lang": "es",
      "is_recurring": false,
      "recurrence": null
    },
    "deadline": null,
    "duration": null,
    "labels": [
      "casa"
    ],
    "child_order": 3,
    "day_order": 0,
    "is_collapsed": false,
    "checked": false,
    "completed_at": null,
    "is_deleted": false,
    "added_at": "2026-10-14T21:03:12.481Z",
    "updated_at": "2026-10-14T22:00:00.123Z",
    "assignee": null
  },
  "event_data_extra": {
    "old_item": {
      "id": "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95",
      "assignee_id": null,
      "project_id": "8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41",
      "section_id": null,
      "parent_id": null,
      "content": "Comprar pan",
      "description": "",
      "note_count": 0,
      "priority": 1,
      "due": {
        "date": "2026-10-15T09:00:00",
        "timezone": null,
        "string": "mañana a las 9",
        "lang": "es",
        "is_recurring": false,
        "recurrence": null
      },
      "deadline": null,
      "duration": null,
      "labels": [],
      "child_order": 3,
      "day_order": 0,
      "is_collapsed": false,
      "checked": false,
      "completed_at": null,
      "is_deleted": false,
      "added_at": "2026-10-14T21:03:12.481Z",
      "updated_at": "2026-10-14T21:03:12.481Z",
      "assignee": null
    },
    "update_intent": "item_updated"
  },
  "initiator": {
    "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "email": "tu@correo.com",
    "full_name": "Gerar Arévalo",
    "image_id": null,
    "is_premium": false
  },
  "triggered_at": "2026-10-14T22:00:00.123456Z",
  "version": "10"
}
  • user_id es el dueño del webhook; initiator, la persona que hizo el cambio.
  • event_data es el objeto completo, con el mismo formato que devuelve la API. En *:deleted es el objeto tal como estaba antes de borrarse.
  • event_data_extra.old_item solo aparece en los *:updated: el objeto antes del cambio.
  • event_data_extra.update_intent aparece en item:updated, item:completed e item:uncompleted, con item_updated, item_completed e item_uncompleted.
  • En los demás eventos event_data_extra es {}. En ping, event_data y event_data_extra son {}.
  • triggered_at está en UTC (ISO 8601, con microsegundos en los eventos y milisegundos en ping). version es siempre "10", como en Todoist.

Cabeceras y firma

CabeceraValor
Content-Typeapplication/json
User-AgentDaylist-Webhooks/1.0
X-Daylist-Hmac-SHA256Base64 de HMAC-SHA256 del cuerpo crudo. La clave es el secreto como texto UTF-8, tal como se mostró (no lo decodifiques de base64url).
X-Todoist-Hmac-SHA256El mismo valor, para integraciones escritas para Todoist.
X-Daylist-Delivery-ID, X-Todoist-Delivery-IDId de la entrega. Es el mismo en todos los intentos y al reenviarla: úsalo para descartar duplicados.

Comprueba siempre la firma sobre el cuerpo crudo, antes de interpretarlo como JSON:

Node
import crypto from "node:crypto";
import express from "express";

const app = express();
const vistos = new Set(); // en producción, guarda los ids en tu base de datos

// Necesitas el cuerpo crudo, tal como llegó, para calcular la firma.
app.post("/daylist", express.raw({ type: "application/json" }), (req, res) => {
  // La clave es el texto del secreto tal como se mostró (no lo decodifiques).
  const esperada = crypto
    .createHmac("sha256", process.env.DAYLIST_WEBHOOK_SECRET)
    .update(req.body)
    .digest("base64");
  const recibida = Buffer.from(req.get("X-Daylist-Hmac-SHA256") ?? "");
  const valida = recibida.length === Buffer.byteLength(esperada) &&
    crypto.timingSafeEqual(recibida, Buffer.from(esperada));
  if (!valida) return res.sendStatus(401);

  // La entrega es «al menos una vez»: descarta ids repetidos.
  const entrega = req.get("X-Daylist-Delivery-ID");
  if (vistos.has(entrega)) return res.sendStatus(200);
  vistos.add(entrega);

  const evento = JSON.parse(req.body);
  console.log(evento.event_name, evento.event_data.id);
  res.sendStatus(200); // responde en menos de 10 s; procesa lo pesado después
});
app.listen(3000);
Python
import base64
import hashlib
import hmac
import os

from flask import Flask, abort, request

app = Flask(__name__)
# La clave es el texto del secreto tal como se mostró (no lo decodifiques).
SECRETO = os.environ["DAYLIST_WEBHOOK_SECRET"].encode("utf-8")
vistos = set()  # en producción, guarda los ids en tu base de datos

@app.post("/daylist")
def daylist():
    cuerpo = request.get_data()  # cuerpo crudo
    esperada = base64.b64encode(hmac.new(SECRETO, cuerpo, hashlib.sha256).digest()).decode()
    if not hmac.compare_digest(esperada, request.headers.get("X-Daylist-Hmac-SHA256", "")):
        abort(401)

    # La entrega es «al menos una vez»: descarta ids repetidos.
    entrega = request.headers.get("X-Daylist-Delivery-ID")
    if entrega in vistos:
        return "", 200
    vistos.add(entrega)

    evento = request.get_json()
    print(evento["event_name"], evento["event_data"].get("id"))
    return "", 200  # responde en menos de 10 s; procesa lo pesado después

Entrega y reintentos

  • Una entrega tiene éxito si tu servidor responde 2xx en menos de 10 segundos. El cuerpo de tu respuesta se ignora y las redirecciones no se siguen (cuentan como fallo).
  • Las entregas son asíncronas: salen unos segundos después del cambio, y pueden llegar fuera de orden tras un reintento.
  • Si falla, se reintenta 5 veces: a los 1 min, 5 min, 30 min, 2 h y 6 h. Tras esos seis intentos la entrega queda como failed.
  • Cada entrega que agota sus intentos suma uno a failure_count; cualquier éxito lo pone a cero. Con 20 entregas agotadas seguidas el webhook se desactiva solo (active: false). Mientras está desactivado, sus entregas pendientes esperan; al reactivarlo (desde Integraciones o con PATCH) se reanudan y failure_count vuelve a cero.
  • La entrega es al menos una vez: en casos raros el mismo evento puede llegar dos veces con el mismo X-Daylist-Delivery-ID. Deduplica por ese id.

Gestionar webhooks por la API

Funcionan con sesión y con token personal, en el espacio activo o el de X-Workspace-Id. Solo ves y gestionas tus propios webhooks de ese espacio. El objeto webhook nunca trae el secreto, salvo al crearlo y al regenerarlo.

Listar webhooks

GET/api/v1/webhooks

curl
curl "https://daylist.gcoder.dev/api/v1/webhooks" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/webhooks", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
    "user_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "workspace_id": "f0a3c7e2-1b9d-4e6a-8c5f-3d2b7e9a1c64",
    "url": "https://ejemplo.com/daylist",
    "events": [
      "item:added",
      "item:completed"
    ],
    "active": true,
    "created_at": "2026-10-14T23:05:00.000Z",
    "last_status": 200,
    "last_delivery_at": "2026-10-14T23:06:10.701Z",
    "failure_count": 0
  }
]

Errores

  • 401 Falta la sesión o el token no es válido.

Crear un webhook

POST/api/v1/webhooks

Cuerpo (JSON)

CampoTipoDescripción
urlstringobligatorioURL https pública de tu servidor (hasta 2048 caracteres).
eventsstring[]obligatorioUno o más de los 21 eventos.
curl
curl -X POST "https://daylist.gcoder.dev/api/v1/webhooks" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://ejemplo.com/daylist", "events": ["item:added", "item:completed"]}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://ejemplo.com/daylist",
    events: [
      "item:added",
      "item:completed"
    ]
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/webhooks",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "url": "https://ejemplo.com/daylist",
        "events": [
            "item:added",
            "item:completed"
        ]
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 201

{
  "id": "d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
  "user_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
  "workspace_id": "f0a3c7e2-1b9d-4e6a-8c5f-3d2b7e9a1c64",
  "url": "https://ejemplo.com/daylist",
  "events": [
    "item:added",
    "item:completed"
  ],
  "active": true,
  "created_at": "2026-10-14T23:05:00.000Z",
  "last_status": null,
  "last_delivery_at": null,
  "failure_count": 0,
  "secret": "kX2vQ9mR4tL7nB1cF6hJ0dZ5aE8wY3sP2gT6uV9xK1o"
}

secret (43 caracteres) solo se muestra aquí y al regenerarlo. Guárdalo para verificar la firma.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 409 Puedes crear un máximo de 10 webhooks.
  • 422 Evento desconocido, URL que no es HTTPS pública, con credenciales o fragmento, o destino que no se pudo resolver.

Actualizar un webhook

PATCH/api/v1/webhooks/{id}

Activarlo (active: true) pone failure_count a cero.

Cuerpo (JSON)

CampoTipoDescripción
urlstringopcional
eventsstring[]opcional
activebooleanopcionalActivar o desactivar.
curl
curl -X PATCH "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47" \
  -H "Authorization: Bearer $DAYLIST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"active": false}'
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    active: false
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.patch(
    "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
    json={
        "active": False
    },
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
  "user_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
  "workspace_id": "f0a3c7e2-1b9d-4e6a-8c5f-3d2b7e9a1c64",
  "url": "https://ejemplo.com/daylist",
  "events": [
    "item:added",
    "item:completed"
  ],
  "active": false,
  "created_at": "2026-10-14T23:05:00.000Z",
  "last_status": null,
  "last_delivery_at": null,
  "failure_count": 0
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Webhook no encontrado.
  • 422 Algún campo no pasa la validación (el mensaje dice cuál).

Borrar un webhook

DELETE/api/v1/webhooks/{id}

Borra también su historial de entregas.

curl
curl -X DELETE "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
// 204: sin cuerpo
Python
import os
import requests

r = requests.delete(
    "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
# 204: sin cuerpo

Respuesta 204

Sin cuerpo.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Webhook no encontrado.

Regenerar el secreto

POST/api/v1/webhooks/{id}/rotate-secret

Devuelve el webhook con el nuevo secret; el anterior deja de valer en el acto.

curl
curl -X POST "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/rotate-secret" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/rotate-secret", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/rotate-secret",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
  "user_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
  "workspace_id": "f0a3c7e2-1b9d-4e6a-8c5f-3d2b7e9a1c64",
  "url": "https://ejemplo.com/daylist",
  "events": [
    "item:added",
    "item:completed"
  ],
  "active": true,
  "created_at": "2026-10-14T23:05:00.000Z",
  "last_status": null,
  "last_delivery_at": null,
  "failure_count": 0,
  "secret": "Tq7wE2rY5uI8oP1aS4dF6gH9jK3lZ0xC5vB8nM2qW4e"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Webhook no encontrado.

Enviar una prueba

POST/api/v1/webhooks/{id}/test

Envía en el momento un evento ping a la URL y devuelve la entrega con el resultado de ese primer intento. Si falla, la entrega queda en cola con sus reintentos normales.

curl
curl -X POST "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/test" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/test", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/test",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

{
  "id": "1c7e4b9a-3f2d-4a8e-b5c6-9d0e2f7a1b34",
  "webhook_id": "d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
  "event_name": "ping",
  "event_id": "8e3a5c1f-6b2d-4e9a-a7c4-2d1f0b8e6c59",
  "payload": {
    "event_name": "ping",
    "user_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
    "event_data": {},
    "event_data_extra": {},
    "initiator": {
      "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
      "email": "tu@correo.com",
      "full_name": "Gerar Arévalo",
      "image_id": null,
      "is_premium": false
    },
    "triggered_at": "2026-10-14T23:06:10.512Z",
    "version": "10"
  },
  "status": "delivered",
  "attempts": 1,
  "next_attempt_at": "2026-10-14T23:06:10.512Z",
  "response_status": 200,
  "response_ms": 184,
  "error": null,
  "created_at": "2026-10-14T23:06:10.512Z",
  "delivered_at": "2026-10-14T23:06:10.701Z"
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Webhook no encontrado.

Últimas entregas

GET/api/v1/webhooks/{id}/deliveries

Las últimas 100, de la más reciente a la más antigua. status es pending, delivered o failed; event_id identifica el evento e id el envío a este webhook (el valor de X-Daylist-Delivery-ID). error explica el último fallo: «Respuesta HTTP 503», «Tiempo de espera agotado (10 s)», «No se pudo conectar con el destino»…

curl
curl "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/deliveries" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/deliveries", {
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.get(
    "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/deliveries",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 200

[
  {
    "id": "5b8d2f1e-9a4c-4e7b-8d3a-6f0c1e9b2a47",
    "webhook_id": "d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
    "event_name": "item:completed",
    "event_id": "0f9c3e7a-2d5b-4a1e-9c8f-7b4d1a6e3c20",
    "payload": {
      "event_name": "item:completed",
      "…": "…"
    },
    "status": "pending",
    "attempts": 2,
    "next_attempt_at": "2026-10-14T23:12:41.000Z",
    "response_status": 503,
    "response_ms": 92,
    "error": "Respuesta HTTP 503",
    "created_at": "2026-10-14T23:06:40.000Z",
    "delivered_at": null
  },
  {
    "id": "1c7e4b9a-3f2d-4a8e-b5c6-9d0e2f7a1b34",
    "webhook_id": "d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
    "event_name": "ping",
    "event_id": "8e3a5c1f-6b2d-4e9a-a7c4-2d1f0b8e6c59",
    "payload": {
      "event_name": "ping",
      "user_id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
      "event_data": {},
      "event_data_extra": {},
      "initiator": {
        "id": "Yx3kP9vQ2mL7nR4tW8sB1cF6hJ0dZ5aE",
        "email": "tu@correo.com",
        "full_name": "Gerar Arévalo",
        "image_id": null,
        "is_premium": false
      },
      "triggered_at": "2026-10-14T23:06:10.512Z",
      "version": "10"
    },
    "status": "delivered",
    "attempts": 1,
    "next_attempt_at": "2026-10-14T23:06:10.512Z",
    "response_status": 200,
    "response_ms": 184,
    "error": null,
    "created_at": "2026-10-14T23:06:10.512Z",
    "delivered_at": "2026-10-14T23:06:10.701Z"
  }
]

En el ejemplo, el payload de la primera entrega está recortado; la API devuelve el cuerpo completo que se envía.

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Webhook no encontrado.

Reenviar una entrega

POST/api/v1/webhooks/{id}/deliveries/{deliveryId}/redeliver

Vuelve a poner la entrega en cola con el mismo id y el mismo cuerpo, y los intentos a cero. El envío es asíncrono.

curl
curl -X POST "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/deliveries/5b8d2f1e-9a4c-4e7b-8d3a-6f0c1e9b2a47/redeliver" \
  -H "Authorization: Bearer $DAYLIST_TOKEN"
JavaScript
const res = await fetch("https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/deliveries/5b8d2f1e-9a4c-4e7b-8d3a-6f0c1e9b2a47/redeliver", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DAYLIST_TOKEN}`,
  },
});
if (!res.ok) throw new Error((await res.json()).error);
const datos = await res.json();
Python
import os
import requests

r = requests.post(
    "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/deliveries/5b8d2f1e-9a4c-4e7b-8d3a-6f0c1e9b2a47/redeliver",
    headers={"Authorization": f"Bearer {os.environ['DAYLIST_TOKEN']}"},
)
r.raise_for_status()
datos = r.json()

Respuesta 202

{
  "id": "5b8d2f1e-9a4c-4e7b-8d3a-6f0c1e9b2a47",
  "webhook_id": "d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47",
  "event_name": "item:completed",
  "event_id": "0f9c3e7a-2d5b-4a1e-9c8f-7b4d1a6e3c20",
  "payload": {
    "event_name": "item:completed",
    "…": "…"
  },
  "status": "pending",
  "attempts": 0,
  "next_attempt_at": "2026-10-14T23:20:00.000Z",
  "response_status": null,
  "response_ms": null,
  "error": null,
  "created_at": "2026-10-14T23:06:40.000Z",
  "delivered_at": null
}

Errores

  • 401 Falta la sesión o el token no es válido.
  • 404 Webhook o entrega no encontrados.
  • 409 Activa el webhook antes de reenviar.

Usar los SDK de Todoist

Doist publica clientes oficiales para su API en github.com/Doist. Como Daylist imita el formato de Todoist, es natural preguntarse si sirven. Respuesta corta: se pueden apuntar a Daylist, pero hoy solo funcionan las operaciones que no leen el objeto de vuelta (completar, reabrir y borrar). Para todo lo demás, llama a la API directamente con los ejemplos de esta página.

Lo que sigue sale de leer el código de cada SDK (versiones indicadas, octubre de 2026), no de una certificación de Doist. No es un uso que Doist soporte.

Cliente¿URL configurable?Cómo
@doist/todoist-sdk 15.3 (antes todoist-api-typescript)Sínew TodoistApi(token, { baseUrl: "https://daylist.gcoder.dev" }). Solo el origen: el SDK añade /api/v1/.
todoist-api-python 4.0NoLa URL está fija en _core/endpoints.py. Solo con el truco de abajo, que no es una opción pública.
@doist/todoist-mcp 13.4 (antes todoist-ai)SíVariables TODOIST_API_KEY y TODOIST_BASE_URL (solo el origen).
@doist/todoist-cli 5.5NoLa URL está fija en el código.

Qué funciona y qué no

OperaciónEstadoPor qué
Completar, reabrir y borrar tareas; borrar proyectos, secciones, etiquetas y comentariosFuncionaEl SDK solo mira que la respuesta sea 2xx.
Crear, obtener o añadido rápido de tareas y otros objetosFallaLa petición llega y el objeto se crea, pero el SDK valida la respuesta y exige campos que Daylist no devuelve (user_id, added_by_uid, responsible_uid, deadline.lang…), así que lanza un error.
Listar (getTasks, get_projects…)FallaEl SDK espera {results, next_cursor}; Daylist devuelve un arreglo.
Actualizar (updateTask, update_project…)FallaEl SDK usa POST /tasks/{id}; Daylist usa PATCH y responde 404.
Filtros, completadas, mover tareas, sync()No existeUsan /tasks/filter, /tasks/completed/*, /tasks/{id}/move y la Sync API (/sync), que Daylist no tiene.
Servidor MCPNo usableArranca, pero casi todas sus herramientas llaman a /user, /sync, /tasks/filter o /activities, que no existen en Daylist.

TypeScript

TypeScript
import { TodoistApi } from "@doist/todoist-sdk"; // v15.3

// Solo el origen: el SDK añade /api/v1/ por su cuenta,
// así que las peticiones van a https://daylist.gcoder.dev/api/v1/…
const api = new TodoistApi(process.env.DAYLIST_TOKEN, {
  baseUrl: "https://daylist.gcoder.dev",
});

const id = "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95";
await api.closeTask(id);   // true  → POST /api/v1/tasks/{id}/close
await api.reopenTask(id);  // true  → POST /api/v1/tasks/{id}/reopen
await api.deleteTask(id);  // true  → DELETE /api/v1/tasks/{id}

Python

Python
# todoist-api-python 4.0 no tiene opción para cambiar la URL base.
# Este truco reemplaza una constante de un módulo privado: puede dejar
# de funcionar en cualquier actualización del SDK.
import os
import todoist_api_python._core.endpoints as endpoints
endpoints.API_URL = "https://daylist.gcoder.dev/api/v1"

from todoist_api_python.api import TodoistAPI

api = TodoistAPI(os.environ["DAYLIST_TOKEN"])
tarea = "5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95"
api.complete_task(tarea)    # True  → POST /api/v1/tasks/{id}/close
api.uncomplete_task(tarea)  # True  → POST /api/v1/tasks/{id}/reopen
api.delete_task(tarea)      # True  → DELETE /api/v1/tasks/{id}

Servidor MCP de Todoist

La configuración es posible, pero recuerda la tabla: hoy la mayoría de sus herramientas fallan contra Daylist.

{
  "mcpServers": {
    "daylist": {
      "command": "npx",
      "args": ["-y", "@doist/todoist-mcp"],
      "env": {
        "TODOIST_API_KEY": "dl_pat_…",
        "TODOIST_BASE_URL": "https://daylist.gcoder.dev"
      }
    }
  }
}

Límites de uso

LímiteValor
Peticiones a /api/v1 por usuario300 por minuto (minuto de reloj), sumando sesión y tokens.
Peticiones por token personal300 por minuto (ventana de 60 s).
Aceptar invitaciones30 por minuto por usuario y 100 por minuto por IP.
Vista previa de invitaciones100 por minuto por IP.
Tareas por cambio en lote100 (/tasks/drag).
Webhooks por usuario10 webhooks por usuario.
TextoComentarios hasta 20 000 caracteres; nombres de proyecto, sección y equipo hasta 120; etiquetas hasta 60; tokens hasta 32.
Paginaciónlimit de 1 a 100 en actividad y de 1 a 200 en reportes; days de 1 a 730 en Próximo.

Al pasarte de un límite la API responde 429 con la cabecera Retry-After en segundos. Espera ese tiempo antes de reintentar y, si haces muchas llamadas, añade una espera creciente entre reintentos.

HTTP/1.1 429 Too Many Requests
Retry-After: 60

{
  "error": "Demasiadas peticiones; vuelve a intentarlo en un minuto"
}

Cambios

    • Publicada esta documentación.
    • Equipos: /workspaces, roles, enlaces de invitación y la cabecera X-Workspace-Id para tokens.
    • Proyectos privados (visibility, /projects/{id}/members) y mover proyectos entre espacios.
    • Asignación de tareas: assignee_id, assignee, /projects/{id}/assignees y /views/assigned.
    • Actividad del espacio y del proyecto (/activity), con el nombre actual de la tarea o el proyecto en entity.
    • Webhooks compatibles con Todoist (ver Webhooks).
    • Cuentas reales con Google y GitHub, y tokens personales dl_pat_.
    • Exportación completa (/export), reportes y preferencias aislados por usuario y espacio.