Skip to content
ver .md sin procesar

Convenciones de Plan

Paginación, la gramática de filtros compartida, ordenamiento, include, errores, límites de solicitudes, idempotencia, concurrencia y lecturas condicionales en la API de Dailybot Plan (Beta).

Beta

Plan está en beta. Todo lo que está bajo /plan en la aplicación web, los comandos del CLI y de la agent skill para proyectos, objetivos, tableros y tareas, y la API pública /v1/plan/ puede cambiar antes de la disponibilidad general. ¿Quieres probarlo con tu equipo? Escribe a [email protected].

La API de Plan sigue las convenciones compartidas por todas las API de Dailybot y suma algunas propias: una gramática de filtros que comparten las listas, los tableros y el timeline; Idempotency-Key al crear; If-Match para ediciones concurrentes; y ETag / 304 en las lecturas más pesadas. Esta página explica cada una una sola vez.

Paginación

Las listas usan páginas. Envía page (empieza en 1) y page_size, o los alias limit y offset:

curl -sS "https://api.dailybot.com/v1/plan/tasks/?board=ENG&page=2&page_size=100" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"

Todas las listas responden con el mismo envoltorio:

{
  "count": 152,
  "next": "https://api.dailybot.com/v1/plan/tasks/?board=ENG&page=3&page_size=100",
  "previous": "https://api.dailybot.com/v1/plan/tasks/?board=ENG&page=1&page_size=100",
  "results": []
}
  • page_size vale 50 por defecto y llega hasta 100. Los valores mayores se ajustan al máximo, nunca se rechazan: si pides 500, recibes 100.
  • La instantánea del tablero devuelve como máximo 50 tareas por columna, porque incluye las etiquetas de cada tarjeta.
  • Sigue next hasta que sea null. results siempre es un arreglo.

El feed de cambios del tablero no es una página: devuelve un cursor, no next. Consulta la referencia del feed de cambios.

Filtros

La lista de tareas, la instantánea del tablero y el timeline comparten una misma gramática de filtros, así que una vista guardada funciona en los tres. Repetir un parámetro es OR; parámetros distintos son AND: ?board=ENG&board=OPS&state=open significa “tareas abiertas en ENG u OPS”.

Parámetro Acepta
board uuids o claves de tablero (ENG), repetible
project · goal · team uuids, repetible. goal coincide con el objetivo propio de la tarea o con el que hereda de su proyecto
state uuids de estado, o los atajos open (backlog, todo, in_progress) y done (done, canceled)
category backlog, todo, in_progress, done, canceled
priority De 1 urgente a 5 ninguna, repetible
owner Un uuid de usuario, me o unowned, repetible: owner=me&owner=unowned son tus tareas más las que no tienen responsable
participant · created_by uuids de usuario, repetible
label uuids de etiqueta, repetible (hasta 50)
due_before · due_after · start_before · start_after · completed_before · completed_after Fechas ISO, inclusivas
has_due_date · has_start_date · has_dates · blocked true o false
estimate_min · estimate_max Enteros
search Texto que se busca en el título y la clave (hasta 256 caracteres)
updated_since Marca de tiempo ISO
is_archived · include_archived true o false: solo filas archivadas, o archivadas y activas juntas

Dos formas de escribirlo que conviene recordar:

  • Vencido es due_before=<today>&state=open. No existe state=overdue: responde 400 invalid_filter_value.
  • Trabajo bloqueado sobre el que se puede actuar es blocked=true&state=open. Una tarea terminada puede seguir teniendo un bloqueo activo, así que blocked=true por sí solo también devuelve trabajo terminado.

En la lista de tareas, un parámetro desconocido se ignora, pero la instantánea del tablero y GET /v1/plan/activity/ solo aceptan los parámetros que documentan y rechazan cualquier otro con 400 invalid_filter_value. Un valor que la API no puede interpretar es 400 invalid_filter_value en todos los casos. owner=me con una API key de la organización es 400 actor_required (consulta Autenticación en Plan).

