La API de tokidu
Lee tus proyectos y tareas, crea tareas desde un formulario o un script y ciérralas desde otra app. La API pasa por la misma puerta que la web: un token nunca hace nada que no pueda hacer su cuenta.
Es del plan Max: la puede usar quien tiene Max y quien trabaja en un proyecto que comparte alguien con Max, y solo llega a esos proyectos.
- Dirección base
https://tokidu.com/api/v1- Cabecera
Authorization: Bearer tkd_…- Límites
- 1000 peticiones por hora y cuenta · 60 por minuto y token
- Referencia completa
- /api/v1/openapi.json
Empezar
- En Cuenta → Integraciones, crea un token y marca «Ver tus proyectos y tareas» (y «Crear y cambiar tareas», si va a escribir).
- Cópialo, porque solo se enseña una vez, y guárdalo como una contraseña. En los ejemplos va en la variable
TOKIDU_TOKEN. - Pruébalo pidiendo tu cuenta:
curl https://tokidu.com/api/v1/me \
-H "Authorization: Bearer $TOKIDU_TOKEN"Llámala desde un servidor o un script, no desde una página web: la API no admite CORS y no mira las cookies.
Tokens y ámbitos
Al crear un token eliges qué puede hacer, a qué proyectos llega y cuándo caduca:
read, «Ver tus proyectos y tareas»: leer los proyectos, las tareas y «Lo siguiente».write, «Crear y cambiar tareas»: crear proyectos y tareas, cambiarlas, terminarlas, reabrirlas y borrarlas, como en la web.- Proyectos: todos los que ves o solo los que marques. Si dejas de ver uno, el token deja de llegar a él.
- Caducidad: a los 30, 90 o 365 días. Si no eliges, al año.
El token de Madrugo (integrations:read) solo lee «Lo siguiente» para Madrugo, de todos los proyectos que ves: no abre esta API y no se puede limitar a unos proyectos.
Puedes tener 10 tokens activos. Revócalos de uno en uno o todos a la vez, y mira en cada uno sus últimos cambios: guardamos qué cambios se hicieron con cada token, sin el contenido, durante 90 días.
Límites
- 1000 peticiones por hora para toda tu cuenta y 60 por minuto para cada token.
- Cada respuesta lleva
RateLimit-Limit,RateLimit-RemainingyRateLimit-Reset: el límite, lo que queda y cuándo se renueva. - Si te pasas, la respuesta es 429
demasiadas_peticionesconRetry-After: espera esos segundos y vuelve a probar. - El cuerpo de una petición puede ocupar 64 KiB como mucho.
Crear tareas sin duplicarlas
Crear una tarea exige la cabecera Idempotency-Key: de 1 a 100 caracteres, distinta para cada tarea que quieres crear (por ejemplo, el id de la fila, del correo o del formulario de donde sale).
Si repites la petición con la misma clave, por ejemplo tras un corte de red, no se crea otra: te devuelve la que ya existe, con 200 e Idempotent-Replayed: true.
Si la tarea de esa clave se borró, no vuelve: la respuesta es 410 tarea_borrada. La misma clave en otro proyecto crea otra tarea.
Errores
Un error responde con su código HTTP y el campo error del cuerpo; con 422, también field, el campo que no vale.
- 401
token_invalido - Falta el token, no existe, está revocado o ha caducado (no se dice cuál).
- 403
ambito - Al token le falta el ámbito que pide la ruta.
- 403
proyecto_fuera_del_token - El token está limitado a otros proyectos.
- 403
max_necesario - Es del plan Max y quien comparte el proyecto no lo tiene (o tú, al crear un proyecto).
- 403
solo_lectura - En ese proyecto solo puedes ver, no cambiar.
- 403
en_pausa - El proyecto está en pausa: quien lo comparte ya no tiene el plan Equipo, así que solo se puede ver.
- 404
proyecto_inexistente - El proyecto no existe o no lo ves.
- 404
tarea_inexistente - La tarea no existe o no la ves.
- 404
ruta_inexistente - Esa ruta no es de la API: revisa el método y la dirección.
- 404
webhook_inexistente - Ese aviso no existe o no lo creó este token.
- 409
id_ocupado - Esa
Idempotency-Keycorresponde a una tarea que no ves: usa otra. - 409
horas_de_otros - No se puede borrar porque tiene horas de otras personas, como en la web.
- 409
adjuntos_de_otros - No se puede borrar porque tiene archivos de otras personas, como en la web.
- 409
entrada_facturada - No se puede borrar porque tiene horas ya facturadas, como en la web.
- 409
ciclo - Esa espera cerraría un círculo: una tarea acabaría esperándose a sí misma.
- 409
webhooks_completo - Ya tienes 20 avisos: borra alguno para crear otro.
- 410
tarea_borrada - Esa
Idempotency-Keyes de una tarea que se borró: no se vuelve a crear. Para crear otra, usa otra clave. - 413
cuerpo_grande - El cuerpo pasa de 64 KiB.
- 415
json_necesario - El cuerpo tiene que ir en JSON, con
Content-Type: application/json. - 422
peticion_invalida - Un campo no vale:
fielddice cuál. - 422
url_no_valida - La dirección de un aviso no vale:
reasondice por qué (https,puerto,usuario,local,tokidu,dnsoip_privada). - 428
idempotencia_necesaria - Para crear una tarea falta la cabecera
Idempotency-Key. - 429
demasiadas_peticiones - Te has pasado del límite: espera lo que diga
Retry-After. - 429
demasiados_intentos - Has comprobado muchas direcciones de avisos seguidas: espera lo que diga
Retry-After. - 503
ocupado - El proyecto estaba ocupado con otro cambio: repite la petición pasado lo que diga
Retry-After. - 503
no_disponible - El servicio no ha podido responder ahora: repite la petición pasado lo que diga
Retry-After.
Referencia
Todas las rutas van detrás de la dirección base y responden en JSON. Los ids de proyectos y tareas son los que devuelve la propia API.
GET /api/v1/me
Tu cuenta, el token (nombre, ámbitos, proyectos y caducidad) y lo que te queda de la hora.
Ámbito: cualquiera
GET /api/v1/projects
Los proyectos que ves de quien tiene Max: nombre, color, quién lo comparte, tu papel, semáforo, fin previsto, foto del plan y avance.
Ámbito: read
GET /api/v1/projects/{id}
Un proyecto, con sus sprints.
Ámbito: read
GET /api/v1/projects/{id}/tasks
Las tareas de un proyecto, sin notas: a qué esperan, si están bloqueadas y cuándo se prevé que acaben. Filtra con status (open, done o all) y updatedSince, y pasa de página con cursor.
Ámbito: read
curl "https://tokidu.com/api/v1/projects/$PROJECT_ID/tasks?status=open" \
-H "Authorization: Bearer $TOKIDU_TOKEN"GET /api/v1/tasks/{id}
Una tarea con sus notas, a qué espera y «¿Por qué esa fecha?».
Ámbito: read
GET /api/v1/next
«Lo siguiente»: lo tuyo que ya se puede empezar.
Ámbito: read
POST /api/v1/projects
Crea un proyecto: name y, si quieres, color. Solo si tienes Max.
Ámbito: write
POST /api/v1/projects/{id}/tasks
Crea una tarea: title y, si quieres, notes, priority, due, assignee, sprintId, estimateMin y after. Exige Idempotency-Key.
Ámbito: write
curl https://tokidu.com/api/v1/projects/$PROJECT_ID/tasks \
-H "Authorization: Bearer $TOKIDU_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: form-42" \
-d '{"title":"Llamar a Ana","due":"2026-10-20"}'PATCH /api/v1/tasks/{id}
Cambia solo los campos que mandes. Responde con la tarea, las que se desbloquean y, si otra persona cambió lo mismo a la vez, lo que se quedó.
Ámbito: write
curl -X PATCH https://tokidu.com/api/v1/tasks/$TASK_ID \
-H "Authorization: Bearer $TOKIDU_TOKEN" \
-H "Content-Type: application/json" \
-d '{"due":"2026-10-24"}'POST /api/v1/tasks/{id}/done
Marca la tarea como hecha y responde con las que se desbloquean.
Ámbito: write
curl -X POST https://tokidu.com/api/v1/tasks/$TASK_ID/done \
-H "Authorization: Bearer $TOKIDU_TOKEN"POST /api/v1/tasks/{id}/reopen
Vuelve a abrir una tarea hecha.
Ámbito: write
PUT /api/v1/tasks/{id}/after/{otherId}
Hace que la tarea espere a otra del mismo proyecto.
Ámbito: write
DELETE /api/v1/tasks/{id}/after/{otherId}
Quita esa espera.
Ámbito: write
DELETE /api/v1/tasks/{id}
Borra la tarea, como en la web.
Ámbito: write
GET /api/v1/webhooks
Los avisos a otras apps que ha creado este token: dirección, eventos, proyectos y estado (nunca el secreto).
Ámbito: webhooks
POST /api/v1/webhooks
Apunta una dirección https para recibir avisos firmados: url, events y, si quieres, projects (solo tuyos). Responde con el secreto, una sola vez.
Ámbito: webhooks
DELETE /api/v1/webhooks/{id}
Borra uno de los avisos que creó este token y deja de mandarlo.
Ámbito: webhooks
Avisos a otras apps (webhooks)
Cuando pasa algo en tus proyectos, tokidu manda un POST firmado a la dirección que digas. Se crean en Cuenta → Integraciones, o por la API con un token con el ámbito webhooks (lo que usan Zapier o Make). Solo el dueño con Max, y solo de sus proyectos; hasta 20 por cuenta.
Qué puedes escuchar
task.created· Se crea una tareatask.done· Se termina una tareatask.reopened· Se reabre una tareatask.unblocked· Se desbloquea una tareatask.assigned· Se asigna una tareatask.deleted· Se borra una tareaproject.status· Cambia el semáforo de un proyecto
Cada aviso lleva el tipo, un id del hecho, la hora, el proyecto y, si es de una tarea, su título, estado, responsable, fecha límite y enlace. Nunca notas, enlaces, archivos, correos ni horas.
Comprobar la firma
Sigue Standard Webhooks: cabeceras webhook-id, webhook-timestamp (segundos) y webhook-signature (v1, y un HMAC-SHA256 en base64 de id.timestamp.cuerpo, con la clave que va detrás de whsec_). Compruébala con el cuerpo tal cual llega y rechaza los de más de 5 minutos. En Node:
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyTokidu(secret, headers, body, toleranceSeconds = 300) {
const id = headers["webhook-id"], ts = headers["webhook-timestamp"], sigs = headers["webhook-signature"];
if (!id || !ts || !sigs) return false;
if (Math.abs(Date.now() / 1000 - Number(ts)) > toleranceSeconds) return false;
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const expected = createHmac("sha256", key).update(`${id}.${ts}.${body}`).digest();
return sigs.split(" ").some((s) => {
const [version, sig] = s.split(",");
const given = Buffer.from(sig ?? "", "base64");
return version === "v1" && given.length === expected.length && timingSafeEqual(given, expected);
});
}Envíos y reintentos
Cualquier 2xx es bueno; las redirecciones no se siguen. Si falla, se reintenta a 1 min, 5 min, 30 min, 2 h, 6 h, 12 h y 24 h. Con 20 entregas fallidas seguidas, o 3 días sin un 2xx, el aviso se apaga y te mandamos un correo.
Solo direcciones https:// públicas, en el puerto 443 u 8443, sin usuario ni contraseña: nada de localhost, redes internas ni IP privadas (se comprueba también al enviar).
OpenAPI
Todos los campos de cada petición y de cada respuesta, en formato OpenAPI, para generar un cliente o importarlos en tu herramienta: /api/v1/openapi.json.