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 base | https://daylist.gcoder.dev/api/v1 |
| Formato | JSON en UTF-8, campos en snake_case |
| Autenticación | Token personal Authorization: Bearer dl_pat_… o sesión de navegador |
| Versión | v1 |
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 -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}'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();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, objetoduecondate,string,langeis_recurring, y rutas como/tasks/quick,/tasks/{id}/closey/tasks/{id}/reopen. - Distinto: los listados devuelven un arreglo JSON directo, sin
resultsninext_cursor; para actualizar se usaPATCH(Todoist usaPOST); los ids son UUID; el responsable se llamaassignee_id;deadlinetrae solodate. - 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 conGET /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-Idy no coincide con el activo, responde 409 «El espacio activo cambió; actualiza la vista». - La cabecera se ignora en
/me,/workspacesy/workspace-invites.
curl "https://daylist.gcoder.dev/api/v1/projects" \
-H "Authorization: Bearer $DAYLIST_TOKEN" \
-H "X-Workspace-Id: 9c4e2a7f-6d1b-4a3e-b8f5-0e7c2d9a4b18"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();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 "https://daylist.gcoder.dev/api/v1/me" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | obligatorio | Nombre 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | obligatorio | De 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
email | string | obligatorio | Tu 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:
PATCHcambia solo los campos que envías. Para vaciar un campo que admitenull, envíanull. - 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-DDoYYYY-MM-DDTHH:mm:sssin zona: «hoy» se calcula en la zona del servidor,America/El_Salvador. - Borrados:
DELETEresponde 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ódigo | Cuándo |
|---|---|
| 400 | El cuerpo no es JSON válido. |
| 401 | Falta la sesión, la sesión caducó o el token no es válido. |
| 403 | No tienes permiso: la ruta requiere sesión, tu rol en el equipo no alcanza, o la escritura con cookie llega desde otro origen. |
| 404 | El 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. |
| 409 | Conflicto con el estado actual: nombre repetido, la Bandeja de entrada no se borra, el espacio activo cambió, ya eres miembro… |
| 410 | Enlace de invitación caducado o revocado. |
| 413 | Cuerpo mayor de 1 KiB en las rutas de invitación. |
| 422 | Validació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. |
| 429 | Demasiadas peticiones. Respeta la cabecera Retry-After (segundos). Ver Límites de uso. |
| 500 | Error 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
}| Campo | Descripción |
|---|---|
priority | 1 normal (P4) · 2 (P3) · 3 (P2) · 4 urgente (P1). |
due | Fecha 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_count | Número de comentarios. |
assignee / assignee_id | Responsable de la tarea: id y un resumen con id, name e image. |
checked / completed_at | Si 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
| Campo | Tipo | Descripción | |
|---|---|---|---|
project_id | string | opcional | Solo las de ese proyecto. |
section_id | string | opcional | Solo las de esa sección. |
parent_id | string | opcional | Solo las subtareas directas de esa tarea. |
label | string | opcional | Solo las que tienen esa etiqueta (nombre exacto). |
assignee_id | string | opcional | Solo las asignadas a ese usuario; me significa tú. |
checked | "true" | opcional | true devuelve solo las completadas en lugar de las pendientes. |
curl "https://daylist.gcoder.dev/api/v1/tasks?project_id=8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41&label=casa" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
content | string | obligatorio | Título de la tarea. No puede quedar vacío. |
description | string | opcional | Notas de la tarea, en Markdown. |
project_id | string | null | opcional | Proyecto. Si falta o es null, la tarea va a la Bandeja de entrada. |
section_id | string | null | opcional | Sección; debe pertenecer al proyecto. |
parent_id | string | null | opcional | Tarea padre. La subtarea hereda el proyecto y la sección del padre. |
priority | entero 1–4 | opcional | 4 es la más urgente (P1 en la app) y 1 la normal (P4). Igual que en Todoist. |
due_string | string | null | opcional | Fecha 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_date | string | null | opcional | Fecha exacta YYYY-MM-DD o con hora YYYY-MM-DDTHH:mm[:ss] (hora local, sin zona). |
due | objeto | null | opcional | Objeto due completo, tal como lo devuelve la API. Útil para copiar la fecha de otra tarea. |
deadline_date | string | null | opcional | Fecha límite YYYY-MM-DD, independiente de due. |
duration | objeto | null | opcional | {"amount": 30, "unit": "minute"}; unit es minute o day. |
labels | string[] | opcional | Nombres de etiqueta. Las que no existan se crean. |
assignee_id | string | null | opcional | Responsable (id de usuario). Debe tener acceso al proyecto; en el espacio personal solo puedes ser tú. |
child_order | entero | opcional | Posición entre sus hermanas. Por defecto, al final. |
day_order | entero | opcional | Posición dentro del día en Hoy y Próximo. |
is_collapsed | boolean | opcional | Si sus subtareas se muestran plegadas. |
insert_before / insert_after | string | opcional | Id de otra tarea junto a la que se coloca. Usa solo uno de los dos. |
order_context | "project" | "day" | opcional | Con insert_before/insert_after: ordenar en el proyecto (por defecto) o en el día. |
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"]}'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();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);
#Proyectoy/Sección;@etiqueta(también%etiqueta);p1–p4para la prioridad;{viernes}entre llaves para la fecha límite;for 30mofor 1h 30mpara 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
text | string | obligatorio | La frase completa. |
project_id | string | null | opcional | Proyecto por defecto si la frase no trae #Proyecto. |
section_id | string | null | opcional | Sección por defecto. |
parent_id | string | null | opcional | Crear como subtarea de esta tarea. |
insert_before / insert_after / order_context | opcional | Igual que al crear una tarea. |
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"}'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();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 -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}'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();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,
contentvací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
| Campo | Tipo | Descripción | |
|---|---|---|---|
forever | "true" | opcional | En una recurrente, cerrarla para siempre en lugar de reprogramarla. |
curl -X POST "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/close" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
completion_id | string | obligatorio | El completion_id que devolvió close. |
previous_due | objeto | obligatorio | El previous_due que devolvió close. |
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"}}}'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();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 -X POST "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95/reopen" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 -X DELETE "https://daylist.gcoder.dev/api/v1/tasks/5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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 cuerpoimport 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 cuerpoRespuesta 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
task_ids | string[] | obligatorio | De 1 a 100 ids, sin repetir. |
action | objeto | obligatorio | Una 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 -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}}'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();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.
| Escribe | Resultado (si hoy es martes 13 de octubre, 10:15) |
|---|---|
hoy, mañana, pasado mañana | 13, 14 y 15 de octubre |
lunes, vie, próximo lunes | el siguiente lunes (19), viernes (16)… |
próxima semana, fin de semana, fin de mes | lunes 19, sábado 17, sábado 31 |
15 oct, 15 de octubre, 15/10, 2026-10-15 | 15 de octubre (día/mes por defecto) |
el 27 | 27 de este mes, o del siguiente si ya pasó |
en 3 días, en 2 semanas, en 2 horas | 16 de octubre, 27 de octubre, hoy a las 12:15 |
a las 9, 9pm, 21:00 | la próxima vez que sean esa hora |
mañana a las 9, viernes 7pm, mañana en la mañana | fecha con hora (mañana 9:00, tarde 12:00, noche 19:00) |
algún día | dentro de dos meses |
cada día, todos los días laborables | recurrente diaria (laborables: lunes a viernes) |
cada lunes y viernes, cada 2 semanas | recurrente semanal |
cada 27, cada último día, cada 15 de enero | mensual y anual |
cada lunes a las 9, cada mañana, cada hora | recurrente con hora |
cada! 3 meses | con ! la siguiente fecha cuenta desde que la completas, no desde la fecha prevista |
tomorrow at 9pm, every monday, next week | lo 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 app | priority | Añadido rápido |
|---|---|---|
| P1 (roja) | 4 | p1 |
| P2 (naranja) | 3 | p2 |
| P3 (azul) | 2 | p3 |
| P4 (sin color) | 1 | p4 |
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 "https://daylist.gcoder.dev/api/v1/projects" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | obligatorio | De 1 a 120 caracteres. |
description | string | opcional | Descripción del proyecto. |
color | string | opcional | Nombre de color al estilo Todoist (charcoal, blue, berry_red…). Por defecto charcoal. |
parent_id | string | null | opcional | Proyecto padre. |
child_order | entero | opcional | Posición entre sus hermanos. Por defecto, al final. |
is_favorite | boolean | opcional | Mostrar en Favoritos. |
view_style | "list" | "board" | "calendar" | opcional | Vista del proyecto. Por defecto list. |
visibility | "team" | "private" | opcional | Solo en equipos: private lo restringe a sus participantes. Un privado debe ser raíz y sin subproyectos. |
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}'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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
is_archived | boolean | opcional | Archivar o desarchivar (con sus subproyectos). |
is_collapsed | boolean | opcional | Plegar sus subproyectos en la barra lateral. |
… | opcional | Cualquier campo de crear. |
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}'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();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 -X DELETE "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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 cuerpoimport 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 cuerpoRespuesta 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
project_id | string | obligatorio | Proyecto que se mueve. |
target_id | string | obligatorio | Proyecto de referencia. |
placement | "before" | "after" | "inside" | obligatorio | Antes, después o dentro (como subproyecto) del de referencia. |
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"}'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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
workspace_id | string | obligatorio | Espacio de destino. |
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"}'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();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 "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/members" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
user_ids | string[] | obligatorio | Ids de usuario (máximo 500); todos deben ser miembros del equipo. |
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"]}'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();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 "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/assignees" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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
| Campo | Tipo | Descripción | |
|---|---|---|---|
project_id | string | opcional | Solo las de ese proyecto. |
curl "https://daylist.gcoder.dev/api/v1/sections?project_id=8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
project_id | string | obligatorio | Proyecto. |
name | string | obligatorio | De 1 a 120 caracteres. |
description | string | null | opcional | Descripción. |
section_order | entero | opcional | Posición. Por defecto, al final. |
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"}'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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | opcional | |
description | string | null | opcional | |
project_id | string | opcional | Mover a otro proyecto. |
section_order | entero | opcional | |
is_collapsed | boolean | opcional | |
is_archived | boolean | opcional | Archivar o desarchivar. |
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"}'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();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 -X DELETE "https://daylist.gcoder.dev/api/v1/sections/c47a2e91-0f3d-4b6c-8e25-9d1b7a4f6e03" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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 cuerpoimport 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 cuerpoRespuesta 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
section_id | string | obligatorio | |
target_id | string | obligatorio | Sección de referencia. |
placement | "before" | "after" | obligatorio |
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"}'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();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 "https://daylist.gcoder.dev/api/v1/labels" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | obligatorio | De 1 a 60 caracteres, sin @, # ni %. Única en el espacio, sin distinguir mayúsculas. |
color | string | opcional | Por defecto charcoal. |
item_order | entero | opcional | Posición. |
is_favorite | boolean | opcional |
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"}'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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | opcional | De 1 a 60 caracteres, sin @, # ni %. Única en el espacio, sin distinguir mayúsculas. |
color | string | opcional | Por defecto charcoal. |
item_order | entero | opcional | Posición. |
is_favorite | boolean | opcional |
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"}'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();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 -X DELETE "https://daylist.gcoder.dev/api/v1/labels/e3c9a7b1-5d2f-4a6e-8b0c-4f1d9e7a2b56" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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 cuerpoimport 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 cuerpoRespuesta 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
| Campo | Tipo | Descripción | |
|---|---|---|---|
task_id | string | obligatorio | La tarea. |
curl "https://daylist.gcoder.dev/api/v1/comments?task_id=5f2b8c1d-7e4a-4c3b-a9d6-1e0f7b2c8a95" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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_ides obligatorio.
Comentar una tarea
POST/api/v1/comments
Cuerpo (JSON)
| Campo | Tipo | Descripción | |
|---|---|---|---|
task_id | string | obligatorio | |
content | string | obligatorio | Texto del comentario. |
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."}'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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
content | string | obligatorio |
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."}'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();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 -X DELETE "https://daylist.gcoder.dev/api/v1/comments/7b1e9d3a-4c6f-4e2b-9a85-0d3f6c1e8b27" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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 cuerpoimport 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 cuerpoRespuesta 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 "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comments" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
content | string | obligatorio |
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."}'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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
content | string | obligatorio |
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."}'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();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 -X DELETE "https://daylist.gcoder.dev/api/v1/project-comments/2d8f5a1c-9e3b-4f7d-a6c2-8b0e4d1f7a39" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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 cuerpoimport 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 cuerpoRespuesta 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 "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/comment-activity" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/views/inbox" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/views/today" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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
| Campo | Tipo | Descripción | |
|---|---|---|---|
days | entero | opcional | Cuántos días, de 1 a 730. Por defecto 14. |
curl "https://daylist.gcoder.dev/api/v1/views/upcoming?days=7" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/views/assigned" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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
| Campo | Tipo | Descripción | |
|---|---|---|---|
q | string | obligatorio | Texto a buscar. Vacío devuelve listas vacías. |
curl "https://daylist.gcoder.dev/api/v1/search?q=pan" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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
| Campo | Tipo | Descripción | |
|---|---|---|---|
from | YYYY-MM-DD | opcional | Desde ese día (incluido), en la zona del servidor. |
to | YYYY-MM-DD | opcional | Hasta ese día (incluido). |
project_id | string | opcional | Solo de ese proyecto. |
limit | entero | opcional | De 1 a 200. Por defecto 100. |
before | string | opcional | Cursor completed_at|id. |
curl "https://daylist.gcoder.dev/api/v1/reports/completed?from=2026-10-01&to=2026-10-14&limit=50" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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,
fromposterior ato,limitfuera 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 "https://daylist.gcoder.dev/api/v1/reports/added?from=2026-10-01" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/stats/today" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/preferences" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
dailyGoal / weeklyGoal | entero 1–10000 | opcional | Metas de tareas completadas. |
weekStart | 0 | 1 | opcional | La semana empieza en domingo (0) o lunes (1). |
daysOff | entero[] | opcional | Días de descanso, 0 = domingo … 6 = sábado. |
vacation | boolean | opcional | Modo vacaciones. |
startView | string | opcional | /inbox, /today, /upcoming, /labels o /reports. |
timeFormat / dateFormat | string | opcional | 12h/24h; DMY/MDY/YMD. |
… | opcional | Cualquier otro campo de la respuesta de leer preferencias. |
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]}'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();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
namecon 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 "https://daylist.gcoder.dev/api/v1/workspaces" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | obligatorio | De 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | obligatorio |
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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
name | string | obligatorio | El 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)
| Campo | Tipo | Descripció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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
user_id | string | obligatorio | Miembro 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
token | string | obligatorio | Los 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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
token | string | obligatorio | Los 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.
| type | Cuándo |
|---|---|
task.created, task.edited, task.moved, task.assigned | Alta y cambios de una tarea. task.edited trae en payload.fields los campos nuevos. |
task.completed, task.reopened, task.deleted | Completar, reabrir y borrar. |
project.created, project.moved, project.archived | Proyectos. |
comment.added, comment.edited, comment.deleted | Comentarios de tarea y de proyecto. |
member.joined, member.left, member.removed, member.role_changed | Miembros 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
| Campo | Tipo | Descripción | |
|---|---|---|---|
project_id | string | opcional | Solo la de ese proyecto. |
limit | entero | opcional | De 1 a 100. Por defecto 50. |
before | string | opcional | El next_cursor de la página anterior. |
curl "https://daylist.gcoder.dev/api/v1/activity?limit=20" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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
limitfuera 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 "https://daylist.gcoder.dev/api/v1/projects/8d3f1c2e-6a4b-4e9d-b7c1-2f5a9e0d3c41/activity" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/export" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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
httpspú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 admitehttpohttpsalocalhosty127.0.0.1.
Eventos
| Evento | Objeto en event_data |
|---|---|
item:added, item:updated, item:deleted, item:completed, item:uncompleted | Tarea (con assignee) |
note:added, note:updated, note:deleted | Comentario de tarea (con task_id) o de proyecto (con project_id) |
project:added, project:updated, project:deleted, project:archived, project:unarchived | Proyecto (can_manage_access calculado para ti) |
section:added, section:updated, section:deleted, section:archived, section:unarchived | Sección |
label:added, label:updated, label:deleted | Etiqueta |
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_ides el dueño del webhook;initiator, la persona que hizo el cambio.event_dataes el objeto completo, con el mismo formato que devuelve la API. En*:deletedes el objeto tal como estaba antes de borrarse.event_data_extra.old_itemsolo aparece en los*:updated: el objeto antes del cambio.event_data_extra.update_intentaparece enitem:updated,item:completedeitem:uncompleted, conitem_updated,item_completedeitem_uncompleted.- En los demás eventos
event_data_extraes{}. Enping,event_datayevent_data_extrason{}. triggered_atestá en UTC (ISO 8601, con microsegundos en los eventos y milisegundos enping).versiones siempre"10", como en Todoist.
Cabeceras y firma
| Cabecera | Valor |
|---|---|
Content-Type | application/json |
User-Agent | Daylist-Webhooks/1.0 |
X-Daylist-Hmac-SHA256 | Base64 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-SHA256 | El mismo valor, para integraciones escritas para Todoist. |
X-Daylist-Delivery-ID, X-Todoist-Delivery-ID | Id 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:
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);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ésEntrega 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 conPATCH) se reanudan yfailure_countvuelve 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 "https://daylist.gcoder.dev/api/v1/webhooks" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
url | string | obligatorio | URL https pública de tu servidor (hasta 2048 caracteres). |
events | string[] | obligatorio | Uno o más de los 21 eventos. |
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"]}'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();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)
| Campo | Tipo | Descripción | |
|---|---|---|---|
url | string | opcional | |
events | string[] | opcional | |
active | boolean | opcional | Activar o desactivar. |
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}'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();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 -X DELETE "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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 cuerpoimport 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 cuerpoRespuesta 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 -X POST "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/rotate-secret" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 -X POST "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/test" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 "https://daylist.gcoder.dev/api/v1/webhooks/d8b2f6a1-4c9e-4a7d-b3e5-1f0c8a6d2e47/deliveries" \
-H "Authorization: Bearer $DAYLIST_TOKEN"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();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 -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"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();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.0 | No | La 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.5 | No | La URL está fija en el código. |
Qué funciona y qué no
| Operación | Estado | Por qué |
|---|---|---|
| Completar, reabrir y borrar tareas; borrar proyectos, secciones, etiquetas y comentarios | Funciona | El SDK solo mira que la respuesta sea 2xx. |
| Crear, obtener o añadido rápido de tareas y otros objetos | Falla | La 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…) | Falla | El SDK espera {results, next_cursor}; Daylist devuelve un arreglo. |
Actualizar (updateTask, update_project…) | Falla | El SDK usa POST /tasks/{id}; Daylist usa PATCH y responde 404. |
Filtros, completadas, mover tareas, sync() | No existe | Usan /tasks/filter, /tasks/completed/*, /tasks/{id}/move y la Sync API (/sync), que Daylist no tiene. |
| Servidor MCP | No usable | Arranca, pero casi todas sus herramientas llaman a /user, /sync, /tasks/filter o /activities, que no existen en Daylist. |
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
# 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ímite | Valor |
|---|---|
Peticiones a /api/v1 por usuario | 300 por minuto (minuto de reloj), sumando sesión y tokens. |
| Peticiones por token personal | 300 por minuto (ventana de 60 s). |
| Aceptar invitaciones | 30 por minuto por usuario y 100 por minuto por IP. |
| Vista previa de invitaciones | 100 por minuto por IP. |
| Tareas por cambio en lote | 100 (/tasks/drag). |
| Webhooks por usuario | 10 webhooks por usuario. |
| Texto | Comentarios hasta 20 000 caracteres; nombres de proyecto, sección y equipo hasta 120; etiquetas hasta 60; tokens hasta 32. |
| Paginación | limit 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 cabeceraX-Workspace-Idpara tokens. - Proyectos privados (
visibility,/projects/{id}/members) y mover proyectos entre espacios. - Asignación de tareas:
assignee_id,assignee,/projects/{id}/assigneesy/views/assigned. - Actividad del espacio y del proyecto (
/activity), con el nombre actual de la tarea o el proyecto enentity. - 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.
- Cuentas reales con Google y GitHub, y tokens personales