Ordenamiento e include

sort recibe un solo campo, con el prefijo - para orden descendente (sort=-updated_at). Todo ordenamiento agrega un desempate estable, así que una fila nunca aparece en dos páginas. Un valor no soportado es 400 invalid_sort, nunca un respaldo silencioso.

Algunas lecturas incluyen datos adicionales si los pides con include, una lista separada por comas. Cada endpoint documenta sus tokens, por ejemplo:

Endpoint Tokens de include
GET /v1/plan/tasks/{task_id}/ children, relations, participants, attachments, comment_count, activity, comments
GET /v1/plan/projects/ progress
GET /v1/plan/goals/ progress, projects
GET /v1/plan/pulse/ projects, attention, activity, goal_progress

Un token desconocido es 400 invalid_filter_value; un include= vacío se ignora.

Errores

Los errores traen un detail legible para personas y, para todo lo que podrías usar en una bifurcación, un code estable y legible por máquinas:

{
  "detail": "This task changed since you loaded it.",
  "code": "version_conflict",
  "extra": { "current_version": 9 }
}

Bifurca según code, nunca según detail. Los errores de validación en un cuerpo listan mensajes por campo. Un objeto inexistente y un objeto de otra organización devuelven el mismo cuerpo 404. Cada código, con lo que debes hacer a continuación, está en Errores de Plan.

Límites de solicitudes

Los límites se aplican por actor (una persona o una API key de la organización), por minuto:

Llamadas Límite
Lecturas 120 por minuto
Escrituras 60 por minuto
Llamadas masivas 30 por minuto
Feed de cambios del tablero 240 por minuto

Si superas un límite, la API responde 429 con un encabezado Retry-After: espera esa cantidad de segundos antes de reintentar. Para un tablero en vivo, consulta el feed de cambios con el poll_after_seconds que sugiere, en lugar de hacerlo con un temporizador fijo.

Idempotencia

Las creaciones y muchas escrituras aceptan un encabezado Idempotency-Key (lo indica la tabla Headers de cada endpoint): una cadena única que generas para una intención.

curl -sS -X POST "https://api.dailybot.com/v1/plan/tasks/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: import-row-42" \
  -d '{"board": "ENG", "title": "Write the migration guide"}'
  • Si vuelves a enviar la misma clave con el mismo cuerpo, recibes la primera respuesta, no se ejecuta nada nuevo y se agrega el encabezado Idempotency-Replayed: true. Reintenta con tranquilidad después de un timeout.
  • Enviar la misma clave con un cuerpo distinto es 409 idempotency_key_payload_mismatch: una clave nombra una sola intención.
  • Una repetición enviada mientras la primera llamada sigue en curso recibe 409 idempotency_in_progress durante hasta 120 segundos.
  • Las claves se recuerdan durante 24 horas.
  • Las operaciones masivas la exigen: POST /v1/plan/tasks/bulk/ sin clave es 400 idempotency_key_required.

Ediciones concurrentes

Cada tarea tiene un version entero. Para no sobrescribir el cambio de otra persona, envía la versión que cargaste en If-Match (entre comillas) o en el campo del cuerpo version:

curl -sS -X PATCH "https://api.dailybot.com/v1/plan/tasks/ENG-142/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "7"' \
  -d '{"due_date": "2026-10-22"}'
  • Si la tarea cambió, la respuesta es 409 version_conflict con extra.current_version: vuelve a leer, concilia y reintenta.
  • Enviar If-Match y version con valores distintos es 400 version_precondition_ambiguous.
  • Sin ninguno de los dos, gana la última escritura.
  • Hoy verifican versiones la actualización de tareas, el movimiento y el movimiento a otro tablero.

Las vistas guardadas funcionan distinto: PUT …/views/ reemplaza toda tu lista, así que exige If-Match con el ETag de tu última lectura. Un valor desactualizado es 412 precondition_failed; si falta, es 428 precondition_required.

Lecturas condicionales

