API público
Permite que otros sistemas (p. ej. uno de sugerencias o una mesa de ayuda) carguen actividades en la fábrica y sigan su avance: listar los proyectos, crear tareas y consultarlas, y sincronizar el catálogo de clientes al que se asignan las tareas. Las tareas creadas entran en la primera etapa activa del flujo (Backlog) y desde ahí las procesan los agentes como cualquier otra.
- Dirección base
/public/v1- Formato
- JSON (UTF-8), con nombres de campo en camelCase.
- Fechas
- Hora local de la fábrica, sin zona:
2026-10-15T12:51:03.
Autenticación
Cada sistema externo tiene su propia clave, que entrega el administrador de la fábrica. Va en el
encabezado X-Api-Key de cada petición. Sin clave, o con una que no es válida, la
respuesta es 401.
X-Api-Key: <clave del sistema>
La clave solo sirve para este API: no da acceso al resto de la fábrica. Guárdala como un secreto (nunca en el navegador ni en el código) y pide una nueva si se filtra: la anterior se revoca sin afectar a los demás sistemas.
Para el administrador: cómo dar de alta un sistema
En la configuración del API (variables de entorno del App Service), un par por sistema:
PublicApi__Clients__0__Name = Sugerencias
PublicApi__Clients__0__Key = <clave larga y aleatoria>
PublicApi__Clients__1__Name = Mesa de ayuda
PublicApi__Clients__1__Key = <otra clave>
PublicApi__TimeZone = America/Lima
Name es el nombre de origen que se usa si el sistema no manda
sourceName. PublicApi__TimeZone es la zona horaria de las PCs de la
fábrica (para la fecha de creación); vacía = la del servidor. Para revocar un sistema, se borra
su clave.
GET /public/v1/projects
Los proyectos (repositorios) de la fábrica, con los flujos de agentes que usa cada uno y sus
etiquetas. Hace falta el id para crear una tarea y, si el proyecto usa más de un flujo,
también el del flujo.
curl /public/v1/projects \
-H "X-Api-Key: $CLAVE"
Respuesta 200
[
{
"id": 3, "name": "Portal de clientes", "color": "#4F46E5",
"flows": [{ "id": 1, "name": "Flujo principal" }, { "id": 2, "name": "Correcciones" }],
"labels": [{ "id": 7, "name": "bug", "color": "#DC2626" }, { "id": 8, "name": "reportes", "color": "#2563EB" }]
},
{ "id": 5, "name": "Facturación", "color": "", "flows": [{ "id": 1, "name": "Flujo principal" }], "labels": [] }
]
POST /public/v1/tasks
Crea una tarea en el primer agente activo de su flujo, sin PC asignada: la toma la primera PC que la
ejecute. Si el proyecto usa un solo flujo, es ese; si usa varios, hay que indicar cuál en
flowId (sin él la respuesta es 400 con los flujos del proyecto).
| Campo | Tipo | Descripción | |
|---|---|---|---|
projectId | número | obligatorio | Id del proyecto (ver proyectos). |
flowId | número | según el proyecto | Id del flujo de agentes que recorre la tarea (ver proyectos). Obligatorio si el proyecto usa varios flujos. |
title | texto ≤ 300 | obligatorio | Título de la tarea. |
description | texto ≤ 100000 | opcional | El requerimiento completo: es lo que reciben los agentes. Cuanto más claro (contexto, criterios de aceptación), mejor resultado. |
baseBranch | texto ≤ 200 | opcional | Rama de la que parte el desarrollo y contra la que se abre el PR. Vacía = la última usada en el proyecto. |
sourceName | texto ≤ 100 | opcional | Nombre del software de origen. Vacío = el nombre con el que se configuró el sistema. |
sourceId | texto ≤ 100 | opcional | Id de la tarea en el software de origen (p. ej. Ticket 0677). Evita duplicados: ver abajo. |
sourceUrl | URL ≤ 1000 | opcional | Link http(s) a la tarea en el software de origen. La fábrica lo muestra en la tarjeta. |
priority | texto | opcional | Low, Medium, High o Urgent (también en español: Baja, Media, Alta, Urgente). Vacía = Medium. Es informativa: el orden de ejecución lo define el tablero. Otra que Medium cuenta como elegida a mano y el Triage ya no la cambia. |
labels | lista de textos ≤ 50 | opcional | Nombres de etiquetas del proyecto (sin importar mayúsculas). Las que el proyecto no tiene se crean con un color de la paleta. Hasta 20. |
complexity | texto | opcional | Low, Medium o High (también Baja, Media, Alta). Vacía = sin clasificar: la pueden sugerir el Triage o el Planificador. Elige el modelo de los agentes que tienen modelos por nivel; indicada cuenta como elegida a mano y los agentes ya no la cambian. |
customerId | texto (GUID) | opcional | Id de un cliente del catálogo (ver clientes): para quién es la tarea. Vacío = sin cliente; uno que no existe = 400. |
dependsOn | lista de números | opcional | Tareas del mismo proyecto de las que depende (hasta 20): no se ejecuta en un agente que ejecuta algo hasta que estén terminadas (ver relaciones). Una inexistente o de otro proyecto = 400. |
curl -X POST /public/v1/tasks \
-H "X-Api-Key: $CLAVE" \
-H "Content-Type: application/json" \
-d '{
"projectId": 3,
"flowId": 1,
"title": "Permitir exportar el reporte de ventas a Excel",
"description": "Los usuarios piden descargar el reporte...",
"baseBranch": "develop",
"sourceName": "Sugerencias",
"sourceId": "Ticket 0677",
"sourceUrl": "https://sugerencias.empresa.com/tickets/677",
"priority": "High",
"labels": ["reportes"],
"complexity": "Medium",
"customerId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301"
}'
Respuesta 201, con la ruta de la tarea en Location
Location: /public/v1/tasks/42
{
"id": 42,
"projectId": 3,
"projectName": "Portal de clientes",
"flowId": 1,
"stageId": 1,
"stageTitle": "Backlog",
"title": "Permitir exportar el reporte de ventas a Excel",
"description": "Los usuarios piden descargar el reporte...",
"baseBranch": "develop",
"status": "NotStarted",
"pullRequestUrl": "",
"createdAt": "2026-10-15T12:51:03",
"sourceName": "Sugerencias",
"sourceId": "Ticket 0677",
"sourceUrl": "https://sugerencias.empresa.com/tickets/677",
"archivedAt": null,
"pullRequestState": "Unknown",
"pullRequestChecks": "None",
"pullRequestHasConflicts": false,
"pullRequestMergedAt": null,
"priority": "High",
"labels": [{ "id": 8, "name": "reportes", "color": "#2563EB" }],
"complexity": "Medium",
"previewUrl": "",
"previewState": "None",
"customer": { "id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301", "name": "Banco del Sur" }
}
Sin duplicados
Un mismo sourceName + sourceId crea una sola tarea. Si se vuelve a enviar
(p. ej. un reintento tras un corte de red), la respuesta es 409 con el id de la que ya
existe en taskId y su ruta en Location: se puede tratar como éxito.
{
"status": 409,
"detail": "Ya existe la tarea 42 para Sugerencias Ticket 0677.",
"taskId": 42
}
GET /public/v1/tasks
Tareas de la fábrica, las más nuevas primero. Todos los filtros son opcionales y se combinan.
| Parámetro | Descripción |
|---|---|
projectId | Solo las de ese proyecto. |
stageId | Solo las que están en esa etapa (agente). |
status | Solo las que tienen ese estado. |
sourceName, sourceId | Solo las de ese origen: p. ej. para encontrar la tarea de un ticket. |
customerId | Solo las de ese cliente. |
skip | Cuántas saltar (paginación). Por defecto 0. |
take | Cuántas devolver, de 1 a 200. Por defecto 50. |
curl "/public/v1/tasks?sourceName=Sugerencias&take=20" \
-H "X-Api-Key: $CLAVE"
Respuesta 200: total es la cantidad que cumple los filtros
{
"total": 57,
"items": [ { "id": 42, "title": "...", ... }, ... ]
}
GET /public/v1/tasks/{id}
Una tarea. 404 si no existe (p. ej. si se eliminó en la fábrica).
curl /public/v1/tasks/42 \
-H "X-Api-Key: $CLAVE"
PATCH /public/v1/tasks/{id}
Cambia la prioridad, las etiquetas, la complejidad o el cliente de una tarea (p. ej. cuando cambian
en el ticket). Lo que no se envía no se cambia; labels reemplaza todas las etiquetas de
la tarea ([] = ninguna) y las que el proyecto no tiene se crean. Cambiar la prioridad o
la complejidad la deja como elegida a mano; "complexity": "" la deja sin clasificar (y
los agentes pueden volver a sugerirla). customerId es el id de un cliente del
catálogo; "customerId": "" deja la tarea sin cliente.
Responde 200 con la tarea, 400 si un campo no es válido o 404.
curl -X PATCH /public/v1/tasks/42 \
-H "X-Api-Key: $CLAVE" \
-H "Content-Type: application/json" \
-d '{ "priority": "Urgent", "labels": ["reportes", "cliente grande"], "complexity": "High",
"customerId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301" }'
PUT /public/v1/tasks/{id}/relations
Cambia la tarea madre y las dependencias de una tarea (las reemplaza). Una tarea con dependencias no
se ejecuta mientras alguna no esté terminada: queda bloqueada (isBlocked) y se ejecuta
sola cuando se desbloquea. Terminada = archivada, con su PR mezclado o, si no tiene PR, al final de
su flujo. La madre y las dependencias tienen que ser del mismo proyecto y no pueden formar un ciclo.
stackOnDependency (opcional, con una sola dependencia) hace que parta de la rama de esa
tarea y que su PR vaya contra esa rama.
Responde 200 con la tarea, 404 o 409 con el motivo si no se
puede.
curl -X PUT /public/v1/tasks/42/relations \
-H "X-Api-Key: $CLAVE" \
-H "Content-Type: application/json" \
-d '{ "parentTaskId": 40, "dependsOn": [41], "stackOnDependency": false }'
POST /public/v1/tasks/{id}/approvals
Algunas etapas son puntos de aprobación (p. ej. la Aprobación del plan): la tarea espera ahí hasta que una persona decide. El sistema externo también puede decidir: aprobarla (pasa a la etapa siguiente) o pedir cambios (vuelve a la etapa anterior con el comentario, que el agente recibe como instrucción).
| Campo | Descripción |
|---|---|
decision | Obligatorio: Approved o ChangesRequested. |
comment | Opcional al aprobar; obligatorio al pedir cambios (hasta 10.000 caracteres). |
author | Opcional: quién decidió en el sistema externo (se guarda junto al nombre del sistema). |
curl -X POST /public/v1/tasks/42/approvals -H "X-Api-Key: $CLAVE" -H "Content-Type: application/json" -d '{ "decision": "ChangesRequested", "comment": "Falta el caso sin permisos.", "author": "Ana" }'
Respuesta 201: la decisión
{
"id": 7, "taskId": 42, "stageId": 4, "stageTitle": "Aprobación del plan",
"decision": "ChangesRequested", "author": "Ana (Sugerencias)", "comment": "Falta el caso sin permisos.",
"requestedAt": "2026-10-23T10:15:00", "decidedAt": "2026-10-23T11:02:41", "waitingSeconds": 2861,
"toStageId": 3, "toStageTitle": "Planificador", "feedbackId": 15
}
409 si la tarea no está esperando aprobación (ya se decidió o se movió) o si la etapa no
tiene adónde pasarla. GET /public/v1/tasks/{id}/approvals devuelve todas las decisiones
de la tarea, de la más antigua a la más reciente.
Clientes
Catálogo de los clientes a los que se asignan las tareas (customerId al
crearlas o cambiarlas). Cada cliente tiene solo un
id (GUID) y un name (hasta 200 caracteres, sin repetir en la fábrica sin
importar mayúsculas). Para sincronizarlo con el sistema externo conviene crear cada cliente con el
mismo id que tiene allá.
| Método y ruta | Qué hace |
|---|---|
GET /customers | Los clientes por nombre: { total, items }. search (opcional) filtra por una parte del nombre, sin importar mayúsculas; paginación con skip y take (1 a 200, 50 por defecto). |
GET /customers/{id} | Un cliente. 404 si no existe. |
POST /customers | Crea un cliente: { "name": "…", "id": "…" } (id opcional; sin él la fábrica genera uno). Responde 201 con su ruta en Location; 409 si el id ya existe (con su ruta en Location) o el nombre ya lo tiene otro. |
PUT /customers/{id} | Crea o renombra el cliente con ese id: { "name": "…" }. Responde 201 si lo creó y 200 si ya existía; se puede enviar cada cliente sin saber si ya está en la fábrica. |
DELETE /customers/{id} | Elimina el cliente: sus tareas quedan sin cliente. Responde 204, o 404 si no existe. |
curl -X PUT /public/v1/customers/3f2504e0-4f89-11d3-9a0c-0305e82c3301 \
-H "X-Api-Key: $CLAVE" \
-H "Content-Type: application/json" \
-d '{ "name": "Banco del Sur" }'
Respuesta 201 (o 200 si ya existía)
{ "id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301", "name": "Banco del Sur" }
curl "/public/v1/customers?search=banco&take=20" \
-H "X-Api-Key: $CLAVE"
Respuesta 200: total es la cantidad que cumple la búsqueda
{
"total": 2,
"items": [
{ "id": "3f2504e0-4f89-11d3-9a0c-0305e82c3301", "name": "Banco del Sur" },
{ "id": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", "name": "Banco Norte" }
]
}
La tarea
| Campo | Descripción |
|---|---|
id | Id de la tarea en la fábrica. |
projectId, projectName | Proyecto. |
flowId | Flujo de agentes que recorre. |
stageId, stageTitle | Etapa (agente) en la que está ahora: Backlog, Planificador, Desarrollador, revisores… |
title, description | Título y requerimiento. |
baseBranch | Rama base. |
status | Estado de la ejecución en su etapa actual (ver abajo). |
pullRequestUrl | Link al pull request, cuando ya se creó (vacío si no). |
createdAt | Fecha de creación (hora local de la fábrica). |
sourceName, sourceId, sourceUrl | Origen. Vacíos en las tareas creadas en la fábrica. |
archivedAt | Cuándo se archivó (hora local de la fábrica): ya no está en el tablero, pero sigue contando en las métricas. null si no está archivada. |
priority | Low, Medium, High o Urgent (informativa). |
labels | Etiquetas de la tarea: id, name y color. |
complexity | Low, Medium, High o null (sin clasificar): elige el modelo de los agentes que tienen modelos por nivel. |
customer | Cliente para el que es la tarea: id y name (ver clientes); null si no tiene. |
pullRequestState | Estado del pull request en su plataforma: Unknown (sin PR o todavía no consultado), Open, Merged o Declined (rechazado o cerrado sin mezclar). Lo actualiza cada pocos minutos la PC de la tarea. |
pullRequestChecks | Resultado del CI del PR: None, Pending, Passed o Failed. |
pullRequestHasConflicts | Si el PR tiene conflictos con la rama base. |
pullRequestMergedAt | Cuándo se mezcló el PR (hora local de la fábrica); null si no está mezclado. |
parentTaskId | Tarea madre; null si no es subtarea. |
isBlocked | Llegó a un agente que ejecuta algo pero alguna dependencia no está terminada: no se ejecuta hasta que se desbloquee. |
dependencies | Tareas de las que depende: id, title e isDone (ya no la bloquea). |
dependents | Tareas que dependen de esta, con isDone. |
subtasks | Subtareas, con isDone: el avance de la madre. |
previewUrl | Link al entorno de prueba publicado de la tarea, para que QA o el cliente la prueben sin instalar nada (vacío si no tiene uno publicado). Lo despliega el pipeline del proyecto. |
previewState | Estado del entorno de prueba: None (nunca tuvo), Deploying, Deployed, DeployFailed, Removing, Removed (se eliminó al terminar la tarea) o RemoveFailed. |
Estados
NotStarted | Esperando a que el agente de su etapa la ejecute. Vuelve a este estado cada vez que la tarea cambia de etapa. |
InProgress | El agente la está ejecutando. |
Completed | El agente terminó bien. |
Rejected | El agente la rechazó (p. ej. un revisor): según el flujo, vuelve a una etapa anterior. |
Error | Falló la ejecución: alguien de la fábrica debe revisarla. |
Errores
Los errores vienen en formato Problem Details.
400 | Datos inválidos. errors trae el detalle por campo. |
401 | Falta la clave o no es válida. |
404 | La tarea o el cliente no existe. |
409 | La tarea ya existe para ese origen (ver sin duplicados), no se puede decidir sobre ella (ver aprobaciones) o ya hay un cliente con ese id o ese nombre (ver clientes). detail trae el motivo. |
500 | Error del servidor: se puede reintentar más tarde. |
{
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"title": [ "El título es obligatorio." ],
"projectId": [ "El proyecto no existe." ]
}
}
Integración recomendada
- Pedir los proyectos y guardar a qué proyecto va cada tipo de actividad.
- Si las tareas son de clientes, sincronizar el catálogo con
PUT /customers/{id}(con el id del sistema externo) cuando se crea o cambia un cliente, yDELETEcuando se elimina; luego crear las tareas con sucustomerId. - Crear la tarea siempre con
sourceIdysourceUrl: así se puede reintentar sin duplicar y desde la fábrica se llega al ticket. - Guardar el
idque devuelve la fábrica (o el que viene en el409). - Consultar el avance de vez en cuando (no más de una vez por minuto) con
GET /tasks/{id}, o todas las propias conGET /tasks?sourceName=…, y mostrarstageTitle,status, elpullRequestUrly elpullRequestStateen el sistema de origen (Merged= el cambio ya está en la rama base).