La instantánea del tablero, el detalle de la tarea y el pulse de inicio devuelven un ETag. Envíalo de vuelta en If-None-Match; si nada cambió, la API responde 304 Not Modified con el cuerpo vacío, así evitas procesar una respuesta que ya tienes:

curl -sS -i "https://api.dailybot.com/v1/plan/boards/$BOARD/board/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN" \
  -H "If-None-Match: $ETAG"

Vistas previas con dry_run

Los endpoints de archivado, la finalización de hitos y las llamadas masivas aceptan ?dry_run=true. La API calcula la consecuencia y la devuelve sin escribir nada, incluido consequence, una frase pensada para mostrársela a una persona antes de que confirme:

curl -sS -X POST "https://api.dailybot.com/v1/plan/boards/$BOARD/archive/?dry_run=true" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN"

Atribución de agente

Cuando un agente trabaja con la credencial de una persona, la persona es la autora de cada escritura y el agente se muestra como quien la ejecutó en su nombre. Nombra al agente en cada escritura:

Escritura Cómo enviar el nombre
Cuerpo JSON El campo agent_name del cuerpo (canónico)
Multipart, o sin cuerpo (DELETE, archivar, restaurar) El header X-Dailybot-Agent-Name, con el valor codificado con percent-encoding (UTF-8)
curl -sS -X POST "https://api.dailybot.com/v1/plan/tasks/ENG-142/comments/" \
  -H "Authorization: Bearer $DAILYBOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"body": "Guía de migración redactada.", "agent_name": "Agente de releases"}'
  • Si vienen los dos, gana el cuerpo. Los caracteres de control se eliminan y un nombre vacío significa sin agente.
  • El máximo es de 128 caracteres. Un nombre más largo o imposible de decodificar es 400 invalid_agent_attribution; nunca se trunca.
  • Todo endpoint de /v1/plan/ que modifica datos (POST, PUT, PATCH, DELETE) lo acepta. Los GET lo ignoran.
  • Una key de tipo agente, que no está ligada a una persona, recibe 400 invalid_agent_attribution si envía un nombre.
  • El sello nunca cambia una respuesta de permisos.
  • El nombre se resuelve contra el mismo registro de agentes que /v1/agent-reports/ (nombre, alias, avatar).
  • Un nombre solo puede usar letras, números, espacios y . - _ ( ) ' # + / & , :. Cualquier otra cosa, y el nombre de un agente desactivado, es 400 invalid_agent_attribution.
  • Un comentario escrito con una API key o sellado con un agente tiene provenance: agent_authored.

En las respuestas, los comentarios, adjuntos y elementos de actividad llevan executed_by_agent ({uuid, name, username, avatar}, o null) junto al autor o actor. Una tarea gana executors, una lista de {uuid, name, username, avatar, first_at, last_at}, del más reciente al más antiguo. Es distinta del executor singular, que sigue siendo quien tiene la pelota ahora.

Desde el CLI de Dailybot (4.0.0 y posteriores): pasa --agent-name (o define DAILYBOT_AGENT_NAME; gana el flag). Sin ninguno, una persona actúa directamente y no se sella nada. Es una etiqueta de atribución, nunca una credencial, así que no cambia ningún permiso. El CLI envía agent_name en escrituras JSON y el header en cargas y escrituras sin cuerpo, nunca en lecturas, y rechaza localmente nombres de más de 128 caracteres.

dailybot --agent-name "Claude Code" plan task comment ENG-12 "Reproducido y corregido"

task comments muestra Jane Doe via "Claude Code", y task get agrega una línea Agents con los ejecutores, del más reciente al más antiguo. task brief [--download DIR] [--force] [--json] lee una tarjeta completa para un agente, adjuntos incluidos.

Cambios aditivos

Durante la Beta pueden aparecer en cualquier momento nuevos campos, parámetros, tipos de evento, tipos de relación y categorías de estado. Ignora los valores que no reconozcas y sigue el changelog de la